jms@tardis.Tymnet.COM (Joe Smith) (06/26/91)
In article <1991Jun18.145334.12569@maytag.waterloo.edu> giguere@csg.UWaterloo.CA writes: >I don't have enough ARexx material! So please, mail me your useful scripts, >point me to useful programs (got RexxArpLib already, obviously), etc. I had been using MRBackup for some time to backup my Amiga's hard disk on to floppies. When it came time to recycle the first set of floppies, I found that some important files existed on these old floppies and nowhere else. That is, my "backup" floppies were being used for archiving. At that point, I decided I needed to locate all the files that existed only on the backup floppies, and copy them over to a special set of "archive" floppies. To do this, I wrote an ARexx script to compare every file on a floppy to the files in a corresponding directory on the hard disk, and tell me whether the files was identical or not. After deleting the identical files from the floppy, everything else was either different from the corresponding file on the hard disk, or no longer existed on the hard disk. I would look at these remaining files individually, and either trash them or archive them. I tried doing this with the standard C:LIST program, and with the LS program, but neither one would output a full relative pathname. They would either output just the file name only (without the name of the directory it was in), or the absolute path name (including volume name). Getting a suitable recursive directory listing was my first major ARexx program. For a while I had a problem with directories with embedded blanks in their names (such as most of the DMCS Music). Then I got a hint from the Net about invoking showdir() with three arguments. The "'00'x" says to separate the names with the NULL byte, instead of blanks. To clean up my floppies, I now use: rx >ram:temp CheckBack.rexx DF0: WORK: delete force emacs ram:temp ; Check for special cases execute ram:temp Here is my CheckBack.rexx program: ----------- cut here ---------- /* Check to see if files on floppy are duplicates of what's on disk */ parse arg floppydir diskdir cmd if pos(":",diskdir) = 0 then do parse source . . myname . say "Usage: rx >RAM:temp" myname "DF0: WORK: delete force" say " This will compare each file on the backup floppy in DF0: to" say " the corresponding file on WORK: and put 'delete force <file>'" say " in RAM:temp for each duplicate file. You can then" say " 'execute RAM:temp' to free up space on the floppy." say "Usage: rx" myname "DIR1 DIR2" say " will list only the files that are different." say " The second directory name must contain a colon." exit; end if right(diskdir,1) ~= ":" then diskdir = diskdir"/"; call addlib('rexxsupport.library',0,-30,0) /* in case not already loaded */ olddir=pragma('D',floppydir) allzeros=copies('00000000'x,128) /* 512 bytes of all nulls */ say 'CD' floppydir " ; compare to" diskdir "as of" date('N') call scandir /* start recursive traverse of new current directory */ say 'CD "'olddir'"' /* (watch out for blanks in volume name) */ exit /* SCANDIR - for each file in the specified directory, compare it with */ /* the corresponding file on the hard disk. If the files are the */ /* same and a command was supplied, output the command and the */ /* file name. Else output a comment describing what is different. */ /* A typical command is "DELETE FORCE" to remove redundant files. */ scandir: procedure expose diskdir cmd allzeros parse arg dir if dir ~= "" then say "; DIRECTORY:" dir files = showdir(dir,'all','00'x) /* get list of files in this dir */ found = words(files) do while words(files) > 0 delimiter = pos('00'x,files) /* locate the NULL */ if delimiter = 0 then delimiter = length(files)+1 /* if only one name */ file = substr(files,1,delimiter-1) /* first name */ files = substr(files,delimiter+1) /* all but 1st name */ if dir = "" then source = file else source = dir"/"file /* The files Fonts:*.font can be re-created by running FixFonts, therefore it's OK for them to have different sizes. The ".info" file is rewritten everytime Workbench looks in a directory; it too can be different. */ last5 = upper(right(source,5)) sizeok = ((upper(left(source,6)) == "FONTS/" & last5 == ".FONT") , | (upper(source) == ".INFO")) /* It's OK if an icon has been snapshot'd with a different position or window size, as long as the size of the *.info file is the same. */ diffok = (last5 == ".INFO" | sizeok) st = comparefile(source,diskdir||source) /* check date, size, etc */ select when word(st,1) = "SAME:" & cmd = "" then nop /* be quiet if no cmd */ when word(st,1) = "SAME:" & cmd ~= "" then say cmd '"'source'"' when word(st,1) = "SIZE:" & cmd ~= "" & sizeok then say cmd '"'source'"' when word(st,1) = "DIFF:" & cmd ~= "" & diffok then say cmd '"'source'"' otherwise say ";" st /* file is different */ end end if found = 0 & cmd ~= "" then say cmd '"'dir'" ; directory is empty' return /* COMPAREFILE - compare the old file on the backup floppy with the current */ /* file on the hard disk. It calls scandir to compare directories. */ comparefile: procedure expose diskdir cmd allzeros parse arg name1,name2 n1s = statef(name1); n2s = statef(name2) /* statef returns {DIR|FILE} length blocks protect days mins ticks note... */ if word(n1s,1) = "DIR" then do call scandir name1 /* Do files in subdirectory. */ if word(n2s,1) = "" then return 'DIRGONE:' name1 n1s else return 'DIREND:' name1 end if word(n1s,1) = "" then /* File is not in directory; name is wrong. */ return 'OOPS:' name1 if word(n2s,1) = "" then return 'GONE:' name1 ';' word(n1s,2) dat(n1s) subword(n1s,8) if word(n1s,2) ~= word(n2s,2) then return 'SIZE:' name1 ';' word(n1s,2) word(n2s,2) ';' dat(n1s) dat(n2s) /* Differences in the following words are explicitly ignored: */ /* Word 3: The block count on OFS floppy is different than an FFS partition*/ /* Word 4: The archive bit is often different; ignore protection difference*/ /* Words 5-7: Differences in date/time don't really matter. */ /* Words 8-end: No big deal if filenotes don't match. */ /* Both files exist and have the same size. Check if data is identical. */ if ~(open(f1,name1,'Read') & open(f2,name2,'Read')) then return 'OPEN:' name1 name2 /* Happens if file has an exclusive lock */ nb = 0; db = 0; zb = 0; do until (eof(f1) | eof(f2)) block1 = readch(f1,512); block2 = readch(f2,512) if length(block1) > 0 then nb = nb + 1 /* number of blocks */ if compare(block1,block2) > 0 then db = db + 1 /* different blocks */ if compare(block1,allzeros) = 0 then zb = zb + 1 /* all-zero blocks */ end close(f1); close(f2) if zb = nb then /* if all blocks were zero (bug in old MRBackup) */ return 'ZERO:' name1 '; B='nb 'D='db 'Z='zb ';' dat(n1s) dat(n2s) if db ~= 0 then /* if there were any different blocks */ return 'DIFF:' name1 '; B='nb 'D='db 'Z='zb ';' dat(n1s) dat(n2s) return 'SAME:' name1 ';' word(n1s,2) dat(n1s) subword(n1s,8) dat: procedure /* returns date/time as YY/MM/DD,HH:MM */ arg s /* seconds = word(s,7)%50 */ minutes = word(s,6)//60 if minutes < 10 then minutes = "0"minutes return date('O',word(s,5),I)','word(s,6)%60':'minutes /* End of CheckBack.Rexx */ ----------- cut here ---------- -- Joe Smith (408)922-6220 | SMTP: jms@tardis.tymnet.com or jms@gemini.tymnet.com BT Tymnet Tech Services | UUCP: ...!{ames,pyramid}!oliveb!tymix!tardis!jms PO Box 49019, MS-C51 | BIX: smithjoe | CA license plate: "POPJ P," (PDP-10) San Jose, CA 95161-9019 | humorous disclaimer: "My Amiga 3000 speaks for me."