/* + CopyDir ver 1.16d -- A utility to robustly copy a directory tree. Created by: Daniel Hellerstein (danielh@crosslink.net) Last modified: 05 May 2002 Usage. From an OS/2 command prompt: : x:>COPYDIR source_dir dest_dir [-opts] where: source_dir -- source directory dest_dir -- destination directory [-opts ] -- list of options (they MUST follow dest_dir) Alternatively, enter COPYDIR at an OS/2 prompt, and answer the questions. Note: Use * to specify the current directory (for either source or dest dir) : The options include: -8 -- Always convert "long" (HPFS) style file names to unique 8.3 (FAT) names; even if destination drive can support long file names. -ALL -- Aggressive replace. -ALL is a shortcut for: -Rar -Share -Attribs=**** -Check -ALL allows you to take a snapshot of a drive or a directory. If you use -ALL, only a subset of the other options are recognized. These are: -MERGE -NOMERGE -LOG -LOGNEW -NOPAUSE and -V -ATTRIBS=xxxx -- Set AHRS attributes of destination files and directories. x can be: - (clear), + (set) , or * (use source) The four positions correspond to AHRS respectively: the archive, hidden, readonly, and system attributes. By default (if -ATTRIBS is not set), all attributes on the destination files (& directories) are cleared. -ERRFILE=filename -- specify fully qualified name of an "error file". The error file is created ONLY if an error occurs (such as the failure to copy a locked file, or a failure to copy a file with bad sectors). If no absolute path is specified in filename, then filename will be written relative to the current (default) directory. If you do NOT specify an -ERRFILE option, then if any error occurs a file named COPYDIR.ERR will be created in the directory containing this program. For example, if you installed COPYDIR.CMD to C:\OS2\APPS, then C:\OS2\APPS\COPYDIR.ERR will be created (assuming an error occurs). Notes: * Actually, the name of the default error file can be set with the ERRFILE user changeable parameter. * in contrast to the LOG file, the error files is ALWAYS overwritten. * we do NOT recommend putting the error file, or the log file, in the directory being copied. Doing so might cause a 32= sharing violation for source or target file error -- due to the log (or error) file being locked by COPYDIR! While not fatal, it is sort of ugly. * To include spaces in the filename, bracket the filename with " characters. For example: -ERRFILE="MY ERRORS" -CHECK -- Check if destination drive has enough space -FilesOnly -- Do NOT process subdirectories (that is, just copy the files in the source directory to the target directory) -I=astring -- Only include files that contain astring You can specify multiple occurences of -I= Use double quotes (") to include spaces, -, &, and other wierd characters in astring -LOG=filename -- log ALL actions to a logfile. This is written in addition to the ERRFILE (the ERRFILE is only created if an error occurs). If no absolute path is specified in filename, then filename will be written relative to the current (default) directory. If you do NOT specify a "filename" (that is, you just include a -LOG option), then a file named COPYDIR.LOG will be created in the directory containing this program. For example, if you installed COPYDIR.CMD to C:\OS2\APPS, then C:\OS2\APPS\COPYDIR.LOG will be created. Notes: * Actually, the name of the default error file can be set with the LOGFILE user changeable parameter. * The logfile is appended to. Each new addition starts with the time, date and parameters used in the COPYDIR command. * If you want to overwrite prior versions of the log file, use the -LOGNEW option. * we do NOT recommend putting the log file, or the error file, in the directory being copied. Doing so might cause a 32= sharing violation for source or target file error -- due to the log (or error) file being locked by COPYDIR! While not fatal, it is sort of ugly. * To include spaces in the filename, bracket the filename with " characters. For example: -LOGFILE="MY LOGFILE.LOG" -LOGNEW=filename -- Same as -LOG, but overwrite filename. -LOGEVENT=event_type -- Select what types of events to log The currently recognized event types are: ALL -- log all events. THIS IS THE DEFAULT. COPY= log all successful and unsuccessful copies Do NOT log "did not replace" events EXCLUDE= log "excluded" files COPY_EXCLUDE= Combination of copy and exclude -MERGE -- Copy source files and subdirectories "into" the destination. This overrides the to_under parameter (that can be modified in COPYDIR.CMD). It has the same effect as using a trailing \* in the destination directory. -METIC=x -- Read file lists, and copy files, meticulously. This means that rather then doing these actions all at once, they will be done in smaller chunks. While slower, this does allow for more frequent status reports, and gives the user more chance to interevene (say, to hit ESC to abort). This overrides the METICULOUS user changeable parameter. x= Y : enable meticulous mode x= N : normal (non-meticulous)mode -NOMERGE -- Copy source files and subdirectories "under" the destination. This override the to_under parameter. It also overrides a trailing \* on the destination directory. -MOVE -- Rename files, or copy and delete source. Within the same drive, a rename is use. Across drives, copy followed by deletion of source file (given a sucessful copy). -NOCHECK -- Do not check if destination drive has enough space -NOPAUSE=v -- Overrides the DOPAUSE parameter (see below) Use -NOPAUSE to suppress cancellation & confirmation queries. v is optional, it can be 0,1, or 2 0: do NOT override dopause. -NOPAUSE=0 is the same as not specifying -NOPAUSE 1: override dopause. -NOPAUSE=1 is the same as -NOPAUSE 2: aggressive no-pause: NEVER ask for user input (fail instead) -PARAMFILE=filename -- The fully qualified name of a parameter file. Since the list of options can be quite long, you can specify them in a "parameter file", and use -PARAMFILE=filename to tell COPYDIR to read the command line options from this parameter file The contents of filename will be read and added to the list of command line options. Precisely, the "-PARAMFILE=filename" phrase will be replaced by the contents of filename. The contents of filename should consist of a set of COPYDIR options; entered just as you would have entered them from the command line. For readability purposes, you can use CRLFs (new lines) -- they are treated as if they were spaces. Notes: * you can have use zero, one, or several -PARAMFILE options * -PARAMFILE is recursive (-PARAMFILE options in a parameter file will BE processed). * if you specify a relative filename, the filename is assumed to be in the current directory. Note that this is different then the LOG and ERRFILES, which use the "COPYDIR.CMD directory" as the default directory. * Lines in a parameter file that begin with a semi colon (;) are ignored * COPYDIR.SMP is a sample of a very simple parameter file * To include spaces in the filename, bracket the filename with " characters. For example: -PARAMFILE="COPYDIR PARAMS.1" -Q -- Quiet mode -QQ -- Very quiet mode -R[abcdfonqrs0Z] -- Replacement criteria. If -R is not specified, newer (or same aged) files are never replaced. Criteria include: a(lways), b(igger), c(heck crc), d(elete), f(uture), o(lder), n(ewer), q(uery), r(readony), s(maller), 0(ero length), z(never) By default -R is the same as -Raq. You can change this by setting the rdefault parameter (see below) For details on -R, see below. -SHARE -- Copy even if source file is open. That is, copy even if a normal copy would cause a "sharing error" (i.e.; a sys0032 file being used by another process error). -SHARE is supported ONLY if the FILEREXX library is available. Note the -SHARE suppresses "true moves" -- when -MOVE is specified, rename is not used (instead, copy followed by source file deletion is used, though "shared" files will never be deleted). -TEST -- Test mode. No files will be copied or moved. Useful when combined with -LOG, to see what will be done. Perhaps this should be called "dry run mode" -V -- Verify all copies, and cross drive moves (using CRC check). Make up to 3 attempts if verification fails. Note the -V suppresses "true moves" -- when -MOVE is specified, rename is not used (instead, copy followed by source file deletion is used). -VIEW_LOG=xx -- View the log file, after all copying has been done. xx=0 : do NOT view the log file xx=1 : you will be asked if you want to view the log file xx=2 : display the log file (without asking) Notes: * -VIEW_LOG overrides the VIEW_LOG parameter (set below) * if a log file is not specified, VIEW_LOG is ignored. * the LOGLISTER variable controls what is used to display the log file. -X=astring -- Exclude files that contain astring You can specify multiple occurences of -X= Use double quotes (") to include spaces, -, &, and other wierd characters in astring -ZIP=filename -- Create directory specific filename.ZIP files on the the destination drive, with each of these ZIP files containing the contents of the source directory. FOR THIS -ZIP OPTION TO WORK, ZIP.EXE MUST BE SOMEWHERE IN YOUR PATH (for example, in x:\OS2\APPS, where x: is your bootdrive). Notes: * The -V, -R, -MOVE, and -ATTRIBS are ignored if -ZIP is specified. * The -X and -I options are not applied to file names (they are applied to directory names) * To include spaces in the filename, bracket the filename with " characters. For example: -ZIP="MY ZIPFILE" + = =================================== = : Notes: * if the destination drive is FAT, the 8.3 conversion of filenames will always be done (if necessary). * when an 8.3 conversion occurs, a name of the form XXXXXnnn.EXT is used; where XXXXX is the first 5 characters of the source file's name, nnn is an integer from 001 to 999, and EXT is the first 3 characters of the source file's extension. * When copying from an HPFS to a FAT drive: If a long directory name is encountered (on the HPFS drive) the directory's contents will NOT be copied. A note WILL be made in the ErrorFile for each such failure. * If a bad sector (or some other form of error) prevents copying a file, the file will be skipped (that is, COPYDIR will try and copy the other files). If -MOVE is specified, files that are not copied (due to an error) are NOT deleted. * By default, if any problems occur a file with the name COPYDIR.ERR is created. This file contains entries that identify problems encountered -- such as a "failure to copy a file" or "longname converted". For more details, see the description of the -ERRFILE option. * If there is not enough room on the destination drive, you will be asked to "continue anyway". Note that this "not enough room" calculation is made without accounting for the possibility of overwriting a file, hence is conservative. * If the source or destination directory contains spaces, be sure to place the directory name between double quote (") characters. * -ATTRIBS examples Note: you can use a shortcut of -ATTRIBS=x where x is -, +, or *. This is equivalent to ----, ++++, and **** (respectively) -ATTRIBS=**** : destination files and directories will have same attributes as source -ATTRIBS=*--- : archive same as attributes; read, hidden, and system attributes are cleared -ATTRIBS=-*+* : archive attribute cleared, hidden and system attributes from source file, readonly attribute set. -ATTRIBS=- : clear all attributes in destination file * -R notes -R is used to specify replacement critieria. They are NOT used if the destination file does not exist. You can specify several case-insensitive criteria, which will be processed in the order entered. Definition: Replace means "copy, or move, the source file to the destination file" The currently recognized (case-insensitive) criteria are: These two are "termination" criteria; criteria following them are never considered. > a : Always replace. > z : Never replace. These next are "necessary" criteria. If they are not satisfied, replacement does NOT occur > b : source file MUST be bigger (or same size) > s : source file MUST be smaller > n : source file MUST be newer > o : source file MUST be older (or same age) > c : source and destination files MUST have different CRC These are sufficient critieria. If true, replacement occurs. If not true, then subsequent criteria are examined. > 0 : if destination files is 0 length, replace (even if it is newer) > f : if destination file has date later then today (a date in the future), replace it (even though it's newer!) Careful, this uses local time! These are modifiers. Their order in the criteria list does not matter. > q : Always ask the user to verify the replacment. This query only occurs if all the other replacement criteria are satisfied (if replacement would otherwise occur). > d : if -MOVE is specified, then ALWAYS delete the source file. This occurs regardless of whether a copy occurs. However, if a failure occurs (say, due to a bad sector) then the source file will NOT be deleted. > r : if the destination file is read only, then you MAY overwrite it. Note that if r is not specified, read only files (files with a +r attribute) are NEVER replaced. That is, -Ra does NOT replace read-only files, but -Rra (or -Rar) DOES. If all of the specified "necessary" criteria are satisfied (and z is not specified), replacement occurs (regardless of file age). More notes on -R criteria: + If -R is not specified, newer files will not be replaced. + If z is specified but the destination file does not exist, the source file WILL be copied + the q and d criteria can be anywhere + q does not apply to files that do not satisfy the other replacement, inclusion, or exclusion criteria. In other words, the user is queried only about files that are not otherwise skipped. + By default, -Raq is the same as -R. This default can be changed by modifying the rdefault parameter. + When a readonly file is replaced, it's replacement is NOT set to be readonly (the new destination file has a -r attribute). -R Examples: -R : depends on value of rdefault. Typically, -R is same as -Raq -R0n : always replace zero length, and older, files, -Rs : replace if source is smaller -Rb : replace if source is bigger -Rso : replace if source is smaller and older -Rc : replace if CRC's don't match (regardless of age) -Rsr : replace smaller & older files, even if they are read only (older only is the default) -Rac : same as -Ra -- the "c" is never considered -Rqn : replace if source is newer, but first ask -Rdb : replace if source is bigger. Regardless of whether source is bigger or smaller, if -MOVE is specified, delete the source file * -ZIP notes: + If you do not include =filename after a -ZIP option, a name derived from today's date is used. For example, 20NOV01.ZIP (corresponding to 20 Nov 2001) + The -ZIP option causes a .ZIP file to be created on the destination drive, and filled with files from the corresponding source drive. The same .ZIP filename is used in each destination directory (though, of course, each of these .ZIP files will contain unique content). Thus, if you ran COPYDIR on 28 Mar 2001, then COPYDIR C:\OLD D:\NEW -ZIP might lead to the following ZIP files being created D:\NEW\28MAR01.ZIP D:\NEW\DIR1\28MAR01.ZIP D:\NEW\DIR2\28MAR01.ZIP D:\NEW\DIR2\SAMPLES\28MAR01.ZIP (assuming that C:\OLD had DIR1, DIR2, and DIR2\SAMPLES subdirectories). * -LOG and -LOGNEW notes + Do NOT include spaces in the -LOG (or -LOGNEW) filename! + For -LOG, if a logfile exists, it will be appended to. For -LOGNEW, it will be overwritten + If the destination directory of a logfile does not exist, the program exits. * -MERGE and -NOMERGE notes When copying a directory (it's files and it's subdirectories) to a preexisting destination directory, one can either ... a) copy TO. This is a "merge", and is equivalent to xcopy source\*.* destination\*.* b) copy UNDER. This is a "directory replication", and is equivalent to xcopy source\*.* destination\source\*.* For example, assuming that E:\MYFILES and F:\OURFILES exist, and that E:\MYFILES\FOO.BAR exists. Then, given the action ... COPYDIR E:\MYFILES F:\OURFILES a) TO mode yields F:\OURFILES\FOO.bAR b) UNDER mode yields F:\OURFILES\MYFILES\FOO.BAR There are three ways of selecting which mode to use: 1) Use a -MERGE or -NOMERGE switch -MERGE selects the TO mode -NOMERGE selects the UNDER mode 2) Select TO mode by appending a \* to the destination directory. Alternatively, use COPYDIR SOURCE DESTINATION\SOURCE\* to perform an UNDER copy. For example: COPYDIR E:\NEW\MYFILES F:\OURFILES\* is the same as COPYDIR E:\NEW\MYFILES F:\OURFILES -MERGE and COPYDIR E:\NEW\MYFILES F:\OURFILES\MYFILES\* is the same as COPYDIR E:\NEW\MYFILES F:\OURFILES -NOMERGE 3) Modify the TO_UNDER parameter If there is any ambiguity, COPYDIR will ask you which you want to do. Note that -MERGE and -NOMERGE override a trailing \*, and a trailing \* overrides the TO_UNDER parameter. * -X and -I notes: + You can specify multiple "exclusion" strings, and you can specify multiple "inclusion" strings. Note that the fully qualified filename (including drive and directory information) is compared to each string. Thus, for ... -x=:\TEMP\ -- exclude d:\TEMP\foo.bar include d:\www\temp\goo.bar include d:\www\temp2\hoo.bar -X=\TEMP\ -- exclude d:\TEMP\foo.bar exclude d:\www\temp\goo.bar include d:\www\temp2\hoo.bar -X=\TEMP -- exclude d:\TEMP\foo.bar exclude d:\www\temp\goo.bar exclude d:\www\temp2\hoo.bar + If you specify both -I= and -X= entries, then only files that -- match (one of) the -I= entries and -- do NOT match (any of) the -X= entries will be copied (or moved). + The -I= or -X= string should NOT contain * or ? wildcard characters (a substring match of "string" against the filename is attempted). * The following options can be modified in the program file (COPYDIR.CMD): dopause = pause a few seconds before starting (allow for cancellation) errfile = default filename where errors should be recorded fileSeconds = use seconds when comparing file ages forceYes = when used with >0 values of dopause, default is NOT continue logfile = default filename to use when -LOG or -LOGNEW is specified loglister = program to use to display logfile metic = enable meticulous mode one_per_line = display one file message per line to_under = default: copy source "to", or "under" destination rdefault = sets the default action of -R testsize = similar to -CHECK verbosity = similar to -Q and -QQ view_log = controls whether or not the log file is displayed zip_opts = specify options to use when ZIPping files Examples: COPYDIR E:\DOCS G:\DOCS COPYDIR F:\GAMES\NEW E:\FUN -8 -CHECK -R COPYDIR F:\MAIL G:\MAILARC -8 -CHECK -Rz COPYDIR "My Docs" "F:\archive\Old Docs" -NOCHECK COPYDIR F:\DOCS\CURRENT I:\ARCHIVE\Y1998 -ZIP COPYDIR F:\MYFILES M:\ARC\MYFILES\15MAR01 -x=\TEMP\ -x=.BAK -x="Back UP" COPYDIR F:\PHOTOS E:\GIFFILES -I=.GIF COPYDIR F:\ANIMALS D:\FELINES -I=CAT -X=DOG This next will do a systematic backup of all files on the C:\ drive, including hidden and system files. Copied files will have the same attributes as the originals. COPYDIR C:\ F:\ARCHIVE\C_TODAY\* -Ra -ATTRIBS=* Requires: The REXXLIB library (which may be included in the distribution file) Disclaimer: This is freeware. It is to be used at one's risk, the authors and any potentially affiliated institutions bear no responsibility for any untoward, unexpected, or disasterous effects arising from the use or misuse of this program. You may freely use and/or distribute this program, or pieces of it. I DO ask for proper attribution. .end of description (do not remove this line) */ /* ------------ USER changeable Parameters ---------- */ /* Note -options supplied by the user may override some of these user chanegable parameters. */ /* colors to use on user prompts; 7 is normal */ fromdircolor=46 todircolor=47 optcolor=11 /* Number of seconds to wait before starting to copy. This allows the user to cancel (by hitting ESC) Set to 0 to suppress this "allow for cancellation" */ dopause=20 /* Fully qualified name of file to write "errors" to. If blank, then COPYDIR.ERR is created in the directory containing this program (the directory containing COPYDIR.CMD) */ errfile='' /* when comparing timestamps of source and destination files, CopyDir can either use the minute of creation, or the second of creation (or of last write access). Using the minute is a bit quicker. To use seconds, set file_seconds=1. To use minutes, set file_seconds=0 */ file_seconds=0 /* If dopause >0, then forceYes (forceYes=0 or 1) controls whether the default action is to "continue" or to "cancel". You probably want to use a high value of dopause when forceYes=1. forceYes=0 means "pause for cancellation. If no cancellation, start copying". forceYes=1 means "pause for confirmation. If no confirmation, exit" */ forceYes=0 /* Fully qualified name of file to write "log" to (used if -LOG or -LOGNEW is specified with a filename) If blank, then COPYDIR.LOG is created in the directory containing this program (the directory containing COPYDIR.CMD) */ logfile='' /* default value of "meticulous" filelist create & file copy 0= normal 1= meticulous */ meticulous=0 /* copydir will attempt to write the name of each file to the screen as it is copied. If one_per_line=0, then copydir will attempt to write several such filenames to each row on your display. If one_per_line=1, then copydir will write one filename per line */ one_per_line=0 /* when copying a directory (its files and its subdirectories) to a preexisting destination directory, one can either a) copy "to". This a "merge", and is equivalent to xcopy source\*.* destination\*.* b) copy "under". This is a "move", and is equivalent to xcopy source\*.* destination\source\*.* TO_UNDER controls the "default" behavior 1 = copy "under" 2 = copy "to" 3 = ask user to choose Note that appending \* to the destination will ALWAYS force "copy to". Similiarly, appending \source\* to the destination will ALWAYS force "copy under". Also note that the -MERGE and -NOMERGE options override to_under */ to_under=3 /* Name of a program, or texteditor, to use to view logfile Leave blank to use built in text displayer (the one used to display the introduction).*/ loglister='' /* set the default action of -R (that is, or -R without any criteria) If '', aq is used (i.e.; -R = -Raq) */ rdefault='aq' /* Check if there is enough room on destination drive =1 : Check =0 : Do NOT check This is overridden by -CHECK and -NOCHECK options */ testsize=1 /* Verbosity: 0 = Minimal 1 = Average 2 = Lots (displays all problems) */ verbose=1 /* If a log file has been specified, COPYDIR can display it after all copying has been completed. VIEW_LOG controls this: VIEW_LOG=0 : Do NOT display the log file VIEW_LOG=1 : Ask the user if he wants to view the log file VIEW_LOG=2 : View the log file This can be overridden by the -VIEW_LOG parameter */ view_log=1 /* Extra options to use when ZIPping files. These should be options understood by the ZIP program. For example: -S = include system and hidden files -9 = tighter compression (a bit slower, though) -o = sets the zipfile date to the date of the latest file inside Notes: * use of -r is not advised, since it will replicate COPYDIR's recursion feature * use of -j, and especially -q, is highly advised! */ zip_opts=' -j -q -S -9 ' /* ------------ END of USER changeable Parameters ---------- */ /*--- Load REXX libraries ----- */ /* Load up advanced REXX functions */ foo=rxfuncquery('sysloadfuncs') if foo=1 then do foo=RxFuncAdd('SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs') if foo=0 then call SysLoadFuncs end foo=rxfuncquery('rexxlibregister') if foo=1 then do foo=rxfuncadd('rexxlibregister','rexxlib', 'rexxlibregister') if foo=0 then call rexxlibregister end foo=rxfuncquery('rexxlibregister') if foo=1 then do say "Sorry, this utility requires the REXXLIB library " exit end /* file rexx functions available? */ got_filerexx=1 if rxfuncquery('cpy_fileread')=1 then do do forever foo1=rxfuncadd('cpy_fileread','FILEREXX','fileread') if foo=1 then do /*can't get filerexx functions */ got_filerexx=0 leave end foo2=rxfuncadd('cpy_filegetinfo','FILEREXX','filegetinfo') foo3=rxfuncadd('cpy_fileopen','FILEREXX','fileopen') foo4=rxfuncadd('cpy_fileclose','FILEREXX','fileclose') leave end end /* doscopy error list */ doscopys.='other error' doscopys.2='source file not found ' doscopys.3=' source or target path not found ' doscopys.5 =' target file exists but -R(eplace) not specified ' doscopys.55 =' target file exists and is Readonly ' doscopys.RENAME= 'error renaming source file' doscopys.VER_REN=' unable to rename target file prior to copying (verification failure)' doscopys.VER_TEMP=' no available temporary filename (verification failure)' doscopys.USER_CANCEL='copying cancelled at user request' doscopys.32 =' sharing violation for source or target file ' doscopys.108 =' source or target drive locked ' doscopys.112 = 'disk full ' doscopys.206 = 'invalid source or target file name ' doscopys.267 = 'source name is a directory ' doscopys.282 = 'extended attributes not supported for target ' sysmkdirs.='other error' sysmkdirs.2='Error. File not found. ' sysmkdirs.3='Error. Path not found. ' sysmkdirs.5='Error. Access denied. ' sysmkdirs.26='Error. Not a DOS disk.' sysmkdirs.87='Error. Invalid parameter. ' sysmkdirs.108='Error. Drive locked. ' sysmkdirs.206='Error. Filename exceeds range. ' sysfiledeletes.='other error' sysfiledeletes.2=' Error. File not found. ' sysfiledeletes.3=' Error. Path not found. ' sysfiledeletes.5=' Error. Access denied. ' sysfiledeletes.16=' Error. Current directory. ' sysfiledeletes.26=' Error. Not DOS disk. ' sysfiledeletes.32=' Error. Sharing violation. ' sysfiledeletes.36=' Error. Sharing buffer exceeded. ' sysfiledeletes.87=' Error. Invalid parameter ' sysfiledeletes.108=' Error. Drive locked. ' sysfiledeletes.206='Error. Filename exceeds range error ' parse source os type name oo=lastpos('.',name) if errfile='' then do if oo>0 then errfile=left(name,oo)||'ERR' else errfile=name'.ERR' end errfile=dosfname(errfile) gfile='' then do if oo>0 then /* default logfile */ logfile0=left(name,oo)||'LOG' else logfile0=name'.LOG' /* if logfile specified, use it */ if logfile<>'' then do logfile0=dosfname(logfile) end aesc='1B'x cy_ye=aesc||'[37;46;m' cyanon=cy_ye normal=aesc||'[0;m' bold=aesc||'[1;m' re_wh=aesc||'[31;47;m' reverse=aesc||'[7;m' nowdir=directory() nowdrive=filespec('d',nowdir) nowpath=filespec('p',nowdir) fatal_error='' fromkeyb=0 parse arg bax bb=strip(bax) if bb='/?' | bb='?' | bb='-?' then do Say bold||"CopyDir ver 1.16d"||normal||, " ... an OS/2 utility to copy a directory tree. " say "Usage: x:>COPYDIR fromdir todir [-opts] " say " Where: fromdir= source directory " say " todir = destination directory " say " [-opts]= one or more options. " say say "Or, COPYDIR with no arguments to be prompted. " foo=wait_sec(0) if foo=2 then exit call showhelp 0 exit end if bb='-??' | bb='??' | bb='???' | bb='-???' then do noww=0 if bb='???' | bb='-???' then noww=3 a=show_intro(noww,,'CopyDir ver 1.16d') exit end /* do */ bax='?' if bax='' then say "Copy a directory tree (? for help)." qq=pos('"',bax) if qq>0 then do do jj=1 to 2 parse var bax a1 bax ; a1=strip(a1) if abbrev(a1,'"')>0 then do if pos('"',a1)>0 then do parse var a1 '"' a1 '"' tt bax=tt||bax end else do /* " contains space */ parse var bax a2 '"' bax a1=a1||a2 end end /*a1 starts with " */ ins.jj=a1 end in1=strip(ins.1,,'"') in2=strip(ins.2,,'"') astuff2=bax end else do parse arg in1 in2 astuff2 end astuff2=translate(astuff2) call getopts If in1='?' then do Say "CopyDir 1.16d: A utility to copy a directory tree " say "Usage: x:>COPYDIR fromdir todir [-opt] " say " Where: fromdir= source directory " say " todir = destination directory " say " [-opts] = one or more options. " foo=wait_sec(0) if foo=2 then exit call showhelp 0 exit end /* do */ a1: if in1='' then do fromkeyb=1 /* keyboard io */ say ' 'cy_ye|| , 'CopyDir ver 1.16d. Enter ?? for more help'||normal say ' ' do forever amess= " From what directory: " call charout,bold|| amess||normal fromdir=stringin(,length(amess)+1,'?',78,,fromdircolor) if length(fromdir)<3 then do if c2d(fromdir)=0 then exit end say if fromdir='?' then do say " Enter a "bold"source"normal" directory." say " All the files, and all the subdirectories, in and under this" say " source directory will be copied." say " To select the current directory, enter * " say " " iterate end if fromdir='??' then do a=show_intro(2,,'CopyDir ver 1.16d') iterate end fromdir=translate(fromdir,'\','/') leave end say end else do fromdir=in1 end /* do */ in1='' fromdir=strip(fixdir(fromdir,nowdrive)) say fromdir if dir_exists(fromdir)=0 then do say "No such xdirectory: " fromdir if dopause_severe=1 then do if dolog=1 then do call lineout logfile, "No such directory: " fromdir call lineout logfile end exit end else do say "Try anyways (1=yes)? " pull ii if ii<>1 then signal a1 end ff=strip(fromdir,,'\') end else do ff=strip(fromdir,,'\') say " >>> Copying from: "ff if dolog=1 then call lineout logfile," >>> Copying from: "fromdir end fromdrive=filespec('d',fromdir) say a2: nop if fromkeyb=1 then do /* keyboard input */ do forever amess= " To what directory: " call charout,bold|| amess||normal todir=stringin(,length(amess)+1,'?',78,,todircolor) if length(todir)<3 then do if c2d(todir)=0 then exit end say todir=translate(todir,'\','/') if todir='?' | todir='??' then do say " Enter a "bold"destination"normal" directory." say " * Files (and subdirectories) on the source directory are copied" say " either 'into or 'under' this "bold"destination"normal" directory." say " To copy 'into' the destination, end with a "bold"\* "normal say " To copy 'under' the destination, end with a "bold"\ "normal if to_under=2 then say " If you end without \* or \, then coping will be 'into'" if to_under=1 then say " If you end without \* or \, then coping will be 'under'" if to_under=3 then say " If you end without \* or \, then you will be asked to choose" say " * Setting current directory as the destination: " say " To copy into the current directory, enter "bold"*"normal say " To copy 'under' the current directory, enter "bold"*\ "normal say " * If destination directory does not exist, copy 'into' is used " say " " iterate end leave end /* look for *, *\, trailing \, or trailing \* */ todir=strip(todir) say select when todir='*' then do domerge=1 end when todir='*\' then do domerge=-1 todir='*' end when right(todir,1)='\' then do domerge=-1 end when right(todir,2)='\*' then do domerge=1 end otherwise do nop end end end else do todir=in2 end /* do */ in2='' todir=fixdir(todir,nowdrive,to_under,fromdir,domerge) todrive=filespec('d',todir) if dir_exists(strip(todir,,'\'))=0 then do if dopause_severe=1 then do mkit=1 end else do say " No such destination directory: "todir mkit=yesno(' |Create it? ','No Yes','Yes') if mkit=0 then do say " bye ..." exit end end foo=0 /* testmode assumes success */ if testmode=0 then foo=sysmkdir2(todir) if foo<>0 then do say "Unable to create directory: "todir say " >> "sysmkdirs.foo exit end if dolog=1 then call lineout logfile,' Creating destination directory: 'todir end if fromkeyb=1 then do astuff2a='' do forever if astuff2a='' then do say amess=" Enter -options (? for help, ENTER when done): " end else do say " Current options: "astuff2a amess=" More -options: " end call charout,bold|| amess||normal astuff2=stringin(,length(amess)+1,' ',60,,optcolor) if length(astuff2)=0 then exit /* esc hit */ if astuff2='' then leave say if astuff2='?' then do call showhelp 1 iterate end astuff2=translate(astuff2) if astuff2='??' then do a=show_intro(2,,'CopyDir ver 1.16d') iterate end astuff2a=astuff2a||' '||astuff2 end astuff2=astuff2a call getopts /* PARSE THE OPTIONS LIST !! */ say end /* true move possible (and no verification); or must we copy and delete ? */ if domove=1 & doverify=0 & doshare=0 then do if filespec('d',fromdir)=filespec('d',todir) then domove=2 end if dozip=0 then say " >>> Writing to: " todir else say " >>> Archiving to " zipfile".ZIP files in " todir say xx='' if user_tou=0 then xx=' (-MERGE) ' if user_tou=1 then xx=' (-NOMERGE) ' say " .... Using Options: " astuff2_use||xx say if dolog=1 then do call lineout logfile,' ' if dozip=0 then do if domove=0 then call lineout logfile," >>> Writing to: " todir else call lineout logfile," >>> Moving to: " todir end else do call lineout logfile," >>> Archiving to " zipfile".ZIP files in " todir end call lineout logfile,' ' call lineout logfile," .... using Options: " astuff2_use call lineout logfile,' ' if logappend=1 then say " >>> Appending to Log File: "logfile else say " >>> Creating Log File: " logfile say ' ' end todrivetype=dosfilesys(strip(todrive,,':')) fromdrivetype=dosfilesys(strip(fromdrive,,':')) if trunc8=1 then do select when todrivetype='FAT' & fromdrivetype<>'FAT' then do say " Caution: copying from non-FAT to FAT drive ." if dolog=1 then call lineout logfile, " Caution: copying from non-FAT to FAT drive ." end when todrivetype<>'FAT' & fromdrivetype<>'FAT' then do say "Caution: copying from non-FAT to non-FAT, with 8.3 truncation " if dolog=1 then call lineout logfile, " Caution: copying from non-FAT to FAT drive." end otherwise do say "Copying from FAT drive; 8.3 truncation not required " if dolog=1 then call lineout logfile, , "Copying from FAT drive; 8.3 truncation not required " end end end else do if todrivetype='FAT' & fromdrivetype<>'FAT' then do say " ** Warning: writing to a FAT drive from a non-FAT drive " if fromkeyb=1 then do aa=yesno(' Continue (long names will be truncated) ?') if aa=0 then exit end trunc8=1 end end getit=strip(fromdir,,'\')'\*.*' if filesonly=1 then do stuff.0=0 end else do if meticulous=0 then do say " Reading directories under "fromdir wow=sysfiletree(getit,'stuff','DOS') end else do wow=metic_sysfiletree(getit) end end j1=stuff.0+1 stuff.j1=fromdir ieelen=length(stuff.j1) stuff.0=j1 if testsize=1 then do /* get size of this, see if enough room on target */ call test_size end if dopause>0 & forceyes=0 then do ii=wait_sec(dopause) if ii=2 then do say ' ' say " bye " if dolog=1 then do call lineout logfile,' Cancelled by user! ' call lineout logfile end exit end end if dopause>0 & forceyes=1 then do do forever ii=wait_sec(dopause,'Hit Y to confirm, ESC to cancel, R to review.','YyNnRr'||'1b0d0a'x) if ii=5 | ii=6 then do /* review */ say "Source directory: "fromdir say "Target directory: "todir say "Options: "astuff2 if dolog=1 then say "Logfile= "logfile say "Error file (if needed)= "errfile iterate end if ii<1 | ii>2 then do say ' ' say " bye " if dolog=1 then do call lineout logfile,' Cancelled by user! ' call lineout logfile end exit end leave end end say trunc8s.0=0 badcopies.0=0 baddeletes.0=0 nexcludes_file=0 nexcludes_dir=0 nfiles=0 nbytes=0 ;badbytes=0 ;nbytes0=0 nfiles2=0 llen=0 gotlong1=0 errlist.0=0 /* might need current datestam */ tday=date('O') o2=left(tday,2) if o2<>'19' & o2<>'20' then do if o2>80 then tday='19'||tday else tday='20'||tday end tday=translate(tday,'_','/') ttime=time('N') nowtime=tday||'_'||translate(ttime,'_',':') nowtime=left(nowtime,length(nowtime)-3) say ' '||cyanon||'..... hit ESC at any time to cancel '||normal do mm=1 to stuff.0 ba=inkey('n') if c2d(ba)=27 then do say say reverse"Cancelling."normal if dolog=1 then do call lineout logfile,'Cancelled by user.' call lineout logfile end exit end stuff.mm.!del=0 if exclude_me(stuff.mm||'\',0)>0 then do nexcludes_dir=nexcludes_dir+1 foo=write_log('Not copying excluded DIR 'stuff.mm,'EXCLUDE') say bold||mm 'of 'stuff.0||normal||')Not copying excluded DIR 'stuff.mm iterate end stuff.mm.!del=1 /* candidate for rd (if -MOVE specified */ thisdir=strip(stuff.mm,,'\') mkit=strip(strip(todir,,'\')||'\'||strip(substr(thisdir,ieelen+1),,'\'),,'\') if attribs.0>0 then do oo=sysfiletree(stuff.mm,'goo.','DT') dattribs=strip(word(goo.1,3)) end foo=1 if trunc8=1 then foo=checkdir8(mkit) if foo=0 then do /* unable to write to this directory */ nk=badcopies.0+1 badcopies.nk=stuff.mm badcopies.nk.2=mkit badcopies.nk.1='*.*' badcopies.0=nk iterate end if verbose>0 then do bb=mm 'of 'stuff.0||") "thisdir " to " mkit '..... ' if length(bb)<78 then do say bold||mm 'of 'stuff.0||normal||") "thisdir " to " mkit '..... ' end else do aa=left(bold||mm ' of 'stuff.0||normal||") "thisdir,82) say aa aa=bold||" to "||normal|| mkit say copies(' ',length(mm 'of 'stuff.0||") ")-4)||aa end end if testmode=0 then do /* create destination directory */ yow=sysmkdir2(mkit) nuatt='' if attribs.0>0 then do /* set its attribute */ do jn=1 to 5 select when attribs.jn='-' then do nuatt=nuatt||'-' end when attribs.jn='+' then do nuatt=nuatt||'+' end otherwise do ta=substr(dattribs,jn,1) if ta<>' ' & ta<>'-' then nuatt=nuatt||'+' else nuatt=nuatt||'-' end end /* select */ end /* jn */ foo=sysfiletree(mkit,'goo','D',,nuatt) /* set its attributes */ end /* attribs.0>0 */ end /* testmode */ wow=sysfiletree(thisdir'\*.*','stuff2a.','ft') do kk=1 to stuff2a.0 parse var stuff2a.kk . . att akk stuff2.kk=strip(akk) if attribs.0>0 then fattribs.kk=strip(att) end if attribs.0>0 then fattribs.0=stuff2a.0 stuff2.0=stuff2a.0 llen=0 igoo=0 if dozip=1 then do if stuff2.0=0 then iterate /* nothing to zip */ say " ZIPping "stuff2.0 " files." foo=write_log(" ZIPping "stuff2.0 " files.") azipfile=mkit||'\'zipfile if trunc8=1 then azipfile=fix8(azipfile) azipfile='"'||azipfile||'"' thisdir='"'||thisdir||'"' foo=0 /* testmode assumes success */ if testmode=0 then do address cmd '@ZIP '||zip_opts||' '||azipfile ' 'thisdir'\*' foo=rc end if rc=0 then foo=write_log('ZIP '||zip_opts||' '||azipfile ' 'thisdir'\*') else foo=write_log('Error 'rc' on ZIP -j -q '||zip_opts||' '||azipfile ' 'thisdir'\*') nfiles=nfiles+stuff2.0 iterate end /* if here NOT doing -ZIP */ /* .... copy each file */ do mmm=1 to stuff2.0 ba=inkey('n') if c2d(ba)=27 then do say say reverse"Cancelling."normal if dolog=1 then do call lineout logfile,'Cancelled by user.' call lineout logfile end exit end if exclude_me(stuff2.mmm,1)=1 then do nexcludes_file=nexcludes_file+1 foo=write_log('Not copying excluded file 'stuff2.mmm,'EXCLUDE') iterate end /* if here, not "excluded" (though might still fail "Replacement" criteria */ nfiles=nfiles+1 yee1=filespec('N',stuff2.mmm) targfile=mkit||'\'||yee1 targfile0=targfile if trunc8=1 then targfile=fix8(targfile) fsize=stream(stuff2.mmm,'c','query size') if fsize='' then do /* missing source file */ if verbose>0 then say 'ERROR: missing source file 'stuff2.mmm if dolog=1 then call lineout logfile,'ERROR: missing source file 'stuff2.mmm iterate end /* Check the -R criteria .... */ parse value check_replace(stuff2.mmm,targfile) with , destexists ',' ok ',' isreadonly ok=strip(ok) if ok=1 & doquery=1 then ok=query_replace(stuff2.mmm,targfile) if ok=5 then leave /* skip to next directory */ if ok<>1 then do foo=write_log(ok) if del_domove=1 then do /* d forces deletion of source */ aa=1 /* even if other crits not satisfied */ if testmode=0 then aa=del_source(stuff2.mmm) if aa=1 then fii=write_log(' .... source deleted 'stuff2.mmm) end iterate /* do not record anything */ end /* Replacement criteria were binding */ nfiles2=nfiles2+1 /* actually copied files */ nbytes0=nbytes0+fsize verified=1 /* assume proper copy */ tf2='' /* used if verification enabled */ isshared=0 /* source is being "shared" */ /* change +r attribute on destination ? */ if isreadonly=1 & destexists<>'' & testmode=0 then do foo=sysfiletree(targfile,'hoo.','F',,'***-*') end /* either rename, or copy */ do iter=1 to 3 /* try up to 3 attempts (on a verify */ truemove=-1 copyok=0 /* assume copy/move failure */ /* note: true move never done if -v enabled */ if (domove>1 & destexists='') then do /* true move possible, and no conflict */ truemove=1 fsize=dosfileinfo(stuff2.mmm,'s') foo=1 /* testmode assumes success */ if testmode=0 then foo=dosrename(stuff2.mmm,targfile) if foo=0 then cstat='RENAME' else copyok=1 end else do /* either a copy, or a move to new drive */ truemove=0 fsize=dosfileinfo(stuff2.mmm,'s') cstat=0 /* testmode default is success */ /* note: don't "Rename target" if it doesn't exist, or if already renamed targfile */ if doverify=1 then do /* move the target out of the way */ targis=stream(targfile,'c','query exists') if iter=1 & targis<>' ' then do /*move old file out of the way*/ ipp=lastpos('.',targfile) ; if ipp=0 then ipp=length(targfile)+1 tf1=left(targfile,ipp-1)||'.$??' tf2=dostempname(tf1) if tf2='' then do cstat='VER_TEMP' /* this should rarely happen */ end else do oo=dosrename(targfile,tf2) if oo=0 then cstat='VER_REN' end end /* make tf2-- old version of targfile */ end /* doverify */ if testmode=0 & cstat=0 then /* verify failures may prevent copying */ cstat=c_doscopy(stuff2.mmm,targfile,doshare,meticulous,one_per_line) /* THIS DOES THE WORK! */ if cstat='SHARED' then do cstat=0 ; isshared=1 end if cstat=5 then cstat=55 /* readonly failure */ if cstat=0 then copyok=1 /* success */ end /* verify? Note: true moves are never done if verification is on */ /* For obvious reasons, don't verify in testmode! */ if doverify=1 & copyok=1 & truemove=0 & testmode=0 then do if iter=1 then do ocrc=shared_filecrc(stuff2.mmm,isshared) if ocrc='' then do verified=0 fii=write_log('Verify failed, unable to read 'stuff2.mmm) leave end end ncrc=filecrc(targfile) if ocrc<>ncrc then do say ; say "Verify failed: "stuff2.mmm' 'targfile fii=write_log("Verify failed ("iter "of 3): "stuff2.mmm' 'targfile) verified=0 iterate end verified=1 end leave end /* "verifiction" iters loop */ /* cleanup & record results */ if copyok=1 & verified=1 then call copied_okay else call copied_notokay end /* go do next file */ if verbose>0 & igoo=1 then say ' ' end /* all directories * */ /* delete directories? */ if domove>0 then call do_rmdir /* All done except the status report .... */ if verbose >-1 then say bold' ----------------- 'normal if dolog>0 then do call lineout logfile,' ' say if domove=0 then do call lineout logfile, , " Copied "nfiles2 " files from " stuff.0 " directories: " end else do call lineout logfile,, " Moved "nfiles2 " files from " stuff.0 " directories: " end if excludes.0>0 then do CALL LINEOUT LOGFILE, , " excluded (or not included): dirs= "nexcludes_dir ', files= 'nexcludes_file if nexcludes_dir>0 then CALL LINEOUT LOGFILE, , " (excluded file count does NOT include files in excluded directories)" end if dozip=0 then do CALL LINEOUT LOGFILE," bytes (allocated) = "||addcomma(nbytes0) ' ('|| , addcomma(nbytes)||')' if badcopies.0+badbytes>0 then CALL LINEOUT LOGFILE, , ' .... of which ' badcopies.0' files failed ('badbytes' bytes) ' if baddeletes.0>0 then call lineout logfile, , ' and 'baddeletes.0 ' source files were not removed' end end say " Copied "nfiles2" files from " stuff.0 " directories. " if verbose>-1 then say " bytes (allocated) = " ||addcomma(nbytes0) ' ('|| , addcomma(nbytes)||')' if verbose>0 then do if excludes.0>0 then do say " excluded (or not included): dirs= "nexcludes_dir ', files= '|| , nexcludes_file if nexcludes_dir>0 then say " (excluded file count does NOT include files in excluded directories)" end if dozip=0 then do if badcopies.0+badbytes>0 then say ' .... of which ' badcopies.0' files failed ('badbytes' bytes) ' if baddeletes.0>0 then say ' and 'baddeletes.0 ' source files were not removed' end end ii2=0 do ii=1 to badcopies.0 if verbose>1 then say badcopies.ii '( 'badcopies.ii.1 ii2=ii2+1 if badcopies.ii.1='*.*' then arf.ii2='COPY 'badcopies.ii||'\*.* 'badcopies.ii.2 else arf.ii2='COPY 'badcopies.ii ' 'badcopies.ii.2 end do ii=1 to trunc8s.0 if verbose>1 then say trunc8s.ii '(' trunc8s.ii.1 ii2=ii2+1 arf.ii2='REN 'trunc8s.ii.2 ' 'trunc8s.ii end do ii=1 to baddeletes.0 ii2=ii2+1 if verbose>1 then say baddeletes.ii arf.ii2='DEL 'baddeletes.ii end arf.0=ii2 call write_errfile /* write error file? */ if testmode=1 & dolog=1 then do call lineout logfile,' ' call lineout logfile,, ' TEST-MODE TEST-MODE TEST-MODE TEST-MODE TEST-MODE TEST-MODE TEST-MODE ' call lineout logfile,' Note that test-Mode ASSUMES success! ' call lineout logfile,, ' TEST-MODE TEST-MODE TEST-MODE TEST-MODE TEST-MODE TEST-MODE TEST-MODE ' call lineout logfile,' ' end IF DOLOG=1 then do CALL LINEOUT LOGFILE if view_log>0 then do call do_view_log end else do /* dopause=0 */ say " Log file written to: " say " "logfile end end exit /**************/ /* view the log file */ do_view_log: if view_log<2 then do if yesno(' View log file ',,'Y')=0 then return 0 end say goo=cy_ye"Displaying logfile: "normal||' '||bold||logfile||normal igoo=length(goo) if igoo< 75 then goo=goo||' '||cy_ye||copies(' ',85-igoo)||normal say goo if loglister='' then do aa=show_intro(2,logfile,'CopyDir Log ('logfile')') foo=stream(logfile,'c','close') ii=yesno('Delete log file? ',,'N') if ii=1 then do foo=sysfiledelete(logfile) if foo<>0 then say "Unable to delete logfile (error= "sysfiledeletes.foo else say "Logfile deleted: "logfile end else do address cmd loglister' 'logfile end /* do */ end /* dopause<>0 */ return 0 /****************/ /* test to see if enough space on target drive */ test_size: arf=sysdriveinfo(todrive) parse var arf . todrivefree . if verbose>0 then say ' 'todrive" has "||addcomma(todrivefree)||' bytes free. ', " Checking if this is sufficient ..." ssize1=0 ;ssize2=0 stuff3.0=0 if meticulous=1 then do ig=0 say " Checking files in "stuff.0" directories" do ig0=1 to stuff.0 ba=inkey('n') if c2d(ba)=27 then do say foo=yesno(' |Continue checking size ','Continue Stop_checking Exit_program') if foo=1 then return 1 if foo=2 then exit end /* esc */ ff=stuff.ig0||'\*.*' wow=sysfiletree(ff,'stuff2','F') if stuff2.0=0 then iterate do ig1=1 to stuff2.0 iat=stuff3.0+ig1 stuff3.iat=stuff2.ig1 end stuff3.0=stuff3.0+stuff2.0 foo=cursor(,1) call charout, " "stuff3.0 " files in " ig0 " directories" end end else do /* not meticulous */ wow=sysfiletree(getit,'stuff3','FS') end mm=0 say " ... checking size of "stuff3.0 " files " do ii=1 to stuff3.0 parse var stuff3.ii d1 d2 jsize d3 aname if exclude_me(aname,1)=1 then iterate ssize1=ssize1+dosfileinfo(strip(aname),'s') ssize2=ssize2+jsize if meticulous=1 | (meticulous=0 & ii//100=1) then do ba=inkey('n') if c2d(ba)=27 then do say foo=yesno(' |Continue checking? ','Continue Stop_checking Exit_program') if foo=1 then return 1 if foo=2 then exit end /* esc */ end /* metic */ if ii//100=1 then do xx=cursor(,1) call charout,' .... 'ii ' files with '||addcomma(ssize2)||' bytes' end end /* 1 to stuff3.0 */ /* just to avoid confusing info on screen */ xx=cursor(,1) call charout,' .... 'ii ' files with '||addcomma(ssize2)||' bytes' say say " == bytes to copy: "||addcomma(ssize2)||' ('||addcomma(ssize1)||')' if ssize1>todrivefree & stop_checking=0 then do say 'Warning: (ignoring overwrites), there may not be enough free space on:' say ' '||todrive if verbose<1 then exit ans=yesno(' Copy anyway?') if ans=0 then exit end return 0 /**************/ /* remove directories? */ do_rmdir: delds.=0 idd=0 do mm=1 to stuff.0 if stuff.mm.!del=1 then do /* candidate for removal */ idd=idd+1 delds.idd=right(length(stuff.mm),6)||' '||stuff.mm end end if idd=0 then return 0 /* no dirs to remove */ delds.0=idd foo=arraysort('delds.',1,,1,6,'D','N') do ii=1 to delds.0 parse var delds.ii . adir ; adir=strip(adir) agetit=strip(adir,,'\')'\*.*' oy=sysfiletree(agetit,'dds.') if dds.0>0 then iterate /* not empty, don't try to remove*/ oy=sysrmdir(adir) if oy=0 then do if dolog=1 then call lineout logfile,' Directory removed 'adir iterate end if verbose>0 then say 'Error ('sysfiledeletes.oy'): could not remove directory 'adir if dolog=0 then iterate call lineout logfile, , 'Error ('sysfiledeletes.oy'): could not remove directory 'adir end return 1 /************/ /* after a unsuccessful copy */ copied_notokay: say if verified=0 then do cstat='CRC' errname='Copy was not verified (mismatch CRC)' end else do errname=doscopys.cstat end if verbose>-1 then do if domove=0 then do aamess="%Error copying: "yee1 '(' cstat'='errname if length(aamess)<80 then do say aamess end else do say '%Error copying ('cstat'='errname')' say "% "yee1 end enn=errlist.0+1 errlist.enn=" Error copying: "yee1 '(' cstat'='errname errlist.0=enn llen=0 end else do say " Error moving: "yee1 enn=errlist.0+1 errlist.enn=" Error Moving: "yee1 '(' cstat'='errname errlist.0=enn end end if dolog=1 then do if domove=0 then call lineout logfile,'Error ('errname') copying 'stuff2.mmm' 'targfile else call lineout logfile,'Error ('errname') moving 'stuff2.mmm' 'targfile end llen=0 igoo=0 nk=badcopies.0+1 badcopies.nk=stuff2.mmm badcopies.nk.2=targfile badcopies.nk.1=cstat badcopies.0=nk badbytes=badbytes+fsize return 0 /************/ /* after a successful copy */ copied_okay: nbytes=nbytes+fsize /* attribute set? */ if testmode=0 then do if attribs.0>0 then do do iz=1 to 5 /* # 2 is always * (D attribute */ select when attribs.iz='+' then thisatt.iz='+' when attribs.iz='-' then thisatt.iz='-' otherwise do /* * -- set to + - depending on fattibs */ thisatt.iz='-' if substr(fattribs.mmm,iz,1)<>'-' then thisatt.iz='+' end end end usea='' do iz=1 to 5 /* make the new attribute string */ usea=usea||thisatt.iz end end else do usea='-*---' end foo=sysfiletree(targfile,'goo.','F',,usea) /* unset ALL attributes including H and S */ end if domove>0 then do /* now delete old version */ if truemove=1 then do fii=write_log('Move 'stuff2.mmm' 'targfile,'COPY') end else do /* not a true move, so delete source file */ oo=1 /* assume success in testmode */ if testmode=0 then oo=del_source(stuff2.mmm) if oo=1 then do if isshared=0 then fii=write_log('Copy/replace 'stuff2.mmm' 'targfile,'COPY') else fuu=write_log('Copy/replace "locked" 'stuff2.mmm' 'targfile,'COPY') end /* oo=1 */ else do fii=write_log('Copy, but unable to remove source: 'stuff2.mmm' 'targfile,'COPY') end /* oo=1 */ end /* truemove =1 */ end /* domove>0 */ else do /* regular copy */ if destexists='' then do if isshared=0 then fii=write_log('Copy 'stuff2.mmm' 'targfile,'COPY') else fii=write_log('Copy "locked" 'stuff2.mmm' 'targfile,'COPY') end else do if isshared=0 then fii=write_log('Replace 'stuff2.mmm' 'targfile,'COPY') else fii=write_log('Replace "locked" 'stuff2.mmm' 'targfile,'COPY') end /* remove "verification backup file? */ if doverify=1 & tf2<>'' then do foo=sysfiledelete(tf2) if foo=5 then do /* might be readonly... */ foo=sysfiletree(tf2,'hoo.','F',,'***-*') foo=sysfiledelete(tf2) end if foo<>0 then do uu=' .... unable to remove ('sysfiledeletes.foo')' , tf2 '(the backup of 'targfile if dolog=1 then call lineout logfile,uu enn=errlist.0+1 errlist.enn=" Error removing: "yee1 '(' cstat'='sysfiledeletes.foo errlist.0=enn end end end /* domove */ if verbose<0 then return 0 /* record some stuff? */ if targfile<>targfile0 then do yee1=yee1' as '||filespec('n',targfile)||',' oi=trunc8s.0+1 trunc8s.oi=stuff2.mmm trunc8s.oi.1=filespec('n',targfile) trunc8s.oi.2=targfile trunc8s.0=oi end if igoo=0 & verbose>0 & doquery=0 & meticulous=0 & one_per_line=0 then call charout,' : ' igoo=1 yee1=strip(yee1) if pos(' ',yee1)>0 then yee1='"'yee1'"' lyee1=length(yee1) lrem=lyee1//14 yee2=yee1 if lrem>0 then do yee2=yee1||copies(' ',14-lrem) end if (llen+length(yee2))>75 then do if llen<>80 & meticulous=0 & one_per_line=0 & doquery=0 & verbose>0 then say '' llen=0 if doquery=0 & meticulous=0 & one_per_line=0 & verbose>0 then call charout ,' : ' end if doquery=0 & meticulous=0 & one_per_line=0 & verbose>0 then call charout,yee2' ' if doquery=0 & meticulous=0 & one_per_line=1 & verbose>0 then say ' : 'targfile' ' llen=length(yee2)+llen+2 return 1 /********************/ /* routine to delete source file (after a move) */ del_source:procedure expose baddeletes. sysdeletes. verbose parse arg afile oo=sysfiledelete(afile) if oo=0 then return 1 if dolog=1 then call lineout logfile,' ... unable to remove ('sysdeletes.oo'): 'afile if verbose>-1 then say " ... error 'sysdeletes.oo' removing: "afile nk=baddeletes.0+1 baddeletes.nk=afile baddeletes.0=nk return 0 /****************/ /* query user about copying oldfile to newfile */ query_replace:procedure expose domove doquery igoo llen logevent dolog logfile parse arg oldfile,newfile say ' ' fname=filespec('n',oldfile) dirname=filespec('p',newfile) do forever /* repeat on Info */ if domove=1 then do a=yesno('....|Move 'fname 'to 'dirname,'No Yes Go Info Exit SkipDir','Y') if a=5 then return 5 if a=4 then do say " bye " if dolog=1 then do call lineout logfile,' ' call lineout logfile,'Exiting on user request. ' call lineout logfile end exit end if a=2 then do say "Remaining files will be copied without asking " a=1 doquery=0 igoo=0 ; llen=0 end if a=3 then do /* supply info, ask again */ tt=show_file_info(oldfile,newfile) iterate end if a=0 then return 'User suppressed move 'oldfile' 'newfile end else do a=yesno('....|Copy 'fname' to 'dirname,'No Yes Go Info Exit SkipDir','Y') if a=5 then return 5 if a=4 then do say " bye " if dolog=1 then do call lineout logfile,' ' call lineout logfile,'Exiting on user request. ' call lineout logfile end exit end if a=2 then do say "Remaining files will be copied without asking " a=1 doquery=0 igoo=0 ; llen=0 end if a=3 then do /* supply info, ask again */ tt=show_file_info(oldfile,newfile) iterate end if a=0 then return 'User suppressed copy 'oldfile' 'newfile end return 1 end /****************/ /* Show info on source and target file */ show_file_info:procedure parse arg oldfile,newfile aesc='1B'x cy_ye=aesc||'[37;46;m' cyanon=cy_ye normal=aesc||'[0;m' bold=aesc||'[1;m' re_wh=aesc||'[31;47;m' reverse=aesc||'[7;m' say ' ' a=sysfiletree(oldfile,'oo.','T') parse var oo.1 odate osize . o2=left(odate,2) if o2<>'19' & o2<>'20' then do if o2>80 then odate='19'||odate else odate='20'||odate end olen=length(' Source: '||oldfile||' 'odate', 'osize) if olen<77 then do say normal||' 'cyanon||'Source: 'normal||bold||oldfile||normal||' 'odate', 'osize end else do say normal||' 'cyanon||'Source: 'normal||bold||oldfile||normal say ' 'odate', 'osize end a=sysfiletree(newfile,'oo.','T') if oo.0=0 then do say normal||' 'cyanon||'No destination: 'normal||bold||newfile||normal return 1 end parse var oo.1 ndate nsize . o2=left(ndate,2) if o2<>'19' & o2<>'20' then do if o2>80 then ndate='19'||ndate else ndate='20'||ndate end olen=length(' Destination: '||newfile||' 'ndate', 'nsize) if olen<77 then do say normal||' 'cyanon||'Destination: 'normal||bold||newfile||normal|| , ' 'ndate', 'nsize end else do say normal||' 'cyanon||'Source: 'normal||bold||newfile||normal say ' 'ndate', 'nsize end return 1 /****************/ /* check replacement critieria. If replacement is okay, then return 1,1 (or ,1 if no newfile exists). Otherwise, return ',status message' */ check_replace:procedure expose rlist. replaceit nowtime okoverwrite file_seconds parse arg oldfile,newfile isdest=1 if file_seconds=1 then do osize=stream(oldfile,'c','query size') if osize='' then return ' , ERROR: missing source file 'oldfile tmp=dosfileinfo(oldfile,'W') parse var tmp mo'/'dd'/'yy hh ':' mm ':' ss odate=space(yy'_'mo'_'dd'_'hh'_'mm'_'ss,0) end else do a=sysfiletree(oldfile,'oo.','T') if oo.0=0 then return ' , ERROR: missing source file 'oldfile parse var oo.1 odate osize . odate=translate(odate,'_','/') end o2=left(odate,2) if o2<>'19' & o2<>'20' then do if o2>80 then odate='19'||odate else odate='20'||odate end if file_seconds=1 then do nsize=stream(newfile,'c','query size') if nsize='' then return ',1' /* no dest file */ tmp=dosfileinfo(NEWfile,'W') parse var tmp mo'/'dd'/'yy hh ':' mm ':' ss . ndate=space(yy'_'mo'_'dd'_'hh'_'mm'_'ss,0) a=sysfiletree(newfile,'oo.','T') parse upper var oo.1 . . nattribs . end else do if newfile='' then do say "ERROR with newfile:"newfile return ' , ERROR: bad newfile name 'newfile end a=sysfiletree(newfile,'oo.','T') if oo.0=0 then return ',1' /* no dest file */ parse upper var oo.1 ndate nsize nattribs . ndate=translate(ndate,'_','/') end o2=left(ndate,2) if o2<>'19' & o2<>'20' then do if o2>80 then ndate='19'||ndate else ndate='20'||ndate end hh=filespec('n',oldfile) isreadonly=0 if pos('R',nattribs)>0 then do if okoverwrite=0 then return ' ,Not replacing readonly destination file 'oldfile' 'newfile else isreadonly=1 end /* no -R --the default is to check for newer */ if replaceit=0 then do if ndate >= odate then do return ' ,Not copying over newer file 'oldfile' 'newfile end else do return isdest',1,'isreadonly /* not newer, no critieria -- ok to copy */ end end /* now check each of the -R criteria */ do ii=1 to rlist.0 select when rlist.ii='B' then do if \(osize>=nsize) then return ' , source NOT larger 'oldfile' 'newfile end when rlist.ii='S' then do if \(osizendate) then return ' , source NOT strictly newer 'oldfile' 'newfile end when rlist.ii='O' then do if \(odate<=ndate) then return ' , source NOT older 'oldfile' 'newfile end when rlist.ii='A' then do return isdest',1,'isreadonly /* always copy */ end when rlist.ii='Z' then do return isdest',Not replaced 'oldfile' 'newfile /* never copy */ end when rlist.ii='0' then do if nsize=0 then return isdest',1,'isreadonly /* overwrite 0length files*/ end when rlist.ii='F' then do /* overwrite files "from the future */ if ndate>nowtime then return isdest',1,'isreadonly end when rlist.ii='C' then do ocrc=filecrc(oldfile) ncrc=filecrc(newfile) if ocrc=ncrc then return ',Same CRC, not copying 'oldfile' 'newfile end otherwise nop end end return isdest',1,'isreadonly /********/ /* display message, wait do pause seconds. Return keystroke using position in okays list. If not in okays list, ignore keystroke, If timeout, return 0. If okays list not specified, okay=' '||esc Default is: 1 on space bar, 2 on esc, 0 on timeout */ wait_sec:procedure parse arg dopause,message,okays,freq if freq='' then freq=0.2 aesc='1B'x normal=aesc||'[0;m' bold=aesc||'[1;m' if okays='' then okays=' '||'1b0d0a'x cr='0d'x if datatype(dopause)<>'NUM' then dopause=5 if dopause=0 then do dopause=11111122 if message='' then message='Hit Esc to cancel, SPACE to continue...' end else do if message='' then message='Hit Esc (within 'dopause' seconds) to cancel, SPACE to continue...' end call charout, bold||message||normal do mm=1 to trunc((1/freq)*dopause) ikey=inkey('N') if length(ikey)>1 then ikey=right(ikey,1) inn=pos(ikey,okays) if inn>0 then do call charout, cr||copies(' ',78)||cr return inn end call syssleep(freq) end call charout, cr||copies(' ',78)||cr return 0 /**********/ /* write an error file (if errors occured */ write_errfile: if arf.0=0 | verbose<0 then return 0 goog=errfile ii=arf.0 ii=ii+1 arf.ii='Rem COPY entries indicate files, or dirs, that could not be copied ' ii=ii+1 arf.ii='Rem REN entries indicate files with shortened file names ' ii=ii+1 arf.ii='Rem DEL entries indicate files that could not be removed ' ii2=ii do enn=1 to errlist.0 ii2=ii+enn arf.ii2='REM : 'errlist.enn end arf.0=ii2 aa=sysfiledelete(goog) do mm=1 to arf.0 call lineout goog,arf.mm end call lineout goog say ss=" Writing failures, and recommended renames, to " goog if length(ss)<76 then do say ss end /* do */ else do say " Writing failures, and recommended renames, to: " say " " goog end /* do */ if dolog=1 then do call lineout logfile,' ' call lineout logfile," Writing failures, and recommended renames, to " goog end if loglister<>'0' then do if dopause<>0 then do if yesno(' View error file ',,'Y')=1 then do say cy_ye"Displaying error file: "normal||bold||goog||normal if loglister='' then do aa=show_intro(2,goog,'CopyDir Error file ('goog')') foo=stream(goog,'c','close') ii=yesno('Delete error file? ',,'N') if ii=1 then do foo=sysfiledelete(goog) if foo<>0 then say "Unable to delete error file (error= "sysfiledeletes.foo else say "Error file deleted: "goog end end else do address cmd loglister' 'goog end end end end return 1 /*************/ show_intro:procedure expose normal parse arg noww,afile,title parse value scrsize() with nrows ncols if afile='' then parse source goo goo2 afile aesc='1B'x cy_ye=aesc||'[37;46;m' cyanon=cy_ye normal=aesc||'[0;m' bold=aesc||'[1;m' re_wh=aesc||'[31;47;m' reverse=aesc||'[7;m' call syscls2 ii=0 lins.0=0 do until lines(afile)=0 foo=linein(afile) if abbrev(strip(foo),'.end ') then leave ii=ii+1 lins.ii=foo end lins.0=ii i1=1 lside=1 if noww=3 then do /* ??? */ do ii=1 to lins.0 select when left(lins.ii,1)=':' then do lsidex=max(2,lside) hoo=bold||strip(substr(lins.ii,lsidex,75))||normal say hoo end when left(lins.ii,1)='+' then do lsidex=max(2,lside) hoo=reverse||strip(substr(lins.ii,lsidex,75))||normal say hoo end otherwise do say substr(lins.ii,lside,78) end end iat=ii end exit end n19=nrows-4 do forever do ii=i1 to min(i1+n19,lins.0) select when left(lins.ii,1)=':' then do lsidex=max(2,lside) hoo=bold||strip(substr(lins.ii,lsidex,75))||normal say hoo end when left(lins.ii,1)='+' then do lsidex=max(2,lside) hoo=reverse||strip(substr(lins.ii,lsidex,75))||normal say hoo end otherwise do say substr(lins.ii,lside,78) end end iat=ii end ll=iat if iat=lins.0 then ll=ll' (eof)' ll=ll||','||lside ms1=ll':ESC,Space,PgUp,PgDn, Arrows, Home, End ' ik=wait_sec(0,ms1,'201b'x||'IQGHPOKM'||'0d0a'x, 0.01) /*Puup PgDN Home UPArow Dwnaro End left right cr lf */ if ik=5 then do /* home */ call syscls2 i1=1 ; lside=1 end if ik=8 then do /* end */ i1=max(1,lins.0-n19) ;lside=1 call syscls2 end if ik=9 then do /* left arrow */ lside=max(1,lside-1) call syscls2 end /* do */ if ik=10 then do /* right arrow */ lside=min(lside+1,1500) call syscls2 end if ik=7 then do /* down arrow */ if i1+1>lins.0 then do ik=1 end else do call syscls2 i1=i1+1 end end if ik=6 then do /* up arrow */ i1=max(1,i1-1) call syscls2 end if ik=4 then do /* pgdn */ i2=iat+nrows-2 if i2>lins.0 then do ik=1 end else do call syscls2 i1=i2 end end if ik=1 | ik>10 then do if iat0 then /* watch out for x:\ vs x: */ nop else fromdir=strip(strip(fromdir,'t','\')) sspos=pos(':',fromdir) if sspos=0 then fromdir=nowdrive||fromdir /* no : in dir, so add X: */ sspos=pos(':',fromdir) if substr(fromdir,sspos+1,1)<>'\' then do /* x:d1\d2 */ foo=directory() yy=left(fromdir,sspos) woo=directory(yy) goo=directory(foo) parse var fromdir a1 ':' a2 fromdir=strip(woo,,'\')||'\'||strip(a2,,'\') end /* do */ fromdir=strip(fromdir,'t','\') if right(fromdir,1)=':' then fromdir=fromdir'\' select when right(fromdir,3)='*.*' then fromdir=left(fromdir,length(fromdir)-3) when right(fromdir,1)='*' then fromdir=left(fromdir,length(fromdir)-1) when right(fromdir,1)='.' then fromdir=left(fromdir,length(fromdir)-1) otherwise nop end fromdir=strip(fromdir,'t','\') if right(fromdir,1)=':' then fromdir=fromdir'\' /* check intounder stuff ... */ if fromd='' then return fromdir /* fromdir call, no need to check */ if intounder=1 then return fromdir /* copy "into" dest */ if dir_exists(strip(fromdir,,'\'))=0 then do /* directory does not exist; always create! */ return fromdir end if length(fromd)=3 & right(fromd,2)=':\' then do /* copying from root */ return fromdir /* maybe later will have options */ end /* exists, not from root. What is the "source" top level directory */ ii=lastpos('\',fromd) topd=substr(fromd,ii+1) if intounder=2 then do fromdir=strip(fromdir,'t','\')||'\'||topd return fromdir end do forever ww=yesno(' Copy to, or under, destination ?','To Under ?','U') user_tou=ww say if ww=0 then return fromdir if ww=1 then do fromdir=strip(fromdir,'t','\')||'\'||topd return fromdir end say " When copying to a pre-existing destination directory, there are 2 options: " say " "bold"TO"normal||, " ) Copy the source files (and source subdirectories) "bold"TO"normal||, " the destination." say " "bold"UNDER"normal||, " ) Copy the source directory "bold"UNDER"normal" the destination." say " For example, suppose that the source is E:\MYDIR, and the (pre-existing)" say " destination is E:\SOMETHING. Then ... " say " * the "bold"TO"normal, " Method would put all the FILES currently in \MYDIR directly" say " into \SOMETHING, and also put all the SUBDIRECTORIES under \MYDIR under" say " \SOMETHING. This is a "merge" of \MYDIR with \SOMETHING." say say " * The "bold"UNDER"normal, " method would keep the copy of \MYDIR totally intact and make" say " it a subdirectory of \SOMETHING. So you'ld end up with", "\SOMETHING\MYDIR" say " where everything from \MYDIR on out would be just like its original." end /* ------------------------------------- */ sysmkdir2:procedure expose logevent dolog logfile sysmkdirs. parse arg adir adir=strip(adir,'t','\') if right(adir,1)=':' then adir=adir'\' if dosisdir(adir)=1 then return 0 ff=sysmkdir(adir) if ff=0 then return ff /* make the tree */ f2=adir'\' dd=filespec('d',f2) pp=filespec('p',f2) if pp='\' | pp='' then return -1 pp2=strip(translate(pp,' ','\')) do mm=1 to words(pp2) a1=subword(pp2,1,mm) a1=translate(a1,'\',' ') dd2=dd'\'a1 if dosisdir(dd2)=1 then iterate hoo=sysmkdir(dd2) if hoo=0 then do say ' ... creating: 'dd2 if dolog=1 then call lineout logfile,' ... creating: 'dd2 end else do say ' Problem creating: 'dd2',' adir say ' >> 'sysmkdirs.hoo if dolog=1 then do call lineout logfile,' Problem creating: 'dd2 call lineout logfile,' >> 'sysmkdirs.hoo end end end return hoo /************/ /* ADD COMMAS TO A NUMBER */ addcomma:procedure parse arg aval,ndec parse var aval p1 '.' p2 if ndec='' then do p2='' end else do p2='.'||left(p2,ndec,'0') end /* do */ plen=length(p1) p1new='' do i=1 to 10000 while plen>3 p1new=','right(p1,3)||p1new p1=delstr(p1,plen-2) plen=plen-3 end /* do */ return p1||p1new||p2 /**********/ /* convert hpfs long file names to some kind of fat name */ fix8:procedure parse arg aname t1=filespec('d',aname) t2=filespec('p',aname) t3=filespec('n',aname) parse var t3 t3a '.' t3b select when pos(' ',t3)>0 then not8=1 when pos('.',t3b)>0 then not8=1 when length(t3a)>8 then not8=1 when length(t3b)>3 then not8=1 otherwise not8=0 end if not8=0 then return aname /* else, it's a non-fat allowable name --- create a new name */ /* first, replace ' ' with _ */ t3a=translate(t3a,'_',' ') t3b=translate(t3b,'__','. ') if length(t3a)>8 | length(t3b)>3 then do t3a=left(t3a,min(length(t3a),5))'???' end /* do */ if length(t3b)>3 then t3b=left(t3b,3) t3=t3a if t3b<>'' then t3=t3a'.'t3b newname=dostempname(t1||t2||t3) return newname /**********/ /* check for 8 character max in directory names */ checkdir8:procedure parse arg aa aa0=aa if (pos(' ',aa)+pos('.',aa))>0 then do say "Caution:ERROR: converting . or space to _ in directory: " aa0 aa=translate(aa,'__',' .') end /* do */ aa=translate(aa,' ',':\') do mm=1 to words(aa) if length(word(aa,mm))>8 then do say "ERROR: Unable to create longname directory on FAT drive: " aa0 return 0 end /* do */ end return 1 /**********/ /* see if filename contains an "exclusion" string */ exclude_me:procedure expose excludes. includes. if includes.0+excludes.0=0 then return 0 parse upper arg aname,chkfile igot=1 /* check includes. first */ if chkfile=1 & includes.0>0 then do igot=0 do mm=1 to includes.0 if pos(includes.mm,aname)>0 then do igot=1 leave end end end /*don't check dir for -I */ if igot=0 then return 1 do mm=1 to excludes.0 if pos(excludes.mm,aname)>0 then return 1 end return 0 /**********/ /* prepend contents of PARAMFILE to astuff2 (astuff2 is list of unprocessed options */ sub_params: pfilename=anarg if pfilename='' then do say "ERROR: you forgot to specify a parameter file with a -PARAMFILE option" exit end pfile=stream(pfilename,'c','query size') if pfipe='' | pfile=0 then do say "ERROR: no such parameter file: "pfilename exit end foo=stream(pfilename,'c','open read') if abbrev(translate(foo),'READY')=0 then do say "ERROR: unable to open parameter file: "pfilename exit end bstuff=charin(pfilename,1,pfile) foo=stream(pfilename,'c','close') cstuff='' do until bstuff='' parse var bstuff bline '0d0a'x bstuff bline=strip(bline) if abbrev(bline,';')=1 then iterate if bline='' then iterate cstuff=cstuff||' '||translate(bline) end astuff2=cstuff||' '||astuff2 return 1 /**********/ getopts: logevent='ALL' replaceit=0 ; rlist.=0 domove=0 ; del_domove=0 doquery=0 doverify=0 filesonly=0 domerge=0 testmode=0 okoverwrite=0 doshare=0 attribs.0=0 trunc8=0 dozip=0 dolog=0 ; logappend=0 logisnew=0 excludes.='' ; nex=0 includes.='' ; nin=0 includes.0=0 ; excludes.0=0 astuff2_use='' do until astuff2='' parse var astuff2 . '-' astuff2 if astuff2='' then leave /* some kind of goof*/ goo2=pos('=',astuff2) goo1=pos(' ',astuff2) gotequal=0 anarg='' select when goo1>0 & goo2=0 then do iat=goo1 end when goo1=0 & goo2>0 then do iat=goo2 gotequal=1 end when goo1>0 & goo2>0 then do iat=min(goo1,goo2) if goo20 then do call sub_params /* prepend contents of paramfile to astuff2 */ return 1 /*do NOT augment astuff2_use */ end when abbrev(anopt,'ALL')>0 then do astuff2='-RAR -SHARE -ATTRIBS=**** -CHECK '||translate(astuff2) return 1 /*do NOT augment astuff2_use */ end /* the remaining are options */ when abbrev(anopt,'QQ')>0 then verbose=-1 when abbrev(anopt,'Q')>0 then verbose=0 when abbrev(anopt,'VIEW_LOG')>0 then do if datatype(anarg)<>'NUM' then anarg=0 view_log=anarg end when abbrev(anopt,'V')>0 then doverify=1 when abbrev(anopt,'MOVE')>0 then domove=1 when abbrev(anopt,'FILESONLY')>0 then filesonly=1 when abbrev(anopt,'MERGE')>0 then domerge=1 when abbrev(anopt,'NOMERGE')>0 then domerge=-1 when abbrev(anopt,'TEST')>0 then testmode=1 when abbrev(anopt,'SHARE')>0 then do doshare=1 if got_Filerexx=0 then do say "Error: FILEREXX.DLL is not available; it is need for copying shared files" exit end end when abbrev(anopt,'R')>0 then do replaceit=1 if rdefault='' then rdefault='AQ' alist=strip(substr(anopt,2)) if alist='' then alist=translate(rdefault) rlist.0=length(alist) do jmm=1 to rlist.0 rlist.jmm=substr(alist,jmm,1) if pos(rlist.jmm,'ABCDFONQRS0Z')=0 then say "WARNING: unknown -R option:" rlist.jmm end if pos('Q',alist)>0 then doquery=1 if pos('D',alist)>0 then del_domove=1 if pos('R',alist)>0 then okoverwrite=1 anarg0=lower(alist) ANOPT='R'||ANARG0 end when abbrev(anopt,'ATTRIB')>0 then do alist=anarg if length(alist)<>4 & length(alist)<>1 then do say "ERROR: bad attribute string (1, or 4, +-* chars expected): "alist exit end if verify(alist,'+-*')>0 then do say "ERROR: bad flag in attribute string (only +-* allowed): "alist exit end attribs.0=5 attribs.2='*' /* D attribute never changed */ if length(alist)=1 then alist=copies(alist,4) do li=1 to 4 li2=li if li>1 then li2=li+1 /* 2nd spot is always -D */ attribs.li2=substr(alist,li,1) end end when abbrev(anopt,'8')>0 then trunc8=1 when abbrev(anopt,'CHECK')>0 then testsize=1 when abbrev(anopt,'NOCHECK')>0 then testsize=0 when abbrev(anopt,'NOPAUSE')>0 then do if strip(anarg)<>'0' then do dopause=0 ; dopause_severe=0 if strip(anarg)='2' then dopause_severe=1 end end when abbrev(anopt,'LOGEVENT')>0 then do etype=anarg if wordpos(etype,'ALL COPY EXCLUDE EXCLUDE_COPY COPY_EXCLUDE')=0 then do say "ERROR. No such LogEvent type: " anarg exit end logevent=anarg end when abbrev(anopt,'ZIP')>0 then do dozip=1 zipfile=anarg if zipfile=' ' then do zipfile=date('n') parse var zipfile a1 a2 a3 zipfile=a1||a2||right(a3,2) end /* check for ZIP.exe */ foo=doscommandfind('ZIP') if foo='' then do say "Sorry, can not find ZIP program in your OS/2 PATH. " exit end end when abbrev(anopt,'LOG')>0 then do logisnew=0 if abbrev(anopt,'LOGNEW')=1 then logisnew=1 logfile=anarg if logfile=' ' then logfile=logfile0 logfile=dosfname(logfile) if logisnew=1 & stream(logfile,'c','query exists')<>'' then do fooo=sysfiledelete(logfile) if fooo<>0 then do say "ERROR: Unable to delete ("sysfiledeletes.fooo") logfile "logfile exit end end foo=stream(logfile,'c','query exists') if foo<>'' then do /* get to end of logfile */ do until lines(logfile)=0 goo=linein(logfile) end call lineout logfile,' ' call lineout logfile,' '||copies('=',78) call lineout logfile,' ' logappend=1 end else do /* write header of log file */ arf=stream(logfile,'c','open write') if abbrev(strip(translate(arf)),'READY')<>1 then do say "ERROR: can not create logfile="logfile exit end end dolog=1 call lineout logfile,"COPYDIR 1.16d log file created "||date('n')||' '||time('n') call lineout logfile,' ' if testmode=1 then do call lineout logfile,' ' call lineout logfile,, ' TEST-MODE TEST-MODE TEST-MODE TEST-MODE TEST-MODE TEST-MODE TEST-MODE ' call lineout logfile,' ' end end when abbrev(anopt,'METIC')>0 then do if Wordpos(anarg,'0 NO N')>0 then meticulous=0 else meticulous=1 end /* do */ when abbrev(anopt,'ERR')>0 then do ERRFILE1=ANARG if errfile1=' ' then errfile1=errfile errfile1=dosfname(errfile1) if stream(errfile1,'c','query exists')<>'' then do fooo=sysfiledelete(errfile1) if fooo<>0 then do say "ERROR: Unable to delete ("sysfiledeletes.fooo") error file "errfile1 exit end end errfile=errfile1 ENd when anOPT='X' then do nex=nex+1 excludes.nex=anarg excludes.0=nex end when anOPT='I' then do nin=nin+1 includes.nin=anarg includes.0=nin end /* unknown parameter is not a fatal error */ otherwise do say bold "Warning. No such COPYDIR option: "normal||anopt return end end /* this is what we tell the user the options that were used */ astuff2_use=astuff2_use' '||strip(anopt) if anarg<>'' then do if words(anarg)>1 then astuff2_use=astuff2_use||'="'||anarg0||'"' else astuff2_use=astuff2_use||'='||anarg0 end return 0 /***************/ showhelp: parse arg iii say say " Available options include:" say ' -?? = more detailed description : -??? with no pauses' say " -8 = copy to FAT style (8.3) file names " say " -ALL = aggressive replace (-Rar -ATTRIBS=**** -SHARE -CHECK)" say " -ATTRIBS=xxxx = set ARHS bits of destination files & dirs" say " -CHECK = check dest. drive for free space : -NOCHECK suppresses" say " -FILESONLY = do NOT copy subdirectories (just copy the files) " say " -I=string = only include files & directories that contain string " say " -LOG=filename = log ALL actions to filename ", ': -LOGNEW overwrite log file' say ' -MOVE = move files (delete source file after successful copy)' say ' -MERGE = copy files "into" destination : -NOMERGE copy under ' say ' -METIC=n/y = read file lists & copy files meticulously ' say " -NOPAUSE=0/1/2 = suppress cancellation & confirmation queries " say " -Q = quiet mode : -QQ = even quieter mode " say ' -R =' , 'always replace (otherwise, newer files are not overwritten) ' say '-R[abcdfonqrsz] = specify replacement criteria' say ' -SHARE = copy even if source is shared (used by another process)' say ' -TEST = Test mode -- copying and deleting will not occur' say " -V = verify all copies (not used with -ZIP)" say " -X=string = exclude files & directories that contain string " say " -PARAMFILE=filename = file containing COPYDIR.CMD options " say " -ZIP=filename = store files in zip files on dest. drive." say ' ' foo=wait_sec(0) if foo=2 & iii<>1 then exit if foo=2 then return 0 say 'Notes: ' say ' * CopyDIR copies all the files, and all the subdirectories, in and' say ' under the source directory. This includes files directly in the ' say ' source directory.' say ' * use double quotes (") to include spaces, &, -, and other wierd ' say ' characters in directory names, file names, and -X and -I strings' say ' * to specify several exclusions, enter multiple instances of -X=string' say ' * to specify several inclusions, enter multiple instances of -I=string' say ' * the -ZIP option requires that ZIP.EXE be in OS/2''s path' say ' * if -R is not specified, newer files are never replaced.' say ' * the abcdonqs0z -R criteria are: ' say ' a(always), b(igger), c(heck CRC), d(elete), f(uture), o(lder), ' say ' n(ewer), q(uery), r(eadonly), s(maller), 0(zero length), z(never) ' say ' ' return 0 /*************************************************/ /* write to logout file, but check for dolog and logevent */ write_log:procedure expose dolog logevent logfile parse arg avalue,log_requires if dolog=0 then return 0 log_requires=strip(translate(log_requires)) okay=0 if logevent<>'ALL' then do do mm=1 to words(log_requires) a1=strip(word(log_requires,mm)) if pos(a1,logevent)>0 then do okay=1 leave end end end else do okay=1 end if okay=1 then call lineout logfile,avalue return okay /*************************************************/ /* Check for the existence of a directory. Correctly identifies empty directories. Usage: flag=dir_exists(a_directory) where flag=1 if a_directory exists (it might be an empty directory ) flag=0 if it doesn't exist */ dir_exists:procedure parse arg adir adir=strip(adir) adir=strip(adir,'t','\') nowdir=directory() nowdrive=filespec('d',nowdir'\') nowpath=filespec('p',nowdir'\') adr=filespec('d',adir) if adr='' then do if abbrev(adir,'\')=0 then adir=nowdrive||nowpath||adir else adir=nowdrive||adir end /* do */ foo=sysfiletree(adir,goo,'D') if goo.0>0 then return 1 return 0 /* -------------------- */ /* choose between 3 alternatives (by default,a yes or no ), return 1 if yes (or 0,1,2 for chosen altenative ) */ yesno:procedure parse arg amessage , altans,def,arrowok ahdr='' if pos('|',amessage)>0 then parse var amessage ahdr '|' amessage aesc='1B'x cy_ye=aesc||'[37;46;m' cyanon=cy_ye normal=aesc||'[0;m' bold=aesc||'[1;m' re_wh=aesc||'[31;47;m' reverse=aesc||'[7;m' aynn=' ' if def='' then defans='' else defans=translate(left(strip(def),1)) if altans='' then altans='No Yes' w.0=words(altans) do iw0=1 to w.0 w.iw0=strip(word(altans,iw0)) a.iw0=translate(left(w.iw0,1)) aa.iw0=substr(w.iw0,2) aynn=aynn||bold if a.iw0=defans then aynn=aynn||cy_ye aynn=aynn||a.iw0||normal||aa.iw0 if iw032 | sharing<>1 then return cstat /* else, try copying even though source may be "shared */ ah1=cpy_fileopen(f1,'crs++','e') if ah1=0 then return 32 /*'Error on FileOpen of: 'f1 */ boo=cpy_filegetinfo(ah1,'ss') /* read into memory */ oo=cpy_fileread(ah1,ss.0) if oo='' then return 32 /* 'Unable to read using FileRead: 'f1 */ aa=cpy_fileclose(ah1) if aa<>0 then return 32 /* "error closing file: "aa */ /* write to new file */ if stream(f2,'c','query exists')<>'' then do a=sysfiledelete(f2) if a<>0 then return 32 end /* not required here if a=5 then do foo=sysfiletree(f2,'goo.','F',,'----') a=sysfiledelete(f2) if a<>0 then return 'error 'a 'deleting: 'f2 end *****/ yow=stream(f2,'c','open write') if abbrev(translate(strip(yow)),'READY')=0 then do return 32 /* " error "yow" opening "f2 */ end else do foo=charout(f2,oo,1) if foo<>0 then return 32 /* say " error "foo" writing: "f2 */ foo=stream(f2,'c','close') end return 'SHARED' /*************/ /* meticulous sysfiletree */ metic_sysfiletree: procedure expose stuff. bold normal logfile reverse parse arg getit stuff.0=0 wow=sysfiletree(getit,'stuff.','DO') say bold"Found "stuff.0" directories "normal"( in "||filespec('d',getit)||filespec('p',getit) ')' say ' 'reverse"(hit ESC to cancel)"normal mm=0 do forever ba=inkey('n') if c2d(ba)=27 then do say foo=stream(f1use,'c','close') foo=stream(f2,'c','close') say bold" Cancel "normal if dolog=1 then do call lineout logfile,'Cancelled by user.' call lineout logfile end exit end mm=mm+1 if mm=stuff.0 then leave getit1=strip(stuff.mm,,'\')||'\*.*' wow=sysfiletree(getit1,'stuff0.','DO') if stuff0.0=0 then iterate ij=0 do jj=stuff.0+1 to stuff.0+stuff0.0 ij=ij+1 stuff.jj=stuff0.ij end stuff.0=stuff.0+stuff0.0 foo=cursor(,1) call charout,left(" Total= "stuff.0 "("stuff0.0" directories in "||filespec('d',getit1)||filespec('p',getit1)||')',79) end say return 1 /*************/ /* meticulous copy using charin charout */ metic_copy:procedure expose logevent dolog reverse normal parse arg f1,f2 aesc='1B'x normal=aesc||'[0;m' bold=aesc||'[1;m' reverse=aesc||'[7;m' f1use=stream(f1,'c','query exists') /* no such source */ if f1use='' then return 2 a1=stream(f1use,'c','open read') f1size=stream(f1use,'c','query size') if f1size='' then return 2 arf=dosfname(f2) darf=filespec('d',arf)||filespec('p',arf) if length(darf)>3 then darf=strip(darf,,'\') if dosisdir(darf)=0 then return 3 f2use=stream(f2,'c','query exists') /* delete prior */ if f2use<>'' then do goo=sysfiledelete(f2use) if goo>0 then return goo /* typically, locked (32) or readonly (5) */ end a=stream(f1use,'c','close') a1=stream(f1use,'c','open read') if abbrev(translate(a1),'READY')<>1 then return 1 /* other error */ a2=stream(f2,'c','open write') if abbrev(translate(a2),'READY')<>1 then return 1 /* other error */ iread=0 tt=strip(right(f1use,57,' ')) call charout, ': '||left(tt,57,' ')||': copied ' do until iread=f1size ba=inkey('n') if c2d(ba)=27 then do say foo=stream(f1use,'c','close') foo=stream(f2,'c','close') say bold" Cancelling ("iread " bytes copied) "normal foo=yesno(" |Delete this partially copied file? ",,'Y') if foo=1 then foo2=sysfiledelete(f2) foo=yesno(' |Continue copying, or Quit ','Continue Quit ','Q') if foo=0 then return 'USER_CANCEL' if dolog=1 then do call lineout logfile,'Cancelled by user.' call lineout logfile end exit end toread=10000 if iread+10000> f1size then toread=f1size-iread bonk=charin(f1use,iread+1,toread) oogle=charout(f2,bonk) if oogle>0 then return 6 foo=cursor(,69) iread=iread+toread call charout,iread end say foo=stream(f1use,'c','close') foo=stream(f2,'c','close') return 0 say charout /*************/ /* doscopy, with sharing copy if needed */ shared_filecrc:procedure parse arg f1,isshared if isshared=0 then do cc1=filecrc(f1) return cc1 end /* shared file; readin string and use stringcrc */ ah1=cpy_fileopen(f1,'crs++','e') if ah1=0 then return '' /* couldn not read crc */ boo=cpy_filegetinfo(ah1,'ss') /* read into memory */ oo=cpy_fileread(ah1,ss.0) if oo='' then return '' aa=cpy_fileclose(ah1) if aa<>0 then return '' cc=stringcrc(oo) return c2x(cc)