[comp.lang.rexx] Programs for the "ARexxLib" archive

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."