User:Devon McCormick/parseDir.ijs

From J Wiki
Jump to navigation Jump to search
NB.* ParseDir.ijs: tools to parse dir listing->backup most recently changed.
NB. Look for command-line arguments SZLIM (max bytes to backup) and TARGDIR
NB. (target directory to which to write backups).

load '~Code/locationInfo.ijs'   NB. On which machine are we running?  Which external drives?
require '~Code/parallelParseDir.ijs'
require '~Code/exclusions.ijs'

NB. require 'csv task WS'
require 'WS'
coinsert 'jtask'

coclass 'parseDir'
require 'jmf files dir dt logger filefns task'
coinsert 'base fldir'
USUVARS=: 'FLNMS';'FLDTS';'FLSZS';'FLPARENT';'DIRNMS';'DIRDEP'

NB.* buildTimedDiskInfo: build files' info vecs in separate namespace for given disk.
NB.* subdirSzs: show directory sizes for all sub-dirs at specified dir.
NB.* successiveRplc: apply pairs of replacements }.y to >{.y .
NB.* findSlash: find which slash is being used in boxed items.
NB.* jpathsep: change backslashes to slashes in a path.
NB.* dospathsep: change forward slashes to backslashes in a path.
NB.* sizeDirs: Add up sizes of all files in directories and all sub-dirs.
NB.* sumSizesSubdirs: sum file sizes in named dir and all sub-dirs.
NB.* accumSubdirs: accumulate dir indexes for dir named and all sub-dirs.
NB.* crDirLog: create directory log file in \amisc\OfldInfo for drive spec'd.
NB.* nameSpan: given 2 names like pfxYYYYMMDD, combine->pfxYYYYMMD1-D2.
NB.* consolidateBkpsTowardPast: copy all contents of successive dirs into earliest starting with oldest->consolidated backups with newest version overwriting older ones.
NB.* buildBatFl: build .BAT file to create target dirs and copy files to them.
NB.* indicateSubdirs: from boolean selecting DIRNMS, indicate all subdirs.
NB.* rmEndSep: remove terminal path separator from string.
NB.* excludeFiles: exclude designated files from list to back up.
NB.* dix2FullPath: dir index -> indexes for full path
NB.* getDirFlInfo: get info on dirs and files starting at node specified.
NB.* cvtDt2Num: convert Y M D h m s date to single num: YYYYMMDD.day fraction.
NB.* initFlsDir: parse memory-mapped file of directory listing->file, dir info.
NB.* getInfo: get directory into into file, memory-map and parse it.
NB.* process1Subdir: parse single subdir entry->files, parent dirs as globals.
NB.* extract1SubdirList: get first full sub-directory listing out of many.
NB.* addPath: put new parent/child index in tree from text of "dir/subdirs..."
NB.* Tst0addPath_tests_: test adding path to index vec tree from text.
NB.* mcopyto: text of DOS .BAT file to do multiple copies.
NB.* setTargetDir: figure target directory based on machine on which we run.
NB.* setGlobalParms: assign globals according to defaults or command-line overrides.
NB.* NYto01: convert 'N' or 'Y' to 0 or 1, respectively.
NB.* runFromSavedVars: Run backup assuming dir&file vars already saved.
NB.* sizeDirs: Add up sizes of all files in directories and all sub-dirs; x is sort column.
NB.* sumSizesSubdirs: sum file sizes in named dir and all sub-dirs.
NB.* accumSubdirs: accumulate dir indexes for dir named and all sub-dirs.
NB.* crDirLog: create directory log file in \amisc\OfldInfo for drive spec'd.
NB.* nameSpan: given 2 names like pfxYYYYMMDD, combine->pfxYYYYMMD1-D2.
NB.* consolidateBkpsTowardPast: copy all contents of successive dirs into
NB.* buildBatFl: build .BAT file to create target dirs and copy files to them.
NB.* indicateSubdirs: from boolean selecting DIRNMS, indicate all subdirs.
NB.* excludeFiles: exclude designated files from list to back up.
NB.* dix2FullPath: dir index -> indexes for full path
NB.* fmtNumComma3: format (literal) number w/commas after each group of 3.
NB.* dirInfo: put directory info in more usable format: names, dates, sizes, dir flag.
NB.* getDirFlInfo: get info on dirs and files starting at node specified.
NB.* getDirFlInfo: get info on dirs and files starting at node specified.
NB.* cvtDt2Num: convert Y M D h m s date to single num: YYYYMMDD.day fraction.
NB.* initFlsDir: parse memory-mapped file of directory listing->file, dir info.
NB.* getInfo: get directory info into file, memory-map and parse it.
NB.* process1Subdir: parse single subdir entry->files, parent dirs as globals.
NB.* extract1SubdirList: get first full sub-directory listing out of many.
NB.* addPath: put new parent/child index in tree from text of "dir\subdirs..."
NB.* Tst0addPath_tests_: test adding path to index vec tree from text.
NB.* mcopyto: text of DOS .BAT file to do multiple copies.
NB.* setTargetDir: figure target directory based on machine on which we run.
NB.* setGlobalParms: assign globals according to defaults or command-line overrides.
NB.* dailyBackup: run backup for today.
NB.* runFromSavedVars: Run backup assuming dir&file vars already saved.
NB.* getCmdlnArgs: get named arguments from command line.
NB.* runBackup: run usual backup of main drive.
NB.* onlyRuntime: only invoke this if not loaded via usual interactive session.

NB.* buildTimedDiskInfo: build files' info vecs in separate namespace for given disk.
buildTimedDiskInfo=: 3 : 0
   'dsk stDir'=. 2{.y  NB.EG y= 'Z' or 'C';'Program Files'
   nms=. ;:'FLNMS FLDTS FLSZS FLPARENT DIRNMS DIRDEP' [ stDir=. (-.' '-:stDir)#stDir
   exeExp=. (1|.'''''',}:;nms,&.><'_',dsk,'_ '),'=. PllDirInfoEG ''',dsk,':\',stDir,''''
   '''',~'6!:2 ''','''' (] #~ [: >: =) exeExp
NB.EG ". buildTimedDiskInfo 'D' NB. Build vecs "FLNM_D_" etc.
NB.EG ". buildTimedDiskInfo 'D';'amisc\pix\Sel NB. Build vecs "FLNM_D_" only for D:\amisc\pix\Sel.
)

checkFork=: 3 : 0
   svIF=. _1
   if. nameExists 'IsFORKED' do. svIF=. IsFORKED end.
   IsFORKED=: y
   svIF
)
svIF=. checkFork 1

DEBUGON=: 0         NB. Show messages as program progresses (1) or don't (0).
DBGFL=: BASEDSK,'/Temp/parseDir.tmp'          NB. These 3 things allow logging at load
sepLF=: 13 : ';((": :: ])y),10{a.'      NB.  time: DBGFL (log file), sepLF, and
fappendDHM=: 4 : '(,x) (#@[ [ 1!:3) :: _1: (([: < 8 u: >) ::]) y'     NB. this.
3 : 0 ''
   if. DEBUGON do. (;sepLF&.>'parseDir.ijs:23';ARGV_j_,<6!:0 '') fappendDHM DBGFL end.
)

PATHSEP_j_=: '/'
jpathsep=: '/'&(('\' I.@:= ])})
dospathsep=: '\'&(('/' I.@:= ])})

NB.* subdirSzs: show directory sizes for all sub-dirs at specified dir.
subdirSzs=: 3 : 0
   y=. endSlash y
   if. '/' e. >{.DIRNMS do. y=. y rplc '\/' end.       NB. Ensure correct slash
   szs=. sizeDirs y
   szsTop=. (2{.szs),szs#~('/'+/ . =y)='/'+/ . =&>2{"1 szs
NB.EG subszs=. subdirSzs 'C:\amisc\pix\Photos'
)

NB.* successiveRplc: apply pairs of replacements }.y to >{.y .
successiveRplc=: [: > 0 { ((([: > {.) rplc 2 {. }.) ; 3 }. ])^:_
NB.* findSlash: find which slash is being used in boxed items.
findSlash=: [: ~. [: ; ] -.&.> (<'/\') -.&.>~ ]
NB.* sizeDirs: Add up sizes of all files in directories and all sub-dirs; x is sort column.
sizeDirs=: 3 : 0
NB.  Assume globals 'FLNMS FLDTS FLSZS FLPARENT DIRNMS DIRDEP'=. PllDirInfoEG rootdir
   1 sizeDirs y   NB. Col 2 default sort
:
   if. -. nameExists 'DIRNMS' do. 'FLNMS FLDTS FLSZS FLPARENT DIRNMS DIRDEP'=: PllDirInfoEG y end.
   y=. endSlash y
   if. '/' e. >{.DIRNMS do. y=. y rplc '\/' end.       NB. Ensure correct slash
   if. (#DIRNMS)=top=. DIRNMS i. <y do. top=. DIRNMS i. <}.y end.  NB. Allow for terminal slash or not.
   assert. top<#DIRNMS   NB. Ensure y found in DIRNMS
   whamsd=. top,I. (DIRNMS{.~&.>#y) e. <y    NB. WHere Are Main Sub-Dirs (of parent)?
   noFiles=. I. -.(i.#DIRNMS) e. FLPARENT
NB. Account for dirs w/no files in them.
   flparent=. FLPARENT,noFiles [ flnms=. FLNMS,noFiles{DIRNMS [ flszs=. FLSZS,0$~#noFiles
   key=. flparent#~choose=. flparent e. whamsd
   chszs=. key +//. choose#flszs
   dkey=. ~.top,key [ chszs=. chszs,~(-.top e. key)#+/chszs
   tit=. '@dir';'w/subdirs';'Directory'
   x=. 0>.1<.<.x    NB. 0 or 1 are allowable sort columns; put #s first, directories last.
   dirinf=. tit,":&.>x(] \: [: ; [{"1 ])(dkey{DIRNMS),.~(<"0 chszs),.sumSizesSubdirs&.>dkey{DIRNMS
   dirinf=. ((-0{szs){.&.>0{"1 dirinf) 0}&.|:dirinf [ szs=. >./#&>dirinf
   dirinf=. ((-1{szs){.&.>1{"1 dirinf) 1}&.|:dirinf    NB. Right-align numeric columns
)
sizeDirs_Usage_=: 0 : 0
(sizeDirs 'G:\amisc') writedsv 'GamiscDirsSzs120221.txt';TAB;''
sz=. sizeDirs 'Z:\'
maxcol=. 4 : '((->./#&>x{"1 y){.&.>x{"1 y) x}&.|:y'
sz=. 1 maxcol sz [ sz=. 0 maxcol sz  NB. Align "number" columns
(;LF ([ ,~ [: }: [: ; ])&.><"1 sz,&.><TAB) fwrite 'ZdirsFlsSzs.txt'
)

NB.* sumSizesSubdirs: sum file sizes in named dir and all sub-dirs.
sumSizesSubdirs=: 3 : '+/FLSZS#~FLPARENT e. accumSubdirs y'
NB.* accumSubdirs: accumulate dir indexes for dir named and all sub-dirs.
accumSubdirs=: 3 : 0
   down1=. all=. DIRNMS i. boxopen y
   while. 0<#down1=. I. DIRDEP e. down1 do. all=. all,down1 end.
   all
NB.EG subdixs=. accumSubdirs 'V:/Recent/utl'
)

NB.* crDirLog: create directory log file in \amisc\OfldInfo for drive spec'd.
crDirLog=: 3 : 0
   y=. {.y,'D'                NB. Drive, e.g. D:, for which
   y=. (],':' #~ ':'~:{:)y    NB.  to create log file.
   1!:44 BASEDSK,'\amisc\ofldinfo\' [ svdir=. 1!:43 ''
   lognm=. '.dir',~>_1{<;._1 ' ',(]{.~i.&LF) CR-.~shell 'dir ',y
   shell 'dir /o-d /s ',BASEDSK,'\ > "',lognm,'"'
   1!:44 svdir
)

NB.* nameSpan: given 2 names like pfxYYYYMMDD, combine->pfxYYYYMMD1-D2.
nameSpan=: 3 : 0
NB. y=. ({:,{.) dd  NB. Earliest, latest
   endlast=. 1 i.~ ~:/>y
   (>0{y),'-',endlast}.>1{y
)

consolidateBkpsTowardPast=: 3 : 0
NB.* consolidateBkpsTowardPast: copy all contents of successive dirs into
NB. earliest starting with oldest->consolidated backups with newest version
NB. overwriting older ones.
   y=. 2{.boxopen y         NB. Files' prefix, dir in which to work
   pfx=. openbox {.y         NB. Prefix is "WL" for laptop, "WD" desktop
   if. 0=#>1{y do.
       wrkdir=. BASEDSK,'/Temp/'
   else. wrkdir=. endSlash >1{y
   end.
   dd=. jd dir wrkdir,'*.'   NB. Just directories
   dd=. (<pfx,'[0-9]{8}$') rxfirst&.>dd   NB. Only names like {pfx}YYYYMMDD
   dd=. \:~dd-.a:             NB. Order from most to least recent.
NB.    if. -.nameExists 'WTTMINC_TMP_' do.
NB.        WTTMINC_TMP_=. 2 1     NB. Add 1 sec to each subsequent wait
NB.    end.
   if. nameExists 'TOPDIR' do. svTD=. TOPDIR end.
   TOPDIR=: (wrkdir rplc '/\';'\') rplc '/\'
   2 moveDirOverAnother/\dd
   newnm=. nameSpan ({:,{.) dd
   cmd=. (TOPDIR{.~>:TOPDIR i. ':'),' && cd ',(TOPDIR}.~>:TOPDIR i. ':'),' && '
   cmd=. ((cmd,'ren "',(>{:dd),'" "',newnm,'"') rplc '/\';'\') rplc '/\'
   shell cmd
   if. nameExists 'svTD' do. TOPDIR=: svTD   NB. Re-instate or
   else. 4!:55 <'TOPDIR' end.                NB.  remove global.
   newnm
NB.EG consolidateBkpsTowardPast 'Dell';'D:/Temp/'
)

consolidateBkpsTowardPast_testCase_=: 3 : 0
   drnms=. (<y),&.>(<'HLL201206'),&.>_2{.&.>'0',&.>":&.>>:i.30
   flnms=. (<'/file'),&.>(":&.>>:i.#drnms),&.><'.txt'
   ({.,{:) cmds=. (<'echo '),&.>(":&.>(#drnms)?@$1e9),&.>'>',&.>drnms,&.>flnms
   assert. a:*./ . =rr=. shell&.>(<'mkdir '),&.>drnms rplc &.><'/\'
   assert. a:*./ . =rr=. shell&.>cmds
   consolidateBkpsTowardPast_base_ 'HLL';y
   assert. dirExists_base_ y,'/HLL20120601-30'
   assert. *./fileExists_base_ &> (<y,'/HLL20120601-30'),&.>flnms
NB.EG consolidateBkpsTowardPast_testCase_ 'D:/Temp/amisc/Tmp/'
)

cleanupConsolidateBkpsTowardPast_testCase_=: 3 : 0
   drnms=. (<y),&.>(<'HLL201206'),&.>_2{.&.>'0',&.>":&.>>:i.30
   drnms=. drnms rplc&.><'/\'
   shell&.>(<'del /Q /S '),&.>drnms
   shell&.>(<'rmdir '),&.>drnms
NB.EG a:*./ . =cleanupConsolidateBkpsTowardPast_testCase_ 'D:/Temp/amisc/Tmp/'
)

NB. KMPFX, KNMACH from locationInfo.ijs
machid=. >KMPFX{~KNMACH i. {.whoami ''       NB. Tailor commands to machine on which we're running.
todayDt=. ([: ; ([: ": {.) ; [: 2&lead0s 1 2&{) qts''

NB. Display commonly-used set of commands in usual sequence; EXTERNDRV from locationInfo.ijs
usuReminder=: (0 : 0) rplc '{todayDt}';todayDt;'{machid}';machid;'{xdrv}';EXTERNDRV
". buildTimedDiskInfo }:BASEDSK
". buildTimedDiskInfo 'D'
nms=. USUVARS
(<'/Temp/') fileVar_WS_&.>,|:USUVARS,&.>/('_',(}:BASEDSK),'_');'_D_'
FLNMS_CD_=: FLNMS_C_,FLNMS_D_
FLPARENT_CD_=: FLPARENT_C_,(#DIRNMS_C_)+FLPARENT_D_
FLDTS_CD_=: FLDTS_C_,FLDTS_D_
FLSZS_CD_=: FLSZS_C_,FLSZS_D_
DIRNMS_CD_=: DIRNMS_C_,DIRNMS_D_
DIRDEP_CD_=: DIRDEP_C_,(#DIRNMS_C_)+DIRDEP_D_
(<'/Temp/') fileVar_WS_&.>USUVARS,&.><'_CD_'
6!:2&.>'''batfl cmds''=. (".&.>USUVARS,&.><''_CD_'') buildBatFl_parseDir_ 10e6;(BASEDSK,''/Temp/{machid}{todayDt}/'');'''';<''CDMCCopyE.bat''';'shell batfl'
6!:2&.>'''batfl cmds''=. (".&.>USUVARS,&.><''_CD_'') buildBatFl_parseDir_ 50e6;''D:\''';'shell batfl'
6!:2&.>'''batfl cmds''=. (".&.>USUVARS,&.><''_CD_'') buildBatFl_parseDir_ 30e6;''Z:\''';'shell batfl' NB. Backup drive
6!:2&.>'''batfl cmds''=. (".&.>USUVARS,&.><''_D_'') buildBatFl_parseDir_ 10e6;(BASEDSK,''/Temp/{machid}{todayDt}/'');'''';<''CDMCCopyE.bat''';'shell batfl'
6!:2&.>'''batfl cmds''=. (".&.>USUVARS,&.><''_D_'') buildBatFl_parseDir_ 50e6;''D:\''';'shell batfl'
6!:2&.>'''batfl cmds''=. (".&.>USUVARS,&.><''_D_'') buildBatFl_parseDir_ 30e6;''Z:\''';'shell batfl' NB. Backup drive
6!:2&.>'''batfl cmds''=. (".&.>USUVARS,&.><''_D_'') buildBatFl_parseDir_ 20e6;''J:\''';'shell batfl'

str=. 'shell ''xcopy ',BASEDSK,'\Temp\{machid}{todayDt} {dsk}:\Temp\{machid}{todayDt} /S /C /H /I /R /Q /Y'''
6!:2 '".&>(<str) rplc&.><"1 (<''{dsk}''),.''D'';''F'';''G'';''Z'''

80 (] {."1~ [ <. [: {: [: $ ]) ":,.rr=. ".&.>_2}.<;._2 usuReminder [ smoutput qts'' NB. Max 80 cols display
usuReminder=: usuReminder rplc ' :';'_:'
)

3 : 0 ] svIF
   if. y=_1 do. 4!:55 <'IsFORKED' else. IsFORKED=: y end.
)

3 : 0 ] usuReminder;svIF
   if. _1=>1{y do. IsFORKED=: 0 end.
   ml=. >./ #&>possib=. '-jijx';'-rt'
   if. -. IsFORKED +. (ml{.&.>possib) +./ . e. ml&{.&.>tolower&.>ARGV_z_ do.
       smoutput >0{y end.
   if. _1=>1{y do. 4!:55 <'IsFORKED' end.
)

buildBatFl=: 3 : 0
NB.* buildBatFl: build .BAT file to create target dirs and copy files to them.
   (FLNMS;FLDTS;FLSZS;FLPARENT;DIRNMS;<DIRDEP) buildBatFl y
:
   'szlim targ DescAsc batflnm'=. 4{.y   NB. 'batflnm' is optional
   'FLNMS FLDTS FLSZS FLPARENT DIRNMS DIRDEP'=. x
   if. 0=#DescAsc do. DescAsc=.'D' end.   NB. Dates in descending order by default.
   targ=. endSlash targ
   xclud=. (FLNMS;FLPARENT;DIRNMS;<DIRDEP) excludeFiles targ
   if. 'D'-:DescAsc do. dtdord=. xclud-.~\:FLDTS  NB. File list indexes in date desc. order
   else. dtdord=. xclud-.~/:FLDTS end.            NB.  or date ascending (oldest first) order.
   ix=. FLSZS figureWhichFiles szlim;<dtdord
   NB. 0{DIRNMS, up to ':', gives disk from which to copy.
   cmds=. (<'//';'/') rplc~&.>(<({.~>:@:i.&':');0{DIRNMS),(FLNMS;FLPARENT;<DIRNMS) makeCopyCmds targ;<ix
   cmds=. dospathsep &.> cmds
   tmpd=. getTempDir ''
   cmds v2f batfl=. tmpd,>(4<:#y){'CDMDCopy.bat';<batflnm
   batfl;<cmds
NB.EG 'batfl cmds'=. buildBatFl 700e6;'C:\Temp\Recent\'
)

figureWhichFiles=: 3 : 0
   FLSZS figureWhichFiles y
:
   'szlim dtdord'=. y   NB. szlim: max,[min] size to copy.
   FLSZS=. x
   if. 2=#>szlim do. szlim=. {:lims=. \:~>szlim else. lims=. szlim,0 end.
   toobig=. szlim<:dtdord{FLSZS    NB. Exclude any single file > size limit
   toobig=. toobig#dtdord          NB.  as this will truncate list prematurely.
   dtdord=. dtdord-.toobig
   ss=. +/\dtdord{FLSZS
   selection=. (ss>:{:lims)*.ss<:{.lims
NB. Do better job noting which files excluded because too big; the following
NB. fails to account for cutoff.
   if. DEBUGON do.
       pl=. 's'#~1~:#toobig
       logMsg_logger_ 'Excluding file',pl,' singly > size limit: ',":#toobig
   end.
   selection#dtdord
)

locChildren=: 3 : 0
   DIRDEP locChildren y
:
   (<^:(L. = 0:)y),~(<#~0~:#) I. x e. >0{y
)

indicateSubdirs=: 3 : 0
   DIRDEP indicateSubdirs y
:
   ~.;;x&locChildren^:_&.>I. y
)

indicateSubdirs0=: 3 : 0
NB.* indicateSubdirs: from boolean selecting DIRNMS, indicate all subdirs.
   xdix=. I. y
   childxd=. xdix-.~I. DIRDEP e. xdix
   while. 0<#childxd do.
       xdix=. ~.xdix,childxd
       childxd=. xdix-.~I. DIRDEP e. xdix
   end.
   xdix                       NB. Index into DIRNMS of all subdirectories.
NB.EG xdix=. indicateSubdirs DIRNMS e. 'c:/amisc';'c:/Program Files'
)

excludeFiles=: 3 : 0
NB.* excludeFiles: exclude designated files from list to back up.
   (FLNMS;FLPARENT;DIRNMS;<DIRDEP) excludeFiles y
:
   'targ xd xf'=. extractExclusions y
   'FLNMS FLPARENT DIRNMS DIRDEP'=. x
   if. 0 *./ . =#&>xd;<xf do. xclud=. i.0 else.
       assert. 1=#ps=. '\/'#~'\/' e. ;DIRNMS     NB. Path Separator being used.
       whxd=. DIRNMS}.~&.>(2*;':'e.&.>DIRNMS)*;DIRNMS i.&.>':'  NB. Drop disk
       whxd=. (toupper&.>whxd)e. ({.ps),&.>toupper&.>xd,<targ   NB. Exclude target
       whxd=. (1) (DIRDEP indicateSubdirs whxd)}whxd         NB.  to avoid recursion.
       whxf=. (toupper&.>FLNMS)e. toupper&.>xf        NB. Exclude files.
       whxf=. whxf+.({.xf)e.~(#>{.xf){.&.>FLNMS       NB. No *~ (emacs save) files.
       xf=. toupper&.>xf#~(2{.&.>xf)e. <'*.'          NB. Simple wild-card:
       xf=. '.' (]}.~[:>:]i.[)&.>xf                   NB. just suffixes.
       whxf=. whxf+.xf e.~toupper&.>'.'(]}.~[:>:]i:[)&.>FLNMS NB. Final .suffixes
       xclud=. I. whxf+.FLPARENT e. I. whxd
   end.
NB. xclud is list of indexes into FLNMS=files to exclude.
)

NB.* dix2FullPath: dir index -> indexes for full path
dix2FullPath=: [ (] ,~ _1 -.~ [ {~ [: {. ])^:_ [: ] ]
NB.EG DIRPARENT&dix2FullPath 239
NB.* fmtNumComma3: format (literal) number w/commas after each group of 3.
fmtNumComma3=: ([: }: [: ; ',' ,~&.> [: |. [: |.&.> _3 <\ |.)"1
NB.EG ('123';'1,234';'1,234,567') -: fmtNumComma3 &.> '123';'1234';'1234567'

showDFSzDt=: 3 : 0
  ((;&.>'\',~&.>&.>(<DIRNMS){~&.>DIRPARENT&dix2FullPath &.> y{FLPARENT),&.>y{FLNMS),.((fmtNumComma3@:":)&.>y{FLSZS),.DateTimeCvt&.>y{FLDTS
)

NB.* dirInfo: put directory info in more usable format: names, dates, sizes, dir flag.
dirInfo=: ([:((0{"1]) ; ([:>1{"1]) ; ([:;2{"1]) ; [:<'d'e.&>4{"1]) dir)

getDirFlInfo=: 3 : 0
NB.* getDirFlInfo: get info on dirs and files starting at node specified.
   dskInf=. >(];[: dirInfo '\*',~]) generalWalkTree rmEndSep y
   DIRNMS=: dospathsep&.>0{"1 dskInf NB. Full names of all paths
   DIRNMS=: DIRNMS rplc&.><'\\';'\'  NB. '\\'->'\'
   DIRDEP=: dirDependencies DIRNMS NB. Dirs' dependency tree: parent indexes
   isfl=. -.;4{"1 dskInf           NB. Exclude dir info->only files.
   FLNMS=: isfl#;1{"1 dskInf
   ned=. 0~:#&>2{"1 dskInf         NB. No empty directories
   FLDTS=: isfl#;cvtTS21Num &.>ned#2{"1 dskInf    NB. Date as single num: YYYYMMDD.day fraction
   FLSZS=: isfl#;3{"1 dskInf       NB. File size in bytes.
   FLPARENT=: isfl#;(#&.>3{"1 dskInf)#&.>i.#DIRNMS
   FLNMS;FLDTS;FLSZS;FLPARENT;DIRNMS;<DIRDEP
)

getDirFlInfo0=: 3 : 0
NB.* getDirFlInfo: get info on dirs and files starting at node specified.
   tree=. dirtree y
   fpths=. (}.~(<:@-@(i.&PATHSEP_j_)@|.))&.>0{"1 tree  NB. Paths w/o filenames.
   DIRNMS=. dirpath y             NB. All dirs - even those with no files
   DIRDEP=: dirDependencies DIRNMS=. ~.DIRNMS,~.fpths  NB. Dir dependency tree
   FLPARENT=: DIRNMS i. fpths                          NB. Parent dirs of files
   assert. FLPARENT *./ . <#DIRNMS                     NB. Found all paths?
   FLNMS=. ((>:@#)&.>fpths)}.&.>0}"1 tree              NB. bare filenames
NB.   FLDTS=: ;100#.&.>3{.&.>1{"1 tree                NB. Num yyyymmdd dates
NB.   max=. 5+24 60 60#.24 60 60 NB. Max secs/day + 5 for any leap seconds.
NB.   FLDTS=: FLDTS+max%~;(<24 60 60)#.&.>_3{.&.>1{"1 tree
   FLDTS=: ;cvtDt2Num >1{"1 tree        NB. Num YYYYMMDD.(day fraction) dates
   FLSZS=: ;2{"1 tree                   NB. Very large files have signed sizes
   FLSZS=: FLSZS+(2^32)*FLSZS<0         NB.  so adjust them.
   FLNMS;FLDTS;FLSZS;FLPARENT;DIRNMS;<DIRDEP
NB.EG 'FLNMS FLDTS FLSZS FLPARENT DIRNMS DIRDEP'=: getDirFlInfo_parseDir_ 'C:\'
)

NB.* cvtDt2Num: convert Y M D h m s date to single num: YYYYMMDD.day fraction.
cvtDt2Num=: 3 : 0"1
   NB. 90065 = 5 + 24 60 60#.24 60 60 NB. Max secs/day+5 fudge for leap secs.
   (100#.3{.y)+90065%~24 60 60#._3{.y
NB.EG ' 20090803.32600899' -: 18j8":cvtDt2Num 2009 8 3 8 9 22
)
NB. Only distinguishes to about 1/10,000 second.

DOMAP=: 1
NB. -------- Dir listing fns: parse text file directory listing:
NB. -------- this is a separate way to accomplish what has been done above.
initFlsDir=: 3 : 0
NB.* initFlsDir: parse memory-mapped file of directory listing->file, dir info.
   if. DOMAP do. JCHAR map_jmf_ 'DIRLSTFL';y-.CR
   else.  DIRLSTFL=: CR-.~fread y end.
   DBSTR=: LF,' Directory of '
   WHDB=: (I. DBSTR E. DIRLSTFL),<:#DIRLSTFL NB. Where directory breaks are
   DIRNMS=: FLNMS=: ''
   FLSZS=: DIRPARENT=: FLPARENT=: i.0
   FLDTS=: 0$0.0
)

3 : 0 ''
   if. DEBUGON do. (;sepLF&.>'parseDir.ijs:465';<6!:0 '') fappendDHM DBGFL end.
)

getInfo=: 3 : 0
NB.* getInfo: get directory info into file, memory-map and parse it.
NB.   shell 'cmd /C dir /A /S C:\ > C:\allfls2.dir'
   dirlstfl=. y
   if. 0=#dirlstfl do.
       dirlstfl=. 'D:\allfls2.dir'
       shell 'dir /A /S D:\ > ',dirlstfl
   end.
   initFlsDir dirlstfl
   for_ix. i.<:#WHDB do.
       ch=. extract1SubdirList WHDB{~ix+0 1
       process1Subdir ch
   end.
   if. DOMAP do. unmapall_jmf_ '' end.
   assert. *./nameExists&>nms=. 'FLNMS';'DIRNMS';'FLPARENT';'FLSZS';'FLDTS';'DIRPARENT'
   nms
)

process1Subdir=: 3 : 0
NB.* process1Subdir: parse single subdir entry->files, parent dirs as globals.
   ch=. a:-.~<;._2 (LF#~-.LF={:y),~y    NB. break into lines; no empty lines
   whd=. (ch{.~&.><:#DBSTR) i. <DBSTR-.LF
   thisdir=. (] }.~ [: <: ':' i.~ ]) >whd{ch
   'isnew thisdn'=. addPath thisdir
   ch=. }:ch}.~>:whd               NB. Drop dir info header and summary trailer.
   ch=. ch#~' '~:;{.&.>ch          NB. Get rid of lines beginning with space.
   ch=. <;._1&.>' ',&.>dsp&.>ch    NB. break apart lines by spaces
NB. Account for time variants: '12:34a' vs. '12:34 AM'.
   whnms=. 3+time2pcs=. *./(2{"1 >ch) e. 'AM';'PM'
NB. re-join any names with embedded spaces
   ch=. |:>(whnms{.&.>ch),&.><&.>(}.@;)&.>(' '&,)&.>&.>whnms}.&.>ch
   if. time2pcs do. ch=. (([: }:&.> [: ,&.>/ ' ' ,~&.> 1 2 { ]) 1} (<<<2) { ]) ch end.   
   chtit=. 'DATE';'TIME';'SIZE';'NAME'  NB. row titles for "ch"
   whmootdirs=. (ch{~chtit i. <'NAME')e. ,&.>'.';'..'
NB. "SIZE" column has "<DIR>" indicator for directory, size for file.
   whdir=. ((ch{~chtit i. <'SIZE') e. '<DIR>';'<JUNCTION>')*.-.whmootdirs
   addPath&.>(<thisdir,'\'),&.>whdir#ch{~chtit i. <'NAME'
   whfls=. -.whdir+.whmootdirs
   ch=. whfls#"1 ch
   FLNMS=: FLNMS,ch{~chtit i. <'NAME'
   FLPARENT=: FLPARENT,(+/whfls)$thisdn
   FLSZS=: FLSZS,;n2j&.>(ch{~chtit i. <'SIZE')-.&.>','
   FLDTS=: FLDTS,;DateTimeCvt &.>,&.>/' ',&.>ch{~chtit i. 'DATE';'TIME'
   thisdn
)

extract1SubdirList=: 3 : 0
NB.* extract1SubdirList: get first full sub-directory listing out of many.
   'st end'=. y
   ch=. CR-.~(st+i.>:end-st){DIRLSTFL
)

addPath=: 3 : 0
NB.* addPath: put new parent/child index in tree from text of "dir\subdirs..."
   p2a=. <;._1 '\',y    NB. Path to add, e.g. 'C:\top\mid\bottom'
   p2a=. p2a-.a:
   isnew=. 0
NB.    if. 0=#wh=. I. DIRNMS e. 0{p2a do.  NB. Completely new root
NB.        DIRPARENT=: DIRPARENT,_1    NB. "Parent Index" of _1 means root node.
NB.        wh=. <:#DIRNMS=: DIRNMS,0{p2a
NB.        isnew=. 1
NB.    else. wh=. {.wh#~_1=wh{DIRPARENT end.     NB. or same old root
   wh=. _1
   for_nm. p2a do.                 NB. "wh" is parent index of current node...
       if. 0=#wh2=. I. DIRNMS e. nm do. NB. new subdir
           DIRPARENT=: DIRPARENT,wh
           wh=. {.<:#DIRNMS=: DIRNMS,nm NB. will be parent of next node, if any
           isnew=. 1
       else.
           if. 0=#wh2=. wh2#~wh=wh2{DIRPARENT do. NB. Name exists but with
               DIRPARENT=: DIRPARENT,wh           NB.  different parent.
               wh=. {.<:#DIRNMS=: DIRNMS,nm
               isnew=. 1
           else. wh=. {.wh2 end.                  NB. Name exists with
       end.                                       NB.  same parent
   end.
   isnew,wh                              NB. 0 if path was already here
)

Tst0addPath_tests_=: 3 : 0
NB.* Tst0addPath_tests_: test adding path to index vec tree from text.
   coinsert 'parseDir base'
   EV=: getEnviVars ''
   WINDIR=: endSlash ,>EV{~<1,~(toupper&.>0{"1 EV)i.<'WINDIR'
   
   d0=. DIRNMS=: 'D:';'Aegis';('\'-.~WINDIR}.~WINDIR i. '\');'Web';'printers';'foo';'Web'
   dp0=. DIRPARENT=: _1 0 0 3 4 0 0

   assert. 0 2-:addPath WINDIR     NB. Shouldn't add it again.
   assert. d0-:DIRNMS              NB. Should not have changed
   assert. dp0-:DIRPARENT          NB. Should not have changed

   td=. 'D:\foo\bar'               NB. New path starting from new root
   assert. 1 9-:addPath td         NB. but with same-named sub as existing
   assert. (d1=. DIRNMS)-:d0,<;._1 '\',td
   assert. (dp1=. DIRPARENT)-:_1 0 0 0 3 4 0 0 _1 8 9
   assert. 0 9-:addPath td    NB. Shouldn't add it again.
   assert. d1-:DIRNMS         NB. Should not have changed
   assert. dp1-:DIRPARENT     NB. Should not have changed

   assert. 1 10-:addPath WINDIR,'foo'
   1
)

NB.* mcopyto: text of DOS .BAT file to do multiple copies.
mcopyto=: 0 : 0
Rem MCopyTo.bat: Multiple COPY TO %1: copy %2, %3, %4, etc.
:START
If %1/==/ goto SHOWHOW
Set tmpnm=%1
:DO1
If %2/==/ goto BYEBYE
Copy %2 %tmpnm% > nul
Shift
Goto DO1
:SHOWHOW
Echo on
Rem  MCOPY target source1 source2...sourceN
Echo off
Goto BYEBYE
:BYEBYE
Set tmpnm=
)

NB.* setTargetDir: figure target directory based on machine on which we run.
setTargetDir=: 3 : 0
   if. -.nameExists 'TARGDIR' do. TARGDIR=. '' end.
   if. -.nameExists 'TMPDIR' do. TMPDIR=. getTempDir '' end.
   if. 0=#TARGDIR do.              NB. Assign dir prefix based on machine, if known.
       pfx=. KMPFX
       'machnm usernm'=. whoami ''
       pfx=. >pfx{~KNMACH i. <machnm         NB. Prefix based on computer name
       today=. ;4 2 2 lead0s&.>3{.qts ''     NB. today's date as 'yyyymmdd'
       dirnm=: TMPDIR,pfx,today,'\'
   else. dirnm=: endSlash TARGDIR end.
   TMPDIR;dirnm
)
NB. ARGV_z_=: ARGV_z_,<'-jijx'

NB.* setGlobalParms: assign globals according to defaults or command-line overrides.
setGlobalParms=: 3 : 0
   SZLIM=: ''$>1e7 lookupValAfterName ARGV_z_;<'SZLIM'
   if. -.isNum SZLIM do. SZLIM=: ".SZLIM end.
   RUNFROMSAVED=: NYto01 {.>0 lookupValAfterName ARGV_z_;<'RUNFROMSAVED'
   TARGDIR=: jpathsep ,>lookupValAfterName ARGV_z_;<'TARGDIR'
   SRCDIR=: ,>'D:\' lookupValAfterName ARGV_z_;<'SRCDIR'
   RUNANYDAY=: NYto01,>'N' lookupValAfterName ARGV_z_;<'RUNANYDAY'
   if. -.nameExists 'DEBUGON' do.
       DEBUGON=: NYto01,>0 lookupValAfterName ARGV_z_;<'DEBUGON'
   end.
   ans=. ans,:".&.>ans=. 'SZLIM';'RUNFROMSAVED';'TARGDIR';'RUNANYDAY';'DEBUGON'
)

NB.* dailyBackup: run backup for today.
dailyBackup=: 3 : 0
   if. -.nameExists 'DEBUGON' do. DEBUGON=: 0 end.
   setGlobalParms ''      NB. Cmd-line size limit, target dir, if present.
   wkdy=. dow 3{.qts ''   NB. Weekday number: 0=Sunday, 6=Saturday
   if. RUNANYDAY+.wkdy e. 1 2 3 4 5 do.      NB. Only Mon-Fri unless
       try. sink=. runDaily '' [ 4!:55 <'ASSERR'   NB.  any day allowed.
       catch. if. nameExists 'ASSERR' do. logMsg_logger_ ASSERR
              else. msg=. '*** Unspecified error in "runDaily" at '
                    logMsg_logger_ msg,'.',~showdate ''
              end.
       end.
   end.
)

NB.* runFromSavedVars: Run backup assuming dir&file vars already saved.
runFromSavedVars=: 3 : 0
   '\amisc\' runFromSavedVars y
:
   bkpargs=. y          NB. 'szlim targ'=. y
   vn=. USUVARS
   (<x,'\'#~'\'~:{:x) unfileVar_WS_&.>vn
   ".&.>(<'parseDir_'),&.>vn,&.>(<'_=. '),&.>vn
   'batfl cmds'=. buildBatFl bkpargs
   shell batfl
   wait 2                                    NB. Give .BAT chance to start.
NB.EG runFromSavedVars 5e6;'C:\Temp\WL20050315\'
)

3 : 0 ''
   if. DEBUGON do. (;sepLF&.>'parseDir.ijs:647';<6!:0 '') fappendDHM DBGFL end.
)

coclass 'base'
coinsert 'parseDir'
3 : 0 ] svIF
   if. _1=y do. 4!:55 <'IsFORKED'
   else. IsFORKED=: y end.
)

getCmdlnArgs=: 3 : 0
NB.* getCmdlnArgs: get named arguments from command line.
   parms=. 'NUM';'NAME'
   num=. ".,>lookupValAfterName CMDLN;<'NUM'      NB. Example numeric value
   num=. >(0=#num) { num;0
   name=. ,>lookupValAfterName CMDLN;<'NAME'      NB. Example character value
   num;name
NB.EG 'num name'=: getSFCmdlnArgs ''
)

NB.* runBackup: run usual backup of main drive.
runBackup=: ([: (80 (] {."1~ [ <. [: {: $) [: ": ,.) [: ".&.> 0 2 3 _3 _2 { <;._2)
NB.EG runBackup usuReminder

onlyRuntime=: 3 : 0
NB.* onlyRuntime: only invoke this if not loaded via usual interactive session.
   if. -.nameExists 'OverrideDefault' do. OverrideDefault=: 0 end.
   if. -.OverrideDefault do.
       if. (5{.&.>'-jijx';<'-rt') +./ . e. 5&{.&.>tolower&.>ARGV_z_ do.
           80 (] {."1~ [ <. [: {: [: $ ]) ":,.rr=. ".&.> ((i.13),_4 _5) { <;._2 usuReminder
           2!:55 ''                              NB. Terminate session.
       end.
   end.
)

onlyRuntime ''