home *** CD-ROM | disk | FTP | other *** search
/ The Best of Mecomp Multimedia 2 / MECOMP-CD-II.iso / amiga / tools / system / updatecopy / source / updatecopy.e
Encoding:
Text File  |  1998-01-20  |  29.1 KB  |  951 lines

  1. /* UpDateCopy.e
  2. ** Copies multiple files/directories to one destination.
  3. ** Not existing directories are created.
  4. ** Already existing files are only replaced by newer ones
  5. ** (UpDateCopy first checks for versions-string and then
  6. **  for dates).
  7. **
  8. ** $VER: UpDateCopy.e 1.00 (20.01.98)
  9. **
  10. ** This program is Cardware. If you use it you should send an Email to
  11. ** the author. Also small presents are very welcome.
  12. ** You may use this sourcefile or parts of it freely in your programs.
  13. ** But please do not spread a modified version under this name (UpDateCopy).
  14. ** For Bugreports, ideas or anything else send a Email to:
  15. **    ss37@irz.inf.tu-dresden.de
  16. ** 
  17. ** Sven Steiniger, 1996, 1997, 1998
  18. **
  19. ** Fold-start: ->// ""
  20. ** Fold-stop:  ->\\
  21. **
  22. ** History:
  23. **  1.00: - implemented SAB, CAB options
  24. **  0.54: - implemented NOASK-option
  25. **  0.53: - now user is asked on errors. Doesn't works perfect but I don't now
  26. **          how to implement it properly without adding to much extra code.
  27. **  0.52: - improved SMARTINFO-option
  28. **  0.51: - removed bug of version-scan-routines
  29. **  0.50: - added FAST-option
  30. **  0.49: - added SMARTINFO option
  31. **        - added wildcard-check. Now you can also use '*' as wildcard and
  32. **          you can also pass devices as sources (eq. ram:)
  33. **  0.48: - used exthelp-feature OF ReadArgs()
  34. **        - no longer needs 'c:copy'-command. Implemented an own routine.
  35. **  0.47: - file was not closed before SetProtection()/SetFileDate()
  36. **  0.46: - added PAPF-option -> archive protection flag is not changed
  37. **  0.45: - added CLONE-option -> Datestamp is copied
  38. **  0.44: - removed a unnecessary module. Saves 1K of executable size!
  39. **        - removed unnecessary ExamineFH()
  40. **        - now file gets only examined if really necessary
  41. **        - optimized a bit
  42. **        - FIXED: TESTMODE was buggy -> Directories were still created.
  43. **  0.43: - add new option TESTMODE; dont copy/replace files
  44. **  0.42: - add new option NODATECHECK; dont compare datestamps
  45. **  0.41: - replaced ALL reads/writes by fread/fwrite
  46. **  0.40: - first PUBLIC release
  47. */
  48.  
  49. OPT OSVERSION=37
  50. OPT REG=5
  51.  
  52. MODULE 'dos/dos','dos/dosasl','dos/dostags','dos/rdargs','dos/stdio'
  53.  
  54. RAISE "MEM"  IF String()=NIL,
  55.       "addp" IF AddPart()=DOSFALSE,
  56.       "^C"   IF CtrlC()=TRUE,
  57.       "copy" IF FileLength()=-1,
  58.       "MEM"  IF AllocDosObject()=NIL,
  59.       "fatt" IF SetProtection()=DOSFALSE,
  60.       "fatt" IF SetComment()=DOSFALSE,
  61.       "fatt" IF SetFileDate()=DOSFALSE
  62.  
  63. CONST MAXPATH=255,                -> Maximum path length
  64.       SPACEADD=3,                 -> Number of spaces per indent
  65.       SAFETYBYTES=100,            -> Maximum length of versionstring
  66.       BIGFILEMEM=100000           -> Files bigger than that are not read
  67.                                   -> completly into memory
  68.  
  69. CONST PATHLENGTH=MAXPATH-1,
  70.       BIGFILESIZE=BIGFILEMEM-SAFETYBYTES
  71.                                   
  72.  
  73. ENUM PDIR_Error,
  74.      PDIR_Skipped,
  75.      PDIR_Created
  76.  
  77. ENUM PFILE_Error,
  78.      PFILE_Skipped,
  79.      PFILE_Copied,
  80.      PFILE_Replaced
  81.  
  82. ENUM ASKUSER_Abort,
  83.      ASKUSER_Retry,
  84.      ASKUSER_Skip
  85.  
  86. DEF frompath[MAXPATH]:STRING,     -> actual sourcepath
  87.     fromlist:PTR TO LONG,         -> pointer to array of sourcestrings
  88.     topath[MAXPATH]:STRING,       -> destinationpath
  89.     doinfo,                       -> should we write informations ?
  90.     recursiv,                     -> scan recursively through subdirectories ?
  91.     ignoreprotection,             -> clear delete-protection ?
  92.     checkversion,                 -> compare version-strings ?
  93.     checkdates,                   -> compare datestamps ?
  94.     testmode,                     -> test modus ? (do not copy/replace files)
  95.     clone,                        -> copy datestamp ?
  96.     cleararchivebit,              -> clear archivebit OF copied file
  97.     smartinfo,                    -> only display copied/replaced files
  98.     fastdisplay,                  -> no linefeed
  99.     interaction,                  -> ask user on errors
  100.     setarchivebit,                -> set Archive bit
  101.     checkarchivebit,              -> check Archive bit
  102.     dirlock=NIL                   -> PTR TO lock-structure OF destination path
  103.  
  104. PROC main() HANDLE
  105. ->// "main()"
  106. DEF rdargs=NIL,
  107.     myargs:PTR TO LONG,
  108.     template,
  109.     myrdargs=NIL:PTR TO rdargs,
  110.     exthelp[1000]:STRING
  111.  
  112.   /* Initialize argument-array */
  113.   myargs:=[
  114.            NIL,           -> fromlist
  115.            NIL,           -> destination path
  116.            FALSE,         -> ~showinfo
  117.            FALSE,         -> scan recursiv throuhg subdirectories
  118.            FALSE,         -> ignore protectionbits
  119.            FALSE,         -> deep scan
  120.            FALSE,         -> ~compare datestamps
  121.            FALSE,         -> testmode
  122.            FALSE,         -> clone
  123.            FALSE,         -> preserve archive protection flag
  124.            FALSE,         -> smartinfo
  125.            FALSE,         -> fastdisplay
  126.            FALSE,         -> interaction
  127.            FALSE,         -> set Archivebit
  128.            FALSE          -> check Archivbit
  129.           ]:LONG    
  130.  
  131.   myrdargs:=AllocDosObject(DOS_RDARGS,NIL)
  132.  
  133.   StrCopy(exthelp, '\n'+
  134.                    '  UpDateCopy v1.00\n'+
  135.                    ' -----------------\n'+
  136.                    '  Copy files and directories.\n'+
  137.                    '  Not existing directories are created and\n'+
  138.                    '  already existing files are only replaced\n'+
  139.                    '  by newer ones. Allows pattern-matching and\n'+
  140.                    '  multiple sourcedirectories/files.\n\n'+
  141.                    'Sven Steiniger, 1996, 1997, 1998\n',
  142.           ALL)
  143.   StrAdd(exthelp,  '\n'+
  144.                    '  FROM        - source directory/file(s)\n'+
  145.                    '  TO          - destination directory\n'+
  146.                    '  QUIET       - no outputs\n'+
  147.                    '  ALL         - scans through subdirectories recursively\n'+
  148.                    '  FORCE       - ignore delete-protectionbit\n'+
  149.                    '  DEEP        - compare version-strings\n'+
  150.                    '  NODATECHECK - don''t compare datestamps\n'+
  151.                    '  TESTMODE    - neither copy/replace files nor create directories\n'+
  152.                    '  CLONE       - copy datestamp\n'+
  153.                    '  PAB         - don''t clear the archive protection flag\n'+
  154.                    '  SMARTINFO   - only displays copied/replaced files\n'+
  155.                    '  FAST        - fast output. No linefeeds\n'+
  156.                    '  NOASK       - don''t ask user on errors\n'+
  157.                    '  SETARCBIT   - set archive-bit on copied directories/files\n'+
  158.                    '  CHECKARCBIT - only copy files with no archive-bit\n',
  159.          ALL)
  160.   myrdargs.exthelp:=exthelp
  161.  
  162.   /* Parse Commandline */
  163.   IF rdargs:=ReadArgs(template:='FROM/M,TO/A,QUIET/S,ALL/S,FORCE/S,DEEP/S,'+
  164.                                 'NDC=NODATECHECK/S,TESTMODE/S,CLONE/S,PAB=PAPF/S,'+
  165.                                 'SMARTINFO/S,FAST/S,NOASK/S,SAB=SETARCBIT/S,'+
  166.                                 'CAB=CHECKARCBIT/S',
  167.                       myargs,myrdargs)
  168.  
  169.     /* Copy Datas to global variables */
  170.     fromlist         := myargs[0]
  171.     StrCopy(topath,myargs[1],ALL)
  172.     doinfo           := Not(myargs[2])
  173.     recursiv         := myargs[3]
  174.     ignoreprotection := myargs[4]
  175.     checkversion     := myargs[5]
  176.     checkdates       := Not(myargs[6])
  177.     testmode         := myargs[7]
  178.     clone            := myargs[8]
  179.     cleararchivebit  := Not(myargs[9])
  180.     smartinfo        := myargs[10]
  181.     fastdisplay      := myargs[11]
  182.     interaction      := Not(myargs[12])
  183.     setarchivebit    := myargs[13]
  184.     checkarchivebit  := myargs[14]
  185.  
  186.     IF fromlist=NIL THEN Throw("ARGS",'No source specified.')
  187.     init_arguments()
  188.  
  189.     IF doinfo
  190.       PrintF('Updating files to "\s".\n',topath)
  191.       IF testmode THEN PrintF('** Testmode **\n')
  192.     ENDIF
  193.  
  194.     IF fastdisplay
  195.       PrintF('\n')
  196.       smartinfo:=FALSE
  197.     ENDIF
  198.  
  199.     WHILE fromlist[]                     -> 'fromlist' is array of string-pointers
  200.       StrCopy(frompath,fromlist[]++,ALL) -> copy the string and increment 'fromlist'
  201.       checkWildCard(frompath)
  202.       scan_directory(frompath,'',1)    -> then process this directory
  203.     ENDWHILE
  204.  
  205.   ENDIF
  206.  
  207. EXCEPT DO  -> Cleanup
  208.  
  209.   IF fastdisplay THEN reportLine(0,'',NIL,NIL)
  210.  
  211.   free_arguments()
  212.   IF rdargs THEN FreeArgs(rdargs)
  213.   FreeDosObject(DOS_RDARGS,myrdargs)
  214.  
  215.   IF exception
  216.     /* Print error description */
  217.     PrintF('Error: ')
  218.     SELECT exception
  219.       CASE "MEM"  ; PrintF('Not enough memory.\n')
  220.       CASE "addp" ; PrintF('Path too long !?\n')
  221.       CASE "^C"   ; PrintF('User abort.\n')
  222.       CASE "copy" ; PrintF('Could not copy file.\n')
  223.       CASE "open" ; PrintF('Could not open file.\n')
  224.       CASE "anly" ; PrintF('Could not analyse file.\n')
  225.       CASE "fatt" ; PrintF('Could not set file attributes.\n')
  226.       DEFAULT     ; PrintF('\s\n',exceptioninfo)
  227.     ENDSELECT
  228.   ELSE
  229.     PrintF('Finished.\n')
  230.   ENDIF
  231.  
  232.   CleanUp(exception)
  233.  
  234. ENDPROC
  235. ->\\
  236.  
  237. CHAR '$VER: UpDateCopy 1.00 (20.01.98)',0
  238.  
  239.  
  240. /* checks the arguments provied by user
  241. */
  242. PROC init_arguments()
  243. ->// "init_arguments()"
  244.  
  245.   IF (dirlock:=Lock(topath,SHARED_LOCK))=NIL            -> check if destination
  246.     Throw("dir",'Could not lock destination directory') -> directory exists
  247.   ENDIF
  248.   /* Enable Version Check if datestamp-check is switch off */
  249.   IF checkdates=FALSE THEN checkversion:=TRUE
  250.  
  251. ENDPROC
  252. ->\\
  253.  
  254. /* handles so special cases with wildcards
  255. ** Parameter:
  256. **   file - file/dir to be checked
  257. */
  258. PROC checkWildCard(file:PTR TO CHAR)
  259. ->// "checkWildCard"
  260. DEF len
  261.  
  262.   len:=StrLen(file)
  263.   IF file[len-1]="*"
  264.     -> replace '*'-wildcard by '#?'
  265.     CopyMem('#?',file+len-1,3)
  266.   ELSEIF file[len-1]=":"
  267.     -> it's an device. Therefore add wildcard
  268.     CopyMem('#?',file+len,3)
  269.   ENDIF
  270.  
  271. ENDPROC
  272. ->\\
  273.  
  274. /* Cleanup datas allocated during init_arguments
  275. */
  276. PROC free_arguments()
  277. ->// "free_arguments()"
  278.   UnLock(dirlock)             -> Unlock destination directory
  279. ENDPROC
  280. ->\\
  281.  
  282.  
  283. /* Scans a directory and copies files/creates subdirectories
  284. ** Parameter:
  285. **   directory    - name of directory to be scanned
  286. **   path         - the current delta path
  287. **   depth        - recursion level
  288. ** Example:
  289. **   The source path is "esource:src".
  290. **   'directory' is "esource:src/tools/file".
  291. **   Then 'path' have to be "tools/file".
  292. */
  293. PROC scan_directory(directory,path,depth) HANDLE
  294. ->// "scan_directory()"
  295. DEF info:PTR TO fileinfoblock,
  296.     anchor=NIL:PTR TO anchorpath,
  297.     error,
  298.     fullpath,
  299.     mypath[MAXPATH]:STRING,
  300.     length
  301.  
  302.   /* Create and initialize anchor structure needed for
  303.   ** scanning through directory.
  304.   ** This structure has no fixed size.
  305.   */
  306.   anchor:=NewR(SIZEOF anchorpath+MAXPATH)
  307.   anchor.strlen:=PATHLENGTH
  308.  
  309.   -> Get start of string
  310.   fullpath:=anchor+SIZEOF anchorpath
  311.  
  312.   error:=MatchFirst(directory,anchor)
  313.   WHILE error=DOSFALSE
  314.  
  315.     CtrlC()
  316.  
  317.     info:=anchor.info                           -> get fileinfoblock
  318.     IF info.direntrytype>0                      -> is it a directory ?
  319.  
  320.       StrCopy(mypath,path,ALL)                  -> init new delta path
  321.       AddPart(mypath,info.filename,PATHLENGTH)  -> including new subdirectory
  322.  
  323.       process_directory(fullpath,info,mypath,   -> knows what to do with this
  324.                         depth*SPACEADD)         -> directory
  325.  
  326.       IF recursiv                               -> scan subdirectories ?
  327.         length:=StrLen(fullpath)
  328.         IF (length+5)<MAXPATH
  329.           CopyMem('/#?',fullpath+length,4)      -> add pattern matching
  330.         ENDIF
  331.         scan_directory(fullpath,mypath,depth+1) -> call our self with new subdirectory
  332.         fullpath[length]:=0                     -> remove pattern matching
  333.       ENDIF
  334.  
  335.     ELSE
  336.  
  337.       process_file(fullpath,info,path,          -> knows what to do with it
  338.                    depth*SPACEADD)
  339.  
  340.     ENDIF
  341.  
  342.     error:=MatchNext(anchor)                    -> Next entry
  343.   ENDWHILE
  344.  
  345. EXCEPT DO
  346.  
  347.   IF anchor
  348.     MatchEnd(anchor)                            -> Clean up
  349.     Dispose(anchor)
  350.   ENDIF
  351.  
  352.   ReThrow()
  353.  
  354. ENDPROC
  355. ->\\
  356.  
  357.  
  358. /* takes care of fastdisplay-option
  359. */
  360. PROC reportLine(spaceanz,fmtstr,arg1,arg2,doCR=TRUE)
  361. ->// "reportLine()"
  362.  
  363.   IF fastdisplay
  364.     PrintF([141,$1b,"[","M",0]:CHAR)
  365.     spaceanz:=SPACEADD
  366.   ENDIF
  367.   WHILE spaceanz-->=0 DO FputC(stdout," ")
  368.   PrintF(fmtstr,arg1,arg2)                 -> Write directory name & status
  369.   IF doCR THEN PrintF('\n')
  370.  
  371. ENDPROC
  372. ->\\
  373.  
  374.  
  375. PROC askError()
  376. ->// "askError()"
  377. DEF c=0,
  378.     ret=ASKUSER_Abort
  379.  
  380.   IF interaction
  381.     PrintF('  (R)etry / (S)kip / (A)bort ? ')
  382.     Flush(stdout)
  383.     REPEAT
  384.     UNTIL WaitForChar(stdin,-1)=DOSTRUE
  385.     c:=FgetC(stdin)
  386.     Flush(stdin)
  387.     IF (c="r") OR (c="R")
  388.       ret:=ASKUSER_Retry
  389.     ELSEIF (c="a") OR (c="A")
  390.       ret:=ASKUSER_Abort
  391.     ELSE
  392.       c:="s"
  393.       ret:=ASKUSER_Skip
  394.     ENDIF
  395.   ENDIF
  396.   ->IF c THEN PrintF('\c',c)
  397.   IF c=0 THEN PrintF('\n')
  398. ENDPROC ret
  399. ->\\
  400.  
  401. /* Prints a directory status.
  402. ** Parameter:
  403. **   spaces - number of leading spaces
  404. **   status - status (PDIR_Error, PDIR_Skipped, PDIR_Created)
  405. **   path   - directory path
  406. */
  407. PROC printDirStatus(spaces,status,path)
  408. ->// "printDirStatus()"
  409. DEF stri
  410.  
  411.   SELECT status
  412.     CASE PDIR_Created ; stri:='created'
  413.     CASE PDIR_Skipped ; IF smartinfo THEN RETURN  -> don't display skipped dirs with smartinfo
  414.                         stri:='skipped'
  415.     DEFAULT           ; stri:='Error!!'
  416.   ENDSELECT
  417.  
  418.   -> Write directory name & status
  419.   IF doinfo OR (status=PDIR_Error)
  420.     reportLine(spaces,'\e[1m\s\e[0m..\s',path,stri,status<>PDIR_Error)
  421.   ENDIF
  422.  
  423. ENDPROC
  424. ->\\
  425.  
  426. /* Process an directory. If it doesnt exists it will be created
  427. ** Parameter:
  428. **  directory - full path of source directory
  429. **  info      - pointer to fileinfoblock of source directory
  430. **  path      - delta path of directory
  431. */
  432. PROC process_directory(directory,info:PTR TO fileinfoblock,path,spaces)
  433. ->// "process_directory()"
  434. DEF stri[MAXPATH]:STRING,
  435.     lock
  436.  
  437.   StrCopy(stri,topath,ALL)
  438.   AddPart(stri,path,PATHLENGTH)                  -> thats the destion-directory
  439.  
  440.   IF checkarchivebit AND
  441.      (info.protection AND FIBF_ARCHIVE)          -> Archive-bit set ?
  442.     printDirStatus(spaces, PDIR_Skipped, path)
  443.  
  444.   ELSEIF lock:=Lock(stri,SHARED_LOCK)            -> Exists this Directory ?
  445.     printDirStatus(spaces, PDIR_Skipped, path)
  446.     UnLock(lock)                                 -> Unlock directory
  447.     IF setarchivebit THEN SetProtection(directory,info.protection OR FIBF_ARCHIVE)
  448.  
  449.   ELSEIF testmode                                -> Don't create directory in testmode
  450.     printDirStatus(spaces, PDIR_Created, path)   -> but write out a info
  451.  
  452.   ELSEIF lock:=needThisDir(stri)                 -> Create the directory
  453.     printDirStatus(spaces, PDIR_Created, path)
  454.     UnLock(lock)                                 -> No erros, unlock directory
  455.     copyAdditionalInformations(stri,info)
  456.     IF setarchivebit THEN SetProtection(directory,info.protection OR FIBF_ARCHIVE)
  457.  
  458.   ELSE                                           -> Directory could not be created
  459.     printDirStatus(spaces, PDIR_Error, path)     -> as it does not exists before
  460.     lock:=askError()
  461.     SELECT lock
  462.       CASE ASKUSER_Abort
  463.         Throw("dir",'Could not create directory')-> something went wrong
  464.       CASE ASKUSER_Retry
  465.         process_directory(directory,info,path,spaces)
  466.     ENDSELECT
  467.   ENDIF
  468.  
  469. ENDPROC
  470. ->\\
  471.  
  472. /*
  473. ** Gets the PathPart of an string.
  474. */
  475. PROC getPath(dst, stri) IS StrCopy(dst, stri, PathPart(stri)-stri)
  476.  
  477. /* Checks if an directory already exists.
  478. ** If not it (and all necessary parent directories) are created.
  479. ** Parameter:
  480. **   stri - full name of the directory to be created
  481. ** Returns a lock to the directory or NIL in case of an error.
  482. */
  483. PROC needThisDir(stri)
  484. ->// "needThisDir()"
  485. DEF lock, hstri[MAXPATH]:STRING
  486.  
  487.   ->WriteF('need dir >>\s<<\n',stri)
  488.   CtrlC()
  489.  
  490.   IF lock:=Lock(stri, SHARED_LOCK)
  491.     -> Already exists
  492.   ELSEIF lock:=needThisDir(getPath(hstri, stri))
  493.     -> All parent directories exists
  494.     UnLock(lock)
  495.     lock:=CreateDir(stri)
  496.   ENDIF
  497.  
  498. ENDPROC lock
  499. ->\\
  500.  
  501. /* Prints a file status.
  502. ** Parameter:
  503. **   spaces - number of leading spaces
  504. **   status - status (PFILE_Error, PFILE_Skipped, PFILE_Created, PFILE_Skipped)
  505. **   file   - filename
  506. */
  507. PROC printFileStatus(spaces,status,file)
  508. ->// "printFileStatus()"
  509. DEF stri
  510.  
  511.   SELECT status
  512.     CASE PFILE_Copied   ; stri:='copied'
  513.     CASE PFILE_Replaced ; stri:='replaced'
  514.     CASE PFILE_Skipped  ; IF smartinfo THEN RETURN -> don't display skipped dirs with smartinfo
  515.                           stri:='skipped'
  516.     DEFAULT             ; stri:='Error!!'
  517.   ENDSELECT
  518.   -> Write file status
  519.   IF doinfo OR (status=PFILE_Error)
  520.     reportLine(spaces,'\s..\s',file,stri,status<>PFILE_Error)
  521.   ENDIF
  522.  
  523. ENDPROC
  524. ->\\
  525.  
  526. /* Processes an file. If it does not exists or is newer it is copied.
  527. ** Parameter:
  528. **   file  - full source filename (include path)
  529. **   info  - ptr to fileinfoblock of sourcefile
  530. **   path  - deltapath to directory (exclude filename !)
  531. */
  532. PROC process_file(file,info:PTR TO fileinfoblock,path,spaces) HANDLE
  533. ->// "process_file()"
  534. DEF stri[MAXPATH]:STRING,
  535.     filepath[MAXPATH]:STRING,
  536.     fh=NIL,
  537.     toinfo=NIL:PTR TO fileinfoblock,
  538.     result=0,
  539.     frombuf=NIL,
  540.     lock,
  541.     hstri[MAXPATH]:STRING
  542.  
  543.   StrCopy(filepath,path,ALL)                             -> create deltapath
  544.   AddPart(filepath,info.filename,PATHLENGTH)             -> inclusive filename
  545.  
  546.   StrCopy(stri,topath,ALL)                               -> create full destination
  547.   AddPart(stri,filepath,PATHLENGTH)                      -> filepath
  548.  
  549.   IF checkarchivebit AND
  550.      (info.protection AND FIBF_ARCHIVE)                  -> Archive-bit set
  551.  
  552.     -> Do not copy already archived files
  553.     printFileStatus(spaces, PFILE_Skipped, filepath)
  554.  
  555.   ELSEIF fh:=Open(stri,MODE_OLDFILE)                     -> Open destinationfile
  556.  
  557.     IF checkversion THEN result,frombuf:=compareversion(file,stri)
  558.  
  559.     IF checkdates AND (result=0)
  560.       /* Fileinfoblock have to be LONGWORD-aligned therefore
  561.       ** use dos.library to create this
  562.       */
  563.       toinfo:=AllocDosObject(DOS_FIB,NIL)
  564.  
  565.       -> fill fileinfoblock
  566.       IF ExamineFH(fh,toinfo)=DOSFALSE THEN Throw("file",'Could not examine file ?!')
  567.  
  568.       /* Compare versionstrings was either not specified by user
  569.       ** or was not successfull. Therefore Compare filedates if
  570.       ** the user want it.
  571.       */
  572.       result:=CompareDates(toinfo.datestamp,info.datestamp)
  573.     ENDIF
  574.     IF result>0                                        -> fromfile newer than tofile ?
  575.       Close(fh) ; fh:=NIL                              -> close destination
  576.  
  577.       IF ignoreprotection THEN SetProtection(stri,0)   -> Clear protectionflags
  578.                                                        -> if specified
  579.       copyfile(file,info,stri,frombuf)                 -> copy the file
  580.       printFileStatus(spaces, PFILE_Replaced, filepath)
  581.     ELSE
  582.       printFileStatus(spaces, PFILE_Skipped, filepath)
  583.     ENDIF
  584.   ELSEIF lock:=needThisDir(getPath(hstri, stri))       -> destination does not exists
  585.     UnLock(lock)
  586.     copyfile(file,info,stri)                           -> copy file
  587.     printFileStatus(spaces, PFILE_Copied, filepath)
  588.   ENDIF
  589.  
  590. EXCEPT DO  -> Cleanup
  591.  
  592.   /* compareversion() may return the contents of the
  593.   ** sourcefile. We must free this buffer
  594.   */
  595.   IF frombuf THEN Dispose(frombuf)
  596.  
  597.   IF toinfo THEN FreeDosObject(DOS_FIB,toinfo)
  598.   IF fh THEN Close(fh)
  599.  
  600.   IF exception
  601.     printFileStatus(spaces, PFILE_Error, filepath)
  602.     result:=askError()
  603.     SELECT result
  604.       CASE ASKUSER_Abort
  605.         ReThrow()            -> something went wrong
  606.       CASE ASKUSER_Retry
  607.         process_file(file,info,path,spaces)
  608.     ENDSELECT
  609.   ENDIF
  610.  
  611. ENDPROC
  612. ->\\
  613.  
  614.  
  615. /* Copies a file. For files >BIGFILESIZE Bytes c:copy is
  616. ** used.
  617. ** Parameter:
  618. **  fromfile  - full path of sourcefile
  619. **  tofile    - full path of destinationfile
  620. **  frombuf   - Contents of sourcefile. If NIL then sourcefile
  621. **              is read
  622. */
  623. PROC copyfile(fromfile,frominfo:PTR TO fileinfoblock,tofile,frombuf=NIL) HANDLE
  624. ->// "copyfile()"
  625. DEF fhfrom=NIL,
  626.     fhto=NIL,
  627.     length,
  628.     buf=NIL,
  629.     steplength,actlength
  630.  
  631.   IF testmode THEN RETURN
  632.  
  633.   IF (fhfrom:=Open(fromfile,OLDFILE)) AND    -> open sourcefile
  634.      (fhto:=Open(tofile,NEWFILE))            -> open destinationfile
  635.  
  636.     IF (length:=FileLength(fromfile))>BIGFILESIZE
  637.       /* Files >BIGFILESIZE are not read completly into memory but in parts
  638.       ** of BIGFILESIZE Bytes.
  639.       */
  640.  
  641.       /* Alloc new buffer */
  642.       buf:=NewR(BIGFILESIZE)
  643.  
  644.       /* steplength is the length of the current block to be read */
  645.       /* actlength is the position within the file */
  646.       steplength:=actlength:=BIGFILESIZE
  647.  
  648.       REPEAT
  649.         /* Read next block to buffer. If steplength<0 then EOF is reached */
  650.         IF steplength>0
  651.           IF Fread(fhfrom,buf,steplength,1)<1 THEN Raise("copy")
  652.         ENDIF
  653.  
  654.         /* Write buffer to destinationfile */
  655.         IF Fwrite(fhto,buf,steplength,1)<1 THEN Raise("copy")
  656.         
  657.         IF (actlength:=actlength+BIGFILESIZE)>length
  658.           steplength:=length-(actlength-BIGFILESIZE)
  659.         ENDIF
  660.  
  661.         /* Read until version-string was found or EOF */
  662.       UNTIL steplength<=0
  663.  
  664.     ELSE
  665.  
  666.       IF frombuf                               -> we already know the
  667.                                                -> contents of the sourcefile
  668.         /* Write buffer to destinationfile */
  669.         IF Fwrite(fhto,frombuf,length,1)<1 THEN Raise("copy")
  670.       ELSE
  671.         buf:=NewR(length)                      -> alloc new buffer
  672.  
  673.         /* Read sourcefile into buffer */
  674.         IF Fread(fhfrom,buf,length,1)<1 THEN Raise("copy")
  675.         /* Write buffer TO destinationfile */
  676.         IF Fwrite(fhto,buf,length,1)<1 THEN Raise("copy")
  677.       ENDIF
  678.  
  679.     ENDIF
  680.   ELSE                                       -> There went something wrong
  681.     Raise("copy")
  682.   ENDIF
  683.  
  684. EXCEPT DO
  685.  
  686.   IF buf THEN Dispose(buf)
  687.   IF fhfrom THEN Close(fhfrom)
  688.   IF fhto THEN Close(fhto)
  689.  
  690.   ReThrow()
  691.  
  692.   /* Everything went ok. Copy comment, protection flags etc. */
  693.   copyAdditionalInformations(tofile,frominfo)
  694.   IF setarchivebit THEN SetProtection(fromfile,frominfo.protection OR FIBF_ARCHIVE)
  695.  
  696. ENDPROC
  697. ->\\
  698.  
  699. /* Copies extra informations like protectionbits, comment etc.
  700. ** 'name' must be a valid File- or Directory path
  701. ** 'info' is the fileinfoblock of the original file/directories
  702. **        (the dates are copied from it)
  703. **
  704. ** Note: the file may not be opened or locked!
  705. */
  706. PROC copyAdditionalInformations(name,info:PTR TO fileinfoblock)
  707. ->// "copyAdditionalInformations()"
  708.  
  709. DEF protection
  710.  
  711.   /* Clear protectionbit (or not) */
  712.   protection:=info.protection
  713.   IF cleararchivebit THEN protection:=protection AND Not(FIBF_ARCHIVE)
  714.  
  715.   SetProtection(name,protection)       -> Copy protection bits
  716.   SetComment(name,info.comment)        -> Copy comment
  717.  
  718.   /* Copy datestamp if clone option is activated */
  719.   IF clone THEN SetFileDate(name,info.datestamp)
  720.  
  721. ENDPROC
  722. ->\\
  723.  
  724.  
  725. /* Compares the version-strings of two files
  726. ** Parameter:
  727. **   fromfile - full path of sourcefile
  728. **   tofile   - full path of destinationfile
  729. **
  730. ** Returns
  731. **   -1 if tofile-version>fromfile-version
  732. **    0 if               =
  733. **    1 if               <
  734. ** *AND* the contents of the sourcefile or NIL
  735. */
  736. PROC compareversion(fromfile,tofile) HANDLE
  737. ->// "compareversion()"
  738. DEF buffer=NIL:PTR TO CHAR,
  739.     version1,base1,
  740.     version2,base2
  741.  
  742.   version2,base2,buffer:=getVersionOfFile(tofile)
  743.   /* We dont need the destination contents. Therefore delete buffer
  744.   */
  745.   IF buffer
  746.     Dispose(buffer)
  747.     buffer:=NIL
  748.   ENDIF
  749.   /* if version is -1 then no version-string was found */
  750.   IF version2=-1 THEN RETURN 0,NIL
  751.  
  752.   version1,base1,buffer:=getVersionOfFile(fromfile)
  753.   IF version1=-1 THEN RETURN 0,buffer
  754.  
  755.   /* Compare the version, reversion of source/destionation
  756.   ** multiply with other base to get same number of signifant numbers
  757.   */
  758.   RETURN IF (version2*base1)>=(version1*base2) THEN -1 ELSE 1,buffer
  759.  
  760. EXCEPT -> Cleanup
  761.  
  762.   IF buffer THEN Dispose(buffer)
  763.   ReThrow()
  764.  
  765. ENDPROC
  766. ->\\
  767.  
  768.  
  769. /* Gets the version-number of a file
  770. ** Parameter:
  771. **   filename - full path of file
  772. ** Returns the version, base and a buffer with the contents of the file
  773. ** (description for version,base see getversion())
  774. ** buffer may be NIL if file was to large
  775. */
  776. PROC getVersionOfFile(filename) HANDLE
  777. ->// "getVersionOfFile()"
  778. DEF fh=NIL,
  779.     buffer=NIL:PTR TO CHAR,
  780.     version,base,
  781.     steplength,filelength,actlength
  782.  
  783.   IF fh:=Open(filename,OLDFILE)
  784.  
  785.     filelength:=FileLength(filename)
  786.     IF filelength>BIGFILEMEM
  787.       /* Files >BIGFILESIZE are not read completly into memory but in parts
  788.       ** of BIGFILESIZE Bytes.
  789.       ** As we may skip a versionstring the last SAFETYBYTES are copied
  790.       ** to start of new BIGFILESIZE block everytime.
  791.       */
  792.  
  793.       /* Alloc new buffer and read filecontents into it */
  794.       buffer:=NewR(BIGFILEMEM)
  795.       IF Fread(fh,buffer,BIGFILEMEM,1)<1 THEN Raise("anly")
  796.  
  797.       /* steplength is the length of the current block to be read */
  798.       steplength:=BIGFILESIZE
  799.       /* actlength is the position within the file */
  800.       actlength:=BIGFILEMEM
  801.  
  802.       REPEAT
  803.  
  804.         /* Get version. If version=-1 then read next block */
  805.         version,base:=getversion(buffer,steplength+SAFETYBYTES)
  806.         IF version=-1
  807.  
  808.           /* The version-string was maybe at the end of buffer
  809.           ** and therfore skipped. Copy the last SAFETYBYTES bytes
  810.           ** to start of buffer to not lose the version-string.
  811.           */
  812.           CopyMem(buffer+steplength,buffer,SAFETYBYTES)
  813.  
  814.           /* Increase actlength. If actlength is greater than filelength
  815.           ** then set steplength to the number of bytes left.
  816.           */
  817.           actlength:=actlength+BIGFILESIZE
  818.           IF actlength>filelength
  819.             steplength:=filelength-(actlength-BIGFILESIZE)
  820.           ENDIF
  821.  
  822.           /* Read next block to buffer. If steplength<0 then EOF is reached */
  823.           IF steplength>0
  824.             IF Fread(fh,buffer+SAFETYBYTES,steplength,1)<1 THEN Raise("anly")
  825.           ENDIF
  826.  
  827.         ENDIF
  828.  
  829.         /* Read until version-string was found or EOF */
  830.       UNTIL (version<>-1) OR (steplength<=0)
  831.  
  832.       Dispose(buffer)
  833.       buffer:=NIL
  834.  
  835.     ELSE
  836.       /* We have got a small file. Load it completly into memory.
  837.       ** Alloc new buffer and read filecontents into it
  838.       */
  839.       buffer:=NewR(filelength)
  840.       IF Fread(fh,buffer,filelength,1)<1 THEN Raise("anly")
  841.  
  842.       /* get versions and reversion of sourcefile */
  843.       version,base:=getversion(buffer,filelength)
  844.     ENDIF
  845.  
  846.     /* We dont need the filehandle any longer */
  847.     Close(fh)
  848.     fh:=NIL
  849.  
  850.   ELSE
  851.     Raise("open")
  852.   ENDIF
  853.  
  854. EXCEPT
  855.   IF fh THEN Close(fh)
  856.   IF buffer THEN Dispose(buffer)
  857.   ReThrow()
  858.  
  859. ENDPROC version,base,buffer
  860. ->\\
  861.  
  862.  
  863. /* Search for a version-string in a file
  864. ** Parameter:
  865. **   buffer       - Contents of file
  866. **   bufferlength - length of buffer in bytes
  867. ** Returns version,base
  868. ** if no version-string was found then -1 is returned as version
  869. ** Example: version=81259, base=10000 means
  870. **          Versionnumber is 81259/10000=8.1259
  871. */
  872. PROC getversion(buffer:PTR TO CHAR,bufferlength)
  873. ->// "getversion()"
  874. DEF version=-1:REG,base:REG
  875.  
  876.       MOVEA.L buffer,A0           -> A0..buffer
  877.       MOVE.L  bufferlength,D0     -> D0..bufferlength
  878.       SUBQ.L  #1,D0
  879.       MOVE.B  #"$",D1
  880.       MOVE.L  #"VER:",D2
  881. gv_search_loop:
  882.       SUBQ.L  #1,D0               
  883.       BLT.W   gv_ende             -> bufferend reached ?
  884.       CMP.B   (A0)+,D1
  885.       BNE.S   gv_search_loop      -> Found a "$" ?
  886.       CMP.L   (A0),D2             -> Yes, then next characters="VER:"
  887.       BNE.S   gv_search_loop
  888.  
  889.       SUBQ.L  #4,D0               -> We have found a version-string
  890.       BLT.S   gv_ende
  891.       ADDQ.L  #4,A0               -> skip "VER:"
  892.  
  893.       MOVE.B  #" ",D1
  894. gv_skipspaces1:                   -> skip all spaces before programname
  895.       SUBQ.L  #1,D0
  896.       BLT.S   gv_ende             -> bufferend reached ?
  897.       CMP.B   (A0)+,D1
  898.       BEQ.S   gv_skipspaces1
  899.       ADDQ.L  #1,D0               -> all spaces skipped; we have gone
  900.       SUBQ.L  #1,A0               -> one step too far.
  901.  
  902. gv_skipname:                      -> skip programname
  903.       SUBQ.L  #1,D0
  904.       BLT.S   gv_ende             -> bufferend reached ?
  905.       CMP.B   (A0)+,D1
  906.       BNE.S   gv_skipname
  907.       ADDQ.L  #1,D0               -> go one step back
  908.       SUBQ.L  #1,A0
  909.  
  910. gv_skipspaces2:                   -> skip spaces before version-number
  911.       SUBQ.L  #1,D0
  912.       BLT.S   gv_ende             -> bufferend reached ?
  913.       CMP.B   (A0)+,D1
  914.       BEQ.S   gv_skipspaces2
  915.       ADDQ.L  #1,D0               -> go one step back
  916.       SUBQ.L  #1,A0
  917.  
  918.       MOVEQ   #0,version
  919.       MOVE.B  #".",D1
  920.       MOVEQ   #0,D2
  921. gv_getversion1:                   -> get version until we found a "."
  922.       SUBQ.L  #1,D0
  923.       BLT.S   gv_ende             -> bufferend reached ?
  924.       MOVE.B  (A0)+,D2
  925.       SUBI.L  #"0",D2             -> Transform character "0".."9" into number
  926.       MULU.W  #10,version         -> version:=version*10
  927.       ADD.L   D2,version          ->                    +number
  928.       CMP.B   (A0),D1
  929.       BNE.S   gv_getversion1
  930.       SUBQ.L  #1,D0               -> skip "."
  931.       ADDQ.L  #1,A0
  932.  
  933.       MOVE.B  #" ",D1
  934.       MOVEQ   #1,base
  935. gv_getversion2:                   -> get reversion
  936.       SUBQ.L  #1,D0
  937.       BLT.S   gv_ende             -> bufferend reached ?
  938.       MOVE.B  (A0)+,D2
  939.       SUBI.L  #"0",D2             -> Transform character "0".."9" into number
  940.       MULU.W  #10,version         -> version:=version*10
  941.       MULU.W  #10,base            -> base:=base*10
  942.       ADD.L   D2,version          ->                    +number
  943.       CMP.B   (A0),D1
  944.       BNE.S   gv_getversion2
  945.  
  946. gv_ende:
  947.  
  948. ENDPROC version,base
  949. ->\\
  950.  
  951.