############################################################################
#
#	File:     when.icn
#
#	Subject:  Program to show file age
#
#	Author:   Chris Tenaglia
#
#	Date:     August 14, 1996
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#  This one was developed for UNIX (namely ULTRIX 4.3 rev 44). Maybe
#  it will work on some other UNIX too. I'd like to know. This program
#  is called 'when'. It's like a date based ls command. Some have told
#  me 'find' can do the same things, but I find find a bit arcane?
#  
#  So 'when' is what I use. Here are some samples:
#  
#       when before 4/12/92      # files before a date
#       when before 300          # files older than an age
#       when after 3/25          # or younger than a date this year
#       when before 2/1/94 and after 10/31/93     # even a range
#  
#  More options and clauses are supported. Look at the code for clues.
#  This one only works in the current directory. It also has an interesting
#  property. Maybe this is just ULTRIX, maybe not, I'd like to know anyway...
#  The interpreted version works fine, but the compiled version has a
#  numeric overflow. That'll make for some fun debugging. I wrote it for
#  myself as a tool to locate old files for archiving or deleting. Study and
#  enjoy!
#
############################################################################
#
#  Requires:  UNIX
#
############################################################################

global base,      # 1970 calculation baseline number
       today,     # displacement from 12:00:01am today
       now,       # upto the second mark for right now
       method,    # ascending or descending order
       output,    # long (ls -al) or brief (ls -1) style
       command,   # optional command to do on each file
       files      # list with files, sizes, and ages

procedure main(param)
  local i, option, j
  calc_today()
  files   := directory()
  method  := "none"
  output  := "long"
  command := ""
  if *param = 0 then show_age()
  every i := 1 to *param do
    {
    (option := param[i]) | break
    case option of
      {
      "to"     |
      "before" |
      "until"   : {
                  files := before(files,param[i+1])
                  i    +:= 1
                  }
      "from"  |
      "since" |
      "after"   : {
                  files := since(files,param[i+1])
                  i    +:= 1
                  }
      "asc"     : method:="ascending"
      "des"     : method:="descending"
      "long"    : output:="long"
      "brief"   : output:="brief"
      "do"      : {
                  every j := i+1 to *param do
                    command ||:= param[j] || " "
                  }
      default   : 5    # stop("Unrecognized option :",option)
      }
    }
  show_age()
  end

#
# just show another ls with days old numbers & optionally sorts
#
procedure show_age()
  local line, age, ks, file, text, results, i
  case method of
    {
    "none" : {  
             every line := !files do
               {
               age := (today - parse(line,' ')[1]) / 86400
               ks  := parse(line,' ')[2] / 1024
               file:= line[23:0]
               (command == "") |
                 {
                 write(command,line[37:0])
                 system(command || line[37:0])
                 next
                 }
               if output == "brief" then text := line[37:0]
                                    else text:= right(age,6) || " days " || right(ks,6) || " kb | " || file
               write(text)
               }
             }
    "descending" : {
                   results := sort(files)
                   every line := !results do
                     {
                     age := (today - parse(line,' ')[1]) / 86400
                     ks  := parse(line,' ')[2] / 1024
                     file:= line[23:0]
                     (command == "") |
                       {
                       write(command,line[37:0])
                       system(command || line[37:0])
                       next
                       }
                     if output == "brief" then text := line[37:0]
                                          else text:= right(age,6) || " days " || right(ks,6) || " kb | " || file
                     write(text)
                     }
                   }
    "ascending" : {
                   results := sort(files)
                   every i := *results to 1 by -1 do
                     {
                     line:= results[i]
                     age := (today - parse(line,' ')[1]) / 86400
                     ks  := parse(line,' ')[2] / 1024
                     file:= line[23:0]
                     (command == "") |
                       {
                       write(command,line[37:0])
                       system(command || line[37:0])
                       next
                       }
                     if output == "brief" then text := line[37:0]
                                          else text:= right(age,6) || " days " || right(ks,6) || " kb | " || file
                     write(text)
                     }
                   }
    default : 5
    }
  end

#
# remove elements later than a date
#
procedure before(lst,days)
  local i, mo, da, yr, tmp, dd, age, work, file, old
  static  mtab
  initial mtab := [0,31,59,90,120,151,181,212,243,273,304,334]
  if find("/",days) then
    {
    mo := parse(days,'/')[1]
    da := parse(days,'/')[2]
    yr := parse(days,'/')[3] | parse(&date,'/')[1]
    if yr < 100 then yr +:= 1900
    tmp := yr * 31557600
    dd  := mtab[mo] + da
    if ((yr % 4) = 0) & (mo > 2) then dd +:= 1
    tmp+:= dd * 86400
    age := tmp
    } else {
    age := now - (days * 86400)
    }
  work := []
  every file := !lst do
    {
    old := parse(file,' ')[1]
    if old <= age then put(work,file)
    }
  return copy(work)
  end

#
# remove elements earlier than a date
#
procedure since(lst,days)
  local mo, da, yr, tmp, dd, age, work, file, old
  static  mtab
  initial mtab := [0,31,59,90,120,151,181,212,243,273,304,334]
  if find("/",days) then
    {
    mo := parse(days,'/')[1]
    da := parse(days,'/')[2]
    yr := parse(days,'/')[3] | parse(&date,'/')[1]
    if yr < 100 then yr +:= 1900
    tmp := yr * 31557600
    dd  := mtab[mo] + da
    if ((yr % 4) = 0) & (mo > 2) then dd +:= 1
    tmp+:= dd * 86400
    age := tmp
    } else {
    age := now - (days * 86400)
    }
  work := []
  every file := !lst do
    {
    old := parse(file,' ')[1]
    if old >= age then put(work,file)
    }
  return copy(work)
  end

#
# calculate today and now figures
#
procedure calc_today()
  local tmpy, tmpm, tmpd, here
  static  mtab
  initial {
          base := 1970*31557600
          mtab := [0,31,59,90,120,151,181,212,243,273,304,334]
          }
  tmpy := parse(&date,'/')[1]
  tmpm := parse(&date,'/')[2]
  tmpd := parse(&date,'/')[3]
  here := tmpy * 31557600 +
          (mtab[tmpm] + tmpd) * 86400
  if ((tmpy%4) = 0) & (tmpm > 2) then here +:= 86400
  today := here
  now   := here +
           parse(&clock,':')[1] * 3600 +
           parse(&clock,':')[2] *   60 +
           parse(&clock,':')[3]
  end

#
# convert a ls -al output into a list for sorting and printing
#
procedure directory()
  local pipe, entries, line, size, file, day, year, sec, mark, text
  static mtab
  initial {
          mtab := table(0)
          mtab["Jan"] := 0
          mtab["Feb"] := 31
          mtab["Mar"] := 59
          mtab["Apr"] := 90
          mtab["May"] := 120
          mtab["Jun"] := 151
          mtab["Jul"] := 181
          mtab["Aug"] := 212
          mtab["Sep"] := 243
          mtab["Oct"] := 273
          mtab["Nov"] := 304
          mtab["Dec"] := 334
          }
  pipe    := open("ls -al","pr")
  entries := []
  every line := !pipe do
    {
    if any('dclst',line) then next   # ignore info and dirs
    size := parse(line,' ')[4]
    file := line[33:0]
    day  := mtab[parse(line,' ')[5]] + parse(line,' ')[6]
    year := if line[40] == " " then parse(line,' ')[7] else parse(&date,'/')[1]
    sec  := if line[40] == " " then 0 else hhmm(parse(line,' ')[7])
    mark := year * 31557600 + day * 86400 + sec
    if (now-mark) < 0 then mark -:= 31557600
    text := right(mark,12) || right(size,10) || " " || file
    put(entries,text)
    }
  close(pipe)
  return entries
  end

#
# convert hh:mm into seconds since midnight
#
procedure hhmm(str)
  local hh, mm
  hh := str[1+:2]
  mm := str[4+:2]
  return hh*3600 + mm*60
  end

#
# parse a string into a list with respect to a delimiter
#
procedure parse(line,delims)
  local tokens
  static chars
  chars  := &cset -- delims
  tokens := []
  line ? while tab(upto(chars)) do put(tokens,tab(many(chars)))
  return tokens
  end


