# daymvdel - A program that moves or deletes files in directories
# or directory trees according to file age. Rick Smereka,
# Copyright (C) 2006.

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, get a copy via the Internet at
# http://gnu.org/copyleft/gpl.html or write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA

# You can contact the author via email at rsmereka@future-lab.com

# Program syntax:

#    daymvdel [-q] [-l[ log_file]] [-n] [-c] task_list_file

# Where 'task_list_file' is a ASCII file containing a list of directories and 
# what to do in each directory. The task list is in the following format for
# a delete operation:

#    d[s][a] numdays fname source_path

# The task list is in the following format for the move operation:

#    m[s][a] numdays fname source_path dest_path
 
# The first option is an 'm' for move or 'd' for delete. One of these
# two must be present. The option 's' includes subdirectories and the
# option 'a' stand for 'all or nothing' which means move or delete all
# files as long as every file meets the age requirement. If any file
# does not meet this requirement, delete or move none of the files.

# The parameter 'numdays' is the file age in days, 'fname' indicates
# which files to consider (the wildcards '*' and '?' may be used),
# 'source_path' is source directory path and 'dest_path' (required
# only for a move operation) is the destination directory path.

# Any task line parameter that contains spaces must be quoted.

# In addition to the normal move or delete operation, you can apply
# the 't'ree command that will perform the move or delete operation to each
# subdirectory underneath the specified tree. The task list line syntax
# along with the 't'ree command is:

# Delete operation:
#    td[s][a] numdays fname source_path[ except...]

# Move operation:

#    tm[s][a] numdays fname source_path dest_path[ except...]

# The 't' signifies a 'tree' move or delete. The 't' should be the first
# character on the line. Most of the task line is the same as the normal
# move or delete with the addition of the optional exception directories.
# Any directory listed as an exception (they may be more than one listed),
# will be excluded from the move or delete operation when it is encountered.
# Exception directories (like all paths in this program) must be specified
# in absolute form.

# The command line option 'q' will suppress all console message output 
# (quiet), the 'l' (el) option will output all messages to either the default
# log destination/file or the supplied destination/file, the 'n'
# option will report only without taking any action and the 'c' option
# will automatically create the destination base directory tree for the
# move operation. Note that the option/switch character is based on the 
# platform and may be not the hyphen.

# This program uses the 'dscan' directory scanning API and the
# the 'path' parsing API.

# Initial tcl version. Aug/2006, Rick Smereka
 
# required path to library modules
set TCLDIR /home/rick/gpl/tcl

source $TCLDIR/lib/stdhead.tcl
source $TCLDIR/lib/dscan.tcl
source $TCLDIR/lib/path.tcl

const APNAME "daymvdel"
const VERSION "1.10-2006.08.06"
const DEFAULT_LOG_FILE "daymvdel.log"

# operation codes
const OPCODE_DELETE 0
const OPCODE_MOVE 1

# global data

set c_log_file ""
set c_task_file ""
set today ""
set line ""
set console_output 1
set log_output 0
set take_action 1
set move_do_base_dir 0
set line_num 0
set line_parms 0
set in 0
set file_days_did_init 0

# the data for a single task is stored in the array 'task', elements are:
#
#    opcode            operation code
#    do_subs           should subdirectories be included?
#    all               all or nothing?
#    tree              'tree' task?
#    numdays           age in days
#    fpat              file name pattern to match
#    path              path to operate on
#    dest_path         destination path (for moving)
#    dirs              'list' of subdirectories

proc process {} {
   # Parse the task list and perform each task.
   global c_task_file in move_do_base_dir log_output c_log_file console_output
   global take_action line line_num APNAME task
   set mname "process"

   if {[catch {open $c_task_file r} in]} {
      app_out "$APNAME:$mname:unable to open task file '$c_task_file',\
               program abort"
      return
   }

   app_out "task list file is '$c_task_file'"
   flag_2_logic_string $move_do_base_dir mes
   app_out "create base directory structure during move op is $mes"
   flag_2_logic_string $log_output mes
   app_out "logging is $mes, log destination is '$c_log_file'"
   flag_2_logic_string $console_output mes
   app_out "console output is $mes"

   if {!$take_action} {
      app_out "in analysis mode only, no action taken (delete or move)"
   }

   set_date
   set line [get_rec $in]
   incr line_num

   while {![eof $in]} {
      if {[parse_task]} {
         if {$task(tree)} {
            do_tree
         } else {
            do_task
         }
      }

      set line [get_rec $in]
      incr line_num
   }

   app_out "$APNAME:$mname:program complete"
   term_app
}

proc parse_comline {c_count c_parm} {
   # Parse the command line for parameters. Returns 1 if no error
   # was detected, 0 otherwise.
   global DEFAULT_LOG_FILE SWITCH_CHAR APNAME c_log_file c_task_file
   global log_output console_output take_action move_do_base_dir
   set mname "parse_comline"

   # set default log file
   set c_log_file $DEFAULT_LOG_FILE
   set c_task_file ""
   set done 0
   set parms 0

   while {!$done} {
      set prm [lindex $c_parm $parms]

      if {[string index $prm 0] == $SWITCH_CHAR} {
         switch -- [ucase [string index $prm 1]] {
            L {
               if {[string length $prm] > 2} {
                  puts "$APNAME:$mname:extraneous input with log switch,\
                        ignoring"
               }

               incr parms

               if {$c_count > $parms} {
                  set prm [lindex $c_parm $parms]

                  if {[string index $prm 0] != $SWITCH_CHAR} {
                     set c_log_file $prm
                     incr parms
                  }
               }

               set log_output 1
            }

            Q {
               set console_output 0
               incr parms
            }

            N {
               set take_action 0
               incr parms
            }

            C {
               set move_do_base_dir 1
               incr parms
            } 

            default {
               puts "$APNAME:$mname:unknown switch($prm)"
               return 0
            }
         }
      } else {
         set c_task_file $prm
         incr parms
      }

      if {$parms >= $c_count} {
         set done 1
      }
   }

   if {$c_task_file == ""} {
      puts "$APNAME:$mname:task list file name is missing"
      return 0
   }

   if {$log_output} {
      if {[logman_start $c_log_file $APNAME]} {
         puts "$APNAME:$mname:error starting logging"
         return 0
      }
   }

   return 1
}

proc parse_task {} {
   # Parse task and load into the 'task' array. Returns 1 upon success,
   # 0 upon error or a comment line.
   global line task OPCODE_DELETE OPCODE_MOVE move_do_base_dir line_num
   global line_parms
   set mname "parse_task"
   set tline [trim $line]

   # ignore blank lines
   if {$tline == ""} {
      return 0
   }

   # ignore comment line ('#' as first non-blank character)
   if {[string index $tline 0] == "#"} {
      return 0
   }

   # initialize task array
   set task(do_subs) 0
   set task(all) 0
   set task(tree) 0
   set task(path) ""
   set task(fpat) ""
   set task(dest_path) ""
   set task(dirs) {}
   set task(numdays) 0
   set line_parms [command_words $tline]

   # there must be at least four command words on the line
   if {$line_parms < 4} {
      app_out "$mname:line($line_num):**error:insufficient number of\
               parameters"
      return 0
   }

   # first command word consists of the operation code and options
   if {![command_word $tline wrd 1]} {
      app_out "$mname:line($line_num):internal error word(1)"
      return 0
   }

   # each character is an operation code or an option
   set len [string length $wrd]

   for {set i 0} {$i < $len} {incr i} {
      set ch [ucase [string index $wrd $i]]

      switch $ch {
         D {
            set task(opcode) $OPCODE_DELETE
         }

         M {
            set task(opcode) $OPCODE_MOVE
         }

         T {
            set task(tree) 1
         }

         S {
            set task(do_subs) 1
         }

         A {
            set task(all) 1
         }

         default {
            app_out "$mname:line($line_num):**error:unknown operation\
                     code/option($ch)"
            return 0
         }
      }
   }

   # if move operation, must be five words on the line
   if {$task(opcode) == $OPCODE_MOVE} {
      if {$line_parms < 5} {
         app_out "$mname:line($line_num):**error:insufficient number of\
                  parameters"
         return 0
      }
   }

   # word two s/b the age in days
   if {![command_word $tline wrd 2]} {
      app_out "$mname:line($line_num):internal error word(2)"
      return 0
   }

   if {![num $wrd]} {
      app_out "$mname:line($line_num):**error:number of days is non-numeric"
      return 0
   }

   if {$wrd <= 0} {
      app_out "$mname:line($line_num):**error:number of days is out of\
               range(s/b >0)"
      return 0
   }

   set task(numdays) $wrd

   # word three is the file name pattern to match
   if {![command_word $tline task(fpat) 3]} {
      app_out "$mname:line($line_num):internal error word(3)"
      return 0
   }

   # 'all or nothing' option must be used with all files ('*' or '*.*')
   if {$task(all)} {
      if {$task(fpat) != "*" && $task(fpat) != "*.*"} {
         app_out "$mname:line($line_num):must specify all files with\
                  'all or nothing'"
         return 0
      }
   }

   # word four is the path to operate on
   if {![command_word $tline wrd 4]} {
      app_out "$mname:line($line_num):internal error word(4)"
      return 0
   }

   if {![isdirectory $wrd]} {
      app_out "$mname:line($line_num):**error:'$wrd' is not a directory"
      return 0
   }

   set task(path) $wrd

   # word five is the move destination path
   if {$task(opcode) == $OPCODE_MOVE} {
      if {![command_word $tline wrd 5]} {
         app_out "$mname:line($line_num):internal error word(5)"
         return 0
      }
   
      if {$move_do_base_dir} {
         if {![isdirectory $wrd]} {
            app_out "$mname:line($line_num):**error:'$wrd' is not a directory"
            return 0
         }
      }

      set task(dest_path) $wrd
   }

   parray task
   return 1
}

proc do_tree {} {
   # Process a 'tree' task. Locate all directories in the specified
   # source directory structure and load these into the 'dirs' list
   # excluding the exception directories. For each directory in the
   # 'dirs' list, process a task.
   global line task line_parms line_num tcl_platform OPCODE_DELETE OPCODE_MOVE
   global PATH_SEP
   set mname "do_tree"

   app_out "$mname:[get_ftime]:starting tree task $line"

   if {$task(opcode) == $OPCODE_DELETE} {
      set ex_start 5
   } else {
      set ex_start 6
   }

   set odest_path $task(dest_path)

   # locate all sub-directories
   set ret [dscan_findfirst $task(path) "*" "d" pfname]

   # add all sub-directories to the 'dirs' list
   while {$ret} {
      # app_out "$mname:found dir '$pfname'"

      # exclude the directory if in the exclude list
      if {$line_parms >= $ex_start} {
         for {set i $ex_start} {$i <= $line_parms} {incr i} {
            if {![command_word $line wrd $i]} {
               app_out "$mname($line_num):exit:internal error getting\
                        exception"
               return
            }

            set should_except 0

            # compare case insensitive for Windows
            if {$tcl_platform(platform) == "windows"} {
               if {[string toupper $wrd] == [string toupper $pfname]} {
                  set should_except 1
               }
            } else {
               if {$wrd == $pfname} {
                  set should_except 1
               }
            }

            if {$should_except} {
               app_out "$mname:dir '$wrd' in exception list,ignoring"
               set ret [dscan_findnext pfname]
               break
            }
         }
      }

      if {!$ret} {
         break
      }

      # add each directory found to the 'dirs' list
      lappend task(dirs) $pfname
      set ret [dscan_findnext pfname]
   }

   # app_out "$mname:debug dirs"
   set nentries [llength $task(dirs)]

   #for {set i 0} {$i < $nentries} {incr i} {
   #   app_out "$mname:dir($i) '[lindex $task(dirs) $i]'"
   #}

   # load each directory from the list into the source path ($task(path)),
   # and process it
   for {set i 0} {$i < $nentries} {incr i} {
      set rdir [lindex $task(dirs) $i]
      set task(path) $rdir

      # if task is move, alter the destination path by adding the last
      # directory name onto it
      if {$task(opcode) == $OPCODE_MOVE} {
         set ret [ll_words $rdir $PATH_SEP]
   
         if {[ll_word $rdir wrd $ret $PATH_SEP]} {
            set pfname [format "%s%s%s" $odest_path $PATH_SEP $wrd]
            # app_out "$mname:old dest=$task(dest_path),new dest=$pfname"
            set task(dest_path) $pfname
         }
      }

      do_task
   }

   # wipe 'dirs' list
   set task(dirs) {}
}

proc do_task {} {
   # Perform the task/operation.
   global task OPCODE_DELETE OPCODE_MOVE take_action move_do_base_dir
   global tcl_platform
   set mname "do_task"
   set done 0
   set mindays 32760
   set fcount 0
   set pfname [get_ftime]
   
   if {$task(opcode) == $OPCODE_DELETE} {
      app_out "$mname:$pfname:starting delete task '$task(path)'"
   } else {
      app_out "$mname:$pfname:starting move task '$task(path)',\
               '$task(dest_path)'"
   }

   while {!$done} {
      if {![file_days pfname days]} {
         set done 1
         continue
      }

      incr fcount

      # if 'all or none', detect minimum number of days
      if {$task(all)} {
         if {$days < $mindays} {
            set mindays $days
             # app_out "$mname:new mindays=$mindays"
         }
      } else {
         # perform operation if greater than number of days specified
         if {$days > $task(numdays)} {
            if {$task(opcode) == $OPCODE_DELETE} {
               if {$take_action} {
                  file delete $pfname
                  app_out "$mname:'$pfname' deleted"
               } else {
                  app_out "$mname:'$pfname' should be deleted"
               }
            }

            if {$task(opcode) == $OPCODE_MOVE} {
               set dest_name $pfname

               if {[adjust_path dest_name]} {
                  if {$take_action} {
                     # create destination directory if requested
                     if {$move_do_base_dir} {
                        if {![create_base_dir $dest_name 1]} {
                           app_out "$mname:exit:bad rc(0) from\
                                    create_base_dir,file skipped"
                           continue
                        }
                     }

                     if {![filecopy $pfname $dest_name]} {
                        app_out "$mname:bad rc(0) from filecopy($pfname)"
                     } else {
                        file delete $pfname
                        app_out "$mname:file moved to '$dest_name'"
                     }
                  }
               }
            }
         }
      }
   }

   # if tree is empty, do nothing
   if {!$fcount} {
      app_out "$mname:dir tree is empty,ignoring"
      return
   }

   # perform operation if 'all or none' and greater than number of days
   if {$task(all)} {
      if {$mindays > $task(numdays)} {
         if {$task(opcode) == $OPCODE_DELETE} {
            # delete 'all or none'
            if {$take_action} {
               if {![delete_all_files]} {
                  app_out "$mname:bad rc(0) from delete_all_files"
               }
            } else {
               app_out "$mname:newest file is $mindays day(s) old,should\
                        perform operation"
            }
         } else {
            # move 'all or none'
            if {$take_action} {
               if {![move_all_files]} {
                  app_out "$mname:bad rc(0) from move_all_files"
               }
            } else {
               app_out "$mname:newest file is $mindays day(s) old,should\
                        perform operation"
            }
         }
      } else {
         app_out "$mname:newest file is $mindays day(s) old,should not\
                  perform operation"
      }                
   }
   
   set pfname [get_ftime]
   app_out "$mname:$pfname:completed task"
}

proc delete_all_files {} {
   # Delete all files from the source directory task(path)
   # If subdirectories are also specified, the entire 
   # directory tree will be deleted. Returns 1 upon
   # success, 0 otherwise.
   global tcl_platform task
   set mname "delete_all_files"

   # prepare flags for 'dscan'
   set flags f

   if {$tcl_platform(platform) == "unix"} {
      # add symbolic link detection if running under Unix/Linux/BSD
      append flags l
   }

   if {$task(do_subs)} {
      append flags s
   }

   # initialize 'dscan'
   set ret [dscan_findfirst $task(path) $task(fpat) $flags pfname]

   if {!$ret} {
      app_out "$mname:no files to delete"
      return 0
   }

   while {$ret} {
      if {[catch {file delete $pfname}]} {
         app_out "$mname:error deleting '$pfname',halted"
         return 0
      }

      set ret [dscan_findnext pfname]
   }


   if {$task(do_subs)} {
      if {[catch {file delete -force $task(path)}]} {
         app_out "$mname:bad rc(0) from file delete deleting\
                  directory tree '$task(path)'"
         return 0
      }
   }

   return 1
}

proc move_all_files {} {
   # Move all files from the source directory task(path) to the
   # destination directory task(dest_path). If subdirectories are also
   # specified, the entire source directory tree will be
   # deleted after all the files have been moved. Returns 1 upon
   # success, 0 otherwise.
   global tcl_platform task move_do_base_dir
   set mname "move_all_files"

   # prepare flags for 'dscan'
   set flags f

   if {$tcl_platform(platform) == "unix"} {
      # add symbolic link detection if running under Unix/Linux/BSD
      append flags l
   }

   if {$task(do_subs)} {
      append flags s
   }

   # initialize 'dscan'
   set ret [dscan_findfirst $task(path) $task(fpat) $flags src_name]

   if {!$ret} {
      app_out "$mname:no files to move"
      return 0
   }

   while {$ret} {
      set dest_name $src_name

      if {![adjust_path dest_name]} {
         app_out "$mname:bad rc(0) from adjust_path($src_name,$dest_name)"
         return 0
      }

      if {$move_do_base_dir} {
         if {![create_base_dir $dest_name 1]} {
            app_out "$mname:exit:bad rc(0) from create_base_dir($src_name,\
                     $dest_name)"
            return 0
         }
      }

      # app_out "$mname:src_name=$src_name,dest_name=$dest_name"

      # use rename to move the file
      if {![qrename $src_name $dest_name]} {
         app_out "$mname:bad rc(0) from qrename($src_name,$dest_name)"
         return 0
      }

      set ret [dscan_findnext src_name]
   }


   if {$task(do_subs)} {
      if {[catch {file delete -force $task(path)}]} {
         app_out "$mname:bad rc(0) from file delete deleting\
                  directory tree '$task(path)'"
         return 0
      }
   }

   return 1
}

proc file_days {fname days} {
   # Get the next file and calculate its age in days. Returns the
   # code from 'dscan', the file/directory name in 'fname' and
   #  and the age in the number of days in 'days' upon success.
   global task file_days_did_init tcl_platform today
   upvar $fname ufname
   upvar $days udays
   set mname "file_days"
   set ufname ""
   set udays 0

   if {!$file_days_did_init} {
      # prepare flags for 'dscan'
      set flags f

      if {$tcl_platform(platform) == "unix"} {
         # add symbolic link detection if running under Unix/Linux/BSD
         append flags l
      }

      if {$task(do_subs)} {
         append flags s
      }

      set file_days_did_init 1

      # initialize 'dscan'
      set ret [dscan_findfirst $task(path) $task(fpat) $flags tmp]
   } else {
      set ret [dscan_findnext tmp]
   }

   if {!$ret} {
      set file_days_did_init 0
      return $ret
   }

   set ufname $tmp

   # get the last modification date of the found object
   if {[catch {file mtime $tmp} tmbuf]} {
      app_out "$mname:unable to get mod date on directory item '$tmp'"
      return 0
   }

   # convert date/time to 'yyyymmdd' for 'datdif'
   set chfdate [clock format $tmbuf -format "%Y%m%d"]

   # calc the number of days difference
   if {![datdif $today $chfdate tmp]} {
      app_out "$mname:bad rc(0) from datdif($ufname)"
      return 0
   }

   # app_out "$mname:$ufname=$chfdate,days=$tmp"
   set udays $tmp
   return $ret
}

proc adjust_path {pfname} {
   # Adjust source path to destination path for the move operation.
   # Final path is returned in 'pfname' upon success.
   # Returns 1 upon success, 0 otherwise.
   global task PATH_SEP
   upvar $pfname upfname
   set mname "adjust_path"

   # split task path, current source path and destination path into parts
   if {![path_split $task(path) 0 tk_drive tk_path ""]} {
      app_out "$mname:exit(0):bad rc from path_split(task(path))"
      return 0
   }

   if {![path_split $upfname 1 pf_drive pf_path pf_name]} {
      app_out "$mname:exit(0):bad rc from path_split(pfname)"
      return 0
   }

   if {![path_split $task(dest_path) 0 tkd_drive tkd_path tkd_name]} {
      app_out "$mname:exit(0):bad rc from path_split(task(dest_path))"
      return 0
   }

   # get the number of directories in the task and current source paths
   set tk_nwords [ll_words $tk_path $PATH_SEP]
   set pf_nwords [ll_words $pf_path $PATH_SEP]
   set i [expr $tk_nwords + 1]
   set j [expr [ll_words $tkd_path $PATH_SEP] + 1]

   # copy each extra directory name into the destination path
   for {} {$i <= $pf_nwords} {incr i;incr j} {
      if {![ll_word $pf_path wrd $i $PATH_SEP]} {
         app_out "$mname:exit(0):bad rc(0) from ll_word($i)"
         return 0
      }

      if {![ll_wordput $tkd_path tmp $wrd $j $PATH_SEP]} {
         app_out "$mname:exit(0):bad rc(0) from ll_wordput($wrd)"
         return 0
      }

      set tkd_path $tmp
   }

   # copy file name
   set tkd_name $pf_name

   # assemble/join new path
   if {![path_join $tkd_drive $tkd_path $tkd_name tmp]} {
      app_out "$mname:exit(0):bad rc(0) from path_join"
      return 0
   }

   # app_out "$mname:dest is '$tmp'"
   set upfname $tmp
   return 1
}

proc create_base_dir {pfname has_fname} {
   # Create the top-level base directory for the move operation.
   # The flag 'has_fname' is used to indicate whether the path
   # ('pfname') has a file name in it. Returns 1 upon success,
   # 0 otherwise.
   global PATH_SEP
   set mname "create_base_dir"
   
   if {$pfname == ""} {
      app_out "$mname:exit(0):empty(pfname)"
      return 0
   }

   if {![path_split $pfname $has_fname drive path fname]} {
      app_out "$mname:exit(0):bad rc(0) from path_split"
      return 0
   }

   set nwords [ll_words $path $PATH_SEP]

   if {$nwords == 0} {
      app_out "$mname:exit(0):path contains no directories"
      return 0
   }

   set wrk ""

   for {set i 1} {$i <= $nwords} {incr i} {
      if {![ll_word $path wrd $i $PATH_SEP]} {
         app_out "$mname:exit(0):bad rc(0) from ll_word($i)"
         return 0
      }

      # assemble path
      if {![ll_wordput $wrk tmp $wrd $i $PATH_SEP]} {
         app_out "$mname:exit(0):bad rc(0) from ll_wordput($i)"
         return 0
      }

      if {![path_join $drive $tmp "" wrk]} {
         app_out "$mname:exit(0):bad rc(0) from path_join($i)"
         return 0
      }

      # app_out "$mname:path=$wrk"

      if {![isdirectory $wrk]} {
         if {[catch {file mkdir $wrk}]} {
            app_out "$mname:exit(0):bad rc from file mkdir($i)"
            return 0
         }
      }

      set wrk $tmp
   }

   return 1
}
         
proc term_app {} {
   # Prepare to terminate the application.
   global log_output

   if {$log_output} {
      logman_end
   }
}

proc set_date {} {
   # Load the 'today' global with today's date in the form
   # 'yyyymmdd'.
   global today

   set tmp [get_time]
   set today [string range $tmp 0 7]
}

proc app_out {mes} {
   # Output a message to the console and/or the log destination.
   global console_output log_output
   set mname "app_out"

   # if no output, return
   if {!$console_output && !$log_output} {
      return
   }

   if {$log_output} {
      logman $mes
   }

   if {!$log_output && $console_output} {
      puts $mes
   }
}

proc usage {} {
   # Display program usage.
   global APNAME SWITCH_CHAR OB CB
   puts [format "usage:%s %c%sq%c %c%sl%c log_file%c%c %c%sn%c %c%sc%c\
                 task_list_file" $APNAME $OB $SWITCH_CHAR $CB $OB \
                 $SWITCH_CHAR $OB $CB $CB $OB $SWITCH_CHAR $CB $OB \
                 $SWITCH_CHAR $CB]
}

# Program main line.
# check number of parameters passed
if {$argc == 0} {
   usage
   exit
}

# initialize
set task(opcode) ""
set task(do_subs) 0
set task(all) 0
set task(tree) 0
set task(numdays) 0
set task(fpat) ""
set task(path) ""
set task(dest_path) ""
set task(dirs) {}

# parse command line
if {![parse_comline $argc $argv]} {
   puts "$APNAME:program abort"
   exit
}

# display logo
app_out "$APNAME for $tcl_platform(platform) Version $VERSION"
app_out "by Rick Smereka, Copyright (c) 2006"
app_out "$APNAME comes with ABSOLUTELY NO WARRANTY"
app_out "This is free software, and you are welcome to redistribute it"
app_out "under certain conditions; see 'gpl.txt' for information."
process
#term_app
exit
