/* BackUp ReFresher 1.00 */
/* Copyright  Michael Tanzer, 1992 */
/* See additional notices in accompanying documentation */

flagsw = 0                 /* 1 = check & set archive flag; 0 = check date */
clonesw = 1                /* 1 = Delete disused directories and files...  */
                           /*     ...and ensure exact match on names       */
ressw = 0                  /* 1 = Make COPY and PROTECT commands resident  */
asynchsw = 1               /* 1 = Run Burf asynchronously                  */
log = 'ram:BurfLog'        /* Log file: may be '*' (console) or a file id  */
                           /*     e.g. 'RAM:BurfLog')                      */

parse arg source target .               /* Get requested directories       */
if source=='?' | upper(source)=='HELP' | words(source)=0 then do
  say 'Use Burf to refresh a backup of a directory.'
  say 'Format: RX BURF source <target>'
  say 'If no target is specified, the current directory will be used.'
  exit
  end

/* Make sure support library is available */
call addlib 'rexxsupport.library',0,-30,0

/* If running asynchronously, get source and target */
if getclip('Burf.asynchsw')=1 then do   /* Already running asynchronously  */
  source = getclip('Burf.source')       /* Get the source directory        */
  target = getclip('Burf.target')       /* Get the target directory        */
  call setclip('Burf.asynchsw')         /* Delete clips                    */
  call setclip('Burf.source')
  call setclip('Burf.target')
  end

/* If running synchronously, verify parameters */
else do
  w = word(statef(source),1)            /* Check source data               */
  if w=='FILE' then do                  /* Source is not a directory       */
    say 'Source object not of required type.'
    exit 212
    end
  if w~=='DIR' then do                  /* Source not found                */
    say 'Source directory not found.'
    exit 204
    end
  w = substr(source,length(source),1)   /* Dir must end in '/' or ':'      */
  if w~=='/' & w~==':' then source = source'/'
  if words(target)=0 then target = pragma('directory') /* Cur if no target */
  w = word(statef(target),1)            /* Check target data               */
  if w=='FILE' then do                  /* Target is not a directory       */
    say 'Target object not of required type.'
    exit 212
    end
  if clonesw then do                    /* Cloning can be dangerous!       */
    say 'Burf may delete subdirectories and files in' target'.'
    say 'Enter ''GO'' if you want to continue.'
    pull w
    if w~='GO' then do
      say 'Operation aborted.'
      exit
      end
    end
  w = substr(target,length(target),1)   /* Dir must end in '/' or ':'      */
  if w~=='/' & w~==':' then target = target'/'
  if asynchsw then do                   /* Set up for asynch task          */
    if log='*' then do                  /* Can't run asynch w/console      */
      say 'Burf cannot run asynchronously and write its log to a console.'
      say 'Change the setting of either ''asynchsw'' or ''log''.'
      exit 97
      end
    call setclip('Burf.asynchsw',1)     /* Set flag for asynch task        */
    call setclip('Burf.source',source)  /* Store source for asynch task    */
    call setclip('Burf.target',target)  /* Store target for asynch task    */
    address arexx 'Burf' source target  /* Launch asynch task              */
    say 'Asynchronous Burf task started.'
    exit                                /* Free up CLI                     */
    end
  end

/* Open the log file */
call open 'logfile',log,'W'
if ~result then do                      /* Handle error                    */
  say 'Unable to open log file.'        /* Try to display error message    */
  exit 99                               /* Pass non-zero return code       */
  end

source.1 = source                       /* Check source directory first    */
target.1 = target                       /* Set corresponding target        */
dx = 1                                  /* Set directory index             */
dircnt  = 0                             /* Clear directory count           */
mdircnt = 0                             /* Clear directories made count    */
drencnt = 0                             /* Clear directories renamed count */
ddelcnt = 0                             /* Clear directories deleted count */
filecnt = 0                             /* Clear file count                */
replcnt = 0                             /* Clear files replaced count      */
copycnt = 0                             /* Clear files copied count        */
frencnt = 0                             /* Clear files renamed count       */
fdelcnt = 0                             /* Clear files deleted count       */
errorcnt = 0                            /* Clear error count               */

signal on error                         /* Trap ADDRESS COMMAND errors     */

/* Make COPY and PROTECT commands resident */
if ressw then do
  address command 'RESIDENT C:COPY'
  if flagsw then address command 'RESIDENT C:PROTECT'
  end

/* Loop through files and subdirectories */
do forever
  if dx=0 then leave                    /* No more subdirs                 */
  source = source.dx                    /* Get new source directory        */
  target = target.dx                    /* Get corresponding target dir    */
  call ckfiles                          /* Go check files                  */
  dx = dx-1                             /* Decrement directory index       */
  call getdirs                          /* Go get more subdirs             */
  end

/* Remove COPY and PROTECT commands from storage */
if ressw then do
  address command 'RESIDENT COPY REMOVE'
  if flagsw then address command 'RESIDENT PROTECT REMOVE'
  end

/* Type the totals and get out */
call writelog 'Source directories checked:' right(dircnt,5)
call writelog 'Target directories made:   ' right(mdircnt,5)
call writelog 'Target directories renamed:' right(drencnt,5)
call writelog 'Target directories deleted:' right(ddelcnt,5)
call writelog 'Source files checked:      ' right(filecnt,5)
call writelog 'Target files replaced:     ' right(replcnt,5)
call writelog 'Target files added:        ' right(copycnt,5)
call writelog 'Target files renamed:      ' right(frencnt,5)
call writelog 'Target files deleted:      ' right(fdelcnt,5)
call writelog 'Errors:                    ' right(errorcnt,5)
if asynchsw then address command,
  'sys:utilities/say "Burf has finished."'
exit

/* Subroutines */
getdirs:                                /* Get subdirectories              */
  sdirnames = showdir(source,'d')       /* Get the source subdir names     */
  if clonesw then do                    /* If deleting disused dirs...     */
    tdirnames = showdir(target,'d')     /* ...get target subdir names...   */
    udirnames = upper(tdirnames)        /* ...get UC version               */
    end
  wcnt = words(sdirnames)               /* Save the count                  */
  do wx = 1 to wcnt                     /* Add names to array              */
    dirname = word(sdirnames,wx)        /* Get directory name              */
    dx = dx+1                           /* Bump directory index            */
    source.dx = source||dirname'/'      /* Save new subdir name            */
    target.dx = target||dirname'/'      /* Save corresp target             */
    if ~clonesw then iterate            /* Don't delete disused dirs       */
    w = find(udirnames,upper(dirname))  /* Find name in UC list            */
    if w=0 then iterate                 /* Go for next one if not found    */
    tdirname = word(tdirnames,w)        /* Get name from target list       */
    if tdirname~==dirname then do       /* Case mismatch                   */
      call writelog '  Renaming directory: ' target||tdirname 'to',
        target||dirname
      call rename target||tdirname,target||dirname /* Rename it            */
      if result then drencnt = drencnt+1/* Bump directories renamed count  */
      else do                           /* Handle error                    */
        errorcnt = errorcnt+1           /* Bump error count                */
        call writelog '! Rename failed.'
        end
      end
    udirnames = delword(udirnames,w,1)  /* Remove name from UC list        */
    tdirnames = delword(tdirnames,w,1)  /* Remove name from LC list        */
    end
  if ~clonesw then return               /* Don't delete disused subdirs    */
  wcnt = words(tdirnames)               /* Get number of dirs to delete    */
  do wx = 1 to wcnt                     /* Add names to array              */
    victim.wx = target||word(tdirnames,wx)'/' /* Name must end with '/'    */
    end
  vx = wcnt                             /* Get number of disused subdirs   */
  do forever                            /* Loop through disused subdirs    */
    if vx=0 then leave                  /* No more subdirs                 */
    victim = victim.vx                  /* Get next disused subdir         */
    vdirnames = showdir(victim,'d')     /* Get lower level subdir names    */
    wcnt = words(vdirnames)             /* Save the count                  */
    do wx = 1 to wcnt                   /* Add names to array              */
      dirname = word(vdirnames,wx)      /* Get subdir name                 */
      vx = vx+1                         /* Bump disused subdir index       */
      victim.vx = victim||dirname'/'    /* Save new name                   */
      end
    if wcnt>0 then iterate              /* More subdirs to check           */
    vfilenames = showdir(victim,'f')    /* Get the disused file names      */
    wcnt = words(vfilenames)            /* Save the count                  */
    do wx = 1 to wcnt                   /* Loop through disused files      */
      filename = victim||word(vfilenames,wx) /* File to be deleted         */
      call writelog '      Deleting file:  ' filename
      call delete filename              /* Delete it                       */
      if result then fdelcnt = fdelcnt+1/* Bump files deleted count        */
      else do                           /* Handle error                    */
        errorcnt = errorcnt+1           /* Bump error count                */
        call writelog '!     Delete failed'
        end
      end
    dirname = substr(victim,1,length(victim)-1) /* Remove '/' from name    */
    call writelog '  Deleting directory: ' dirname
    call delete dirname                 /* Delete disused subdir           */
    if result then ddelcnt = ddelcnt+1  /* Bump directories deleted count  */
    else do                             /* Handle error                    */
      errorcnt = errorcnt+1             /* Bump error count                */
      call writelog '! Delete failed.'
      end
    vx = vx-1                           /* Decrement index                 */
    end
  drop victim. victim vdirnames vfilenames
  return

ckfiles:                                /* Check file dates or flags       */
  dircnt = dircnt+1                     /* Bump directory count            */
  call writelog '  Checking directory: ' source
  w = word(statef(target),1)            /* Make sure target dir exists     */
  if w=='FILE' then do                  /* Target exists as file           */
    w = substr(target,1,length(target)-1) /* Remove '/' from target        */
    errorcnt = errorcnt+1               /* Bump error count                */
    call writelog '! File found:         ' w
    return                              /* Bypass this directory           */
    end
  if w~=='DIR' then do                  /* Target does not exist           */
    w = length(target)                  /* Get length of target name       */
    if substr(target,w)==':' then do    /* Target is a device              */
      call writelog 'Device not found:' target
      call writelog 'Operation aborted.'
      exit 205
      end
    w = substr(target,1,w-1)            /* Remove '/' from target          */
    call writelog '  Making directory:   ' target
    call makedir(w)                     /* Make new directory              */
    if result then mdircnt = mdircnt+1  /* Bump directories made count     */
    else do                             /* Handle error                    */
      call writelog 'Unable to make directory:' w
      call writelog 'Operation aborted.'
      exit 204
      end
    end

  sfilenames = showdir(source,'f')      /* Get the source file names       */
  if clonesw then do                    /* If deleting disused files...    */
    tfilenames = showdir(target,'f')    /* ...get target file names...     */
    ufilenames = upper(tfilenames)      /* ...get UC version               */
    end
  wcnt = words(sfilenames)              /* Save the count                  */
  do wx = 1 to wcnt
    filecnt = filecnt+1                 /* Bump file count                 */
    filename = word(sfilenames,wx)      /* Get a filename                  */
    call writelog '      Checking file:  ' filename
    sourcefile = source||filename       /* Get source filename             */
    targetfile = target||filename       /* Get target filename             */
    tinfo = statef(targetfile)          /* Get target file info            */
    if word(tinfo,1)=='DIR' then do     /* Target exists as directory      */
      errorcnt = errorcnt+1             /* Bump error count                */
      call writelog '!     Directory found:' targetfile
      iterate                           /* Go check next file              */
      end
    if length(tinfo)=0 then do          /* Target file does not exist      */
      call writelog '      Copying file:   ' filename
      address command 'COPY "'sourcefile'" "'targetfile'" CLONE' /* Copy   */
      if flagsw then address command 'PROTECT "'sourcefile'" +A'/* Set flag*/
      copycnt = copycnt+1               /* Bump copy count                 */
      iterate                           /* Go check next file              */
      end
    if clonesw then do                  /* If deleting disused files:      */
      w = find(ufilenames,upper(filename)) /* Find name in UC list         */
      tfilename = word(tfilenames,w)       /* Get name from LC list        */
      ufilenames = delword(ufilenames,w,1) /* Remove name from UC list     */
      tfilenames = delword(tfilenames,w,1) /* Remove name from LC list     */
      end
    sinfo = statef(sourcefile)          /* Get source file info            */
    if flagsw then do                   /* Check arhcive flag, not date    */
      w = substr(word(sinfo,4),4,1)     /* Isolate archive flag            */
      if w='A' then do                  /* File has been archived          */
        if clonesw then call ckcase     /* Go check for case mismatch      */
        iterate                         /* Go check next file              */
        end
      call writelog '      Replacing file: ' filename
      address command 'COPY "'sourcefile'" "'targetfile'" CLONE' /* Replace*/
      address command 'PROTECT "'sourcefile'" +A' /* Set archive flag      */
      replcnt = replcnt+1               /* Bump replace count              */
      iterate                           /* Go check next file              */
      end
    days = right(word(sinfo,5),4,'0')   /* Get days                        */
    mins = right(word(sinfo,6),4,'0')   /* Get minutes                     */
    tics = right(word(sinfo,7),4,'0')   /* Get ticks                       */
    sourcedate = days||mins||tics       /* Build source date               */
    days = right(word(tinfo,5),4,'0')   /* Get days                        */
    mins = right(word(tinfo,6),4,'0')   /* Get minutes                     */
    tics = right(word(tinfo,7),4,'0')   /* Get ticks                       */
    targetdate = days||mins||tics       /* Build target date               */
    if sourcedate<targetdate then do    /* Target file is newer            */
      errorcnt = errorcnt+1             /* Bump error count                */
      call writelog '!     Backup is newer.'
      iterate                           /* Go check next file              */
      end
    if sourcedate~>targetdate then do   /* No replacement required         */
      if clonesw then call ckcase       /* Go check for case mismatch      */
      iterate                           /* Go check next file              */
      end
    call writelog '      Replacing file: ' filename
    address command 'COPY "'sourcefile'" "'targetfile'" CLONE' /* Replace  */
    replcnt = replcnt+1                 /* Bump replace count              */
    end
  if ~clonesw then return               /* Don't delete disused files      */
  wcnt = words(tfilenames)              /* Get number of files to delete   */
  do wx = 1 to wcnt                     /* Delete disused files            */
    filename = target||word(tfilenames,wx) /* File to be deleted           */
    call writelog '      Deleting file:  ' filename
    call delete filename                /* Delete it                       */
    if result then fdelcnt = fdelcnt+1  /* Bump files deleted count        */
    else do                             /* Handle error                    */
      errorcnt = errorcnt+1             /* Bump error count                */
      call writelog '!     Delete failed'
      end
    end
  return

/* Check filename case and rename if required */
ckcase:
  if tfilename==filename then return    /* No case mismatch                */
  call writelog '      Renaming file:  ' target||tfilename
  call rename target||tfilename,target||filename /* Rename it              */
  if result then frencnt = frencnt+1    /* Bump files renamed count        */
  else do                               /* Handle error                    */
    errorcnt = errorcnt+1               /* Bump error count                */
    call writelog '!     Rename failed.'
    end
  return

/* Write a line to the log file */
writelog:
  parse arg line                        /* Get the line                    */
  call writeln 'logfile',line           /* Write it                        */
  if result>0 then return               /* Return if all is well           */
  say 'Unable to write to log file.'    /* Try to display error message    */
  exit 98                               /* Pass non-zero return code       */

/* Handle non-zero return code from ADDRESS COMMAND */
error:
  call writelog 'Operation aborted.'
  if asynchsw then address command,
    'sys:utilities/say "Burf operation aborted"'
  exit rc