home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 14 / AACD14.ISO / CDTools / VersionCopy / VersionCopy.c < prev    next >
C/C++ Source or Header  |  2000-08-03  |  26KB  |  625 lines

  1. //     ___       ___
  2. //   _/  /_______\  \_     ___ ___ __ _                       _ __ ___ ___
  3. //__//  / _______ \  \\___/                                               \___
  4. //_/ | '  \__ __/  ` | \_/     © Copyright 1999-2000, Christopher Page     \__
  5. // \ | |    | |__  | | / \   Released as Free Software under the GNU GPL   /
  6. //  >| .    |  _/  . |<   >--- --- -- -                       - -- --- ---<
  7. // / \  \   | |   /  / \ /   This file is part of the VersionCopy source   \
  8. // \  \  \_/   \_/  /  / \  and it is released under the GNU GPL. Please   /
  9. //  \  \           /  /   \   read the "COPYING" file which should have   /
  10. // //\  \_________/  /\\ //\    been included in the distribution arc.   /
  11. //- --\   _______   /-- - --\      for full details of the license      /-----
  12. //-----\_/       \_/---------\   ___________________________________   /------
  13. //                            \_/                                   \_/
  14. //
  15. // Description:
  16. //
  17. //  VersionCopy version aware copy command
  18. //
  19. // Functions:
  20. //
  21. //  BOOL IsDirectory        (STRPTR TestName)
  22. //  STRPTR FindSubBuffer    (UBYTE *Source, UBYTE * Pattern, LONG SourceLen, LONG PatternLen)
  23. //  VersionData *FindVersion(BPTR Source, struct FileInfoBlock *SourceData)
  24. //  void FreeVersion        (struct VersionData *OldData)
  25. //  LONG FileCopy           (STRPTR SourceName, STRPTR TargetName, ULONG BufferSize)
  26. //  void CopyAttributes     (struct FileInfoBlock *SourceInfo, STRPTR TargetName)
  27. //  LONG VersionCopy        (struct FileInfoBlock *SourceFIB, struct FileInfoBlock *TargetFIB, STRPTR SourceName, STRPTR TargetName, ULONG BufferSize)
  28. //
  29. // Detail:
  30. //
  31. //  VersionCopy is a cut-down "Copy" command which is aware of the version information
  32. //  within the file being copied. Before copying the source file it checks whether the
  33. //  destination already exists and, if it does, the version numbers of the source and
  34. //  destination are compared. The source is only copied over the destination if the
  35. //  source version or revision is greater than that of the destination. Unlike Copy,
  36. //  wildcards and directory copy are not supported - indeed these facilities would be
  37. //  somewhat meaningless for the applications this command is intended.
  38. //
  39. // Fold Markers:
  40. //
  41. //  Start: /*GFS*/
  42. //    End: /*GFE*/
  43.  
  44. #include<exec/exec.h>
  45. #include<dos/dos.h>
  46.  
  47. #include<clib/exec_protos.h>
  48. #include<clib/dos_protos.h>
  49.  
  50. #include<ctype.h>
  51. #include<stdio.h>
  52. #include<stdlib.h>
  53. #include<string.h>
  54.  
  55. #include"VersionCopy_rev.h"
  56.  
  57. const char Copyright[] = "© copyright 2000 Chris Page";
  58. const char Version[]   = VERSTAG;
  59.  
  60. // Typing saver! :)
  61. #define ShowMessage(TextData) if(!((BOOL)ArgResult[OPT_QUIET])) printf("%s\n", TextData)
  62.  
  63. // Stuff for ReadArgs()
  64. #define ARGS_TEMPLATE  "FROM/A,TO/A,QUIET/S,BUF=BUFFER/K/N,CLONE/S,DATE/S,COM/S,NOPRO/S,IGNORENAME/S"
  65. #define OPT_FROM       0
  66. #define OPT_TO         1
  67. #define OPT_QUIET      2
  68. #define OPT_BUFFER     3
  69. #define OPT_CLONE      4
  70. #define OPT_DATE       5
  71. #define OPT_COM        6
  72. #define OPT_NOPRO      7
  73. #define OPT_IGNORE     8
  74. #define OPT_COUNT      9
  75.  
  76. // Note this does not consider dates!
  77. struct VersionData
  78. {
  79.     STRPTR Name    ; // Name given after $VER:, NULL if not found
  80.     LONG   Version ; // Version number
  81.     LONG   Revision; // Revision, 0 if no revision given.
  82. };
  83.  
  84. // Prototype type things..
  85.        BOOL IsDirectory        (STRPTR TestName);
  86.        STRPTR FindSubBuffer    (UBYTE *Source, UBYTE * Pattern, LONG SourceLen, LONG PatternLen);
  87. struct VersionData *FindVersion(BPTR Source, struct FileInfoBlock *SourceData);
  88.        void FreeVersion        (struct VersionData *OldData);
  89.        LONG FileCopy           (STRPTR SourceName, STRPTR TargetName, ULONG BufferSize);
  90.        void CopyAttributes     (struct FileInfoBlock *SourceInfo, STRPTR TargetName);
  91.        LONG VersionCopy        (struct FileInfoBlock *SourceFIB, struct FileInfoBlock *TargetFIB, STRPTR SourceName, STRPTR TargetName, ULONG BufferSize);
  92.  
  93.  
  94. // Globals...
  95. char  WorkBuffer[80]       ;  // Yeah, this is nasty but it saves some messing about later
  96. ULONG ArgResult [OPT_COUNT];  // ReadArgs store.
  97.  
  98.  
  99. /* BOOL IsDirectory(STRPTR)                                                  */
  100. /* -=-=-=-=-=-=-=-=-=-=-=-=                                                  */
  101. /* If the specified filename is actually a directory this returns TRUE.      */
  102. /*                                                                           */
  103. /* Parameters:                                                               */
  104. /*   TestName       Name of the object to identify.                          */
  105.  
  106. /*GFS*/  BOOL IsDirectory(STRPTR TestName)
  107. {
  108.     struct FileInfoBlock *TestFIB ;
  109.            BPTR           TestLock;
  110.            BOOL           IsDir   = TRUE;
  111.  
  112.     if(TestFIB = AllocDosObject(DOS_FIB, NULL)) {
  113.         if(TestLock = Lock(TestName, SHARED_LOCK)) {
  114.             Examine(TestLock, TestFIB);
  115.  
  116.             IsDir = (TestFIB -> fib_DirEntryType >= 0);
  117.  
  118.             UnLock(TestLock);
  119.         } else {
  120.             IsDir = FALSE;
  121.         }
  122.         FreeDosObject(DOS_FIB, TestFIB);
  123.     }
  124.  
  125.     return(IsDir);
  126. }/*GFE*/
  127.  
  128.  
  129. /* STRPTR FindSubBuffer(UBYTE *, UBYTE *, LONG, LONG)                        */
  130. /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=                        */
  131. /* This is basically a version of strstr which will work for non-string data */
  132. /* (ie: buffers containing arbitrary data, not just NULL-terminated strings) */
  133. /*                                                                           */
  134. /* Parameters:                                                               */
  135. /*     Source       Buffer to scan                                           */
  136. /*    Pattern       Pattern to find in Source                                */
  137. /*  SourceLen       Length of the source buffer in bytes                     */
  138. /* PatternLen       Length of the pattern in bytes                           */
  139.  
  140. /*GFS*/  STRPTR FindSubBuffer(UBYTE *Source, UBYTE * Pattern, LONG SourceLen, LONG PatternLen)
  141. {
  142.     LONG SourcePos = 0;
  143.  
  144.     while(SourcePos <= SourceLen) {
  145.         if(!memcmp(&Source[SourcePos], Pattern, PatternLen)) {
  146.             return(&Source[SourcePos]);
  147.         } else {
  148.             SourcePos ++;
  149.         }
  150.     }
  151.  
  152.     return(NULL);
  153. }/*GFE*/
  154.  
  155.  
  156. /* VersionData *FindVersion(BPTR, FileInfoBlock *)                           */
  157. /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-                           */
  158. /* This routine loads the file specified by the provided file handle into a  */
  159. /* memory buffer and searches it for the $VER: string that identifies the    */
  160. /* start of an Amiga version string. If it finds this the name, version and  */
  161. /* revision are copied into a VersionData structure and returned. Use FreeVec*/
  162. /* to release the name buffer and VersionData structure returned by this     */
  163. /* If this returns NULL then IoErr() will contain a code indicating why      */
  164. /*                                                                           */
  165. /* Parameters:                                                               */
  166. /*     Source       File handle for the file to load                         */
  167. /* SourceData       FIB filled in by calling ExamineFH() on the source file  */
  168.  
  169. /*GFS*/  struct VersionData *FindVersion(BPTR Source, struct FileInfoBlock *SourceData)
  170. {
  171.     struct VersionData *NewData;
  172.            STRPTR       Buffer ;
  173.            STRPTR       Search ;
  174.            STRPTR       Start  ;
  175.            BOOL         Spaced    = FALSE;
  176.            BOOL         Complete  = FALSE;
  177.            LONG         ErrorCode = 0;
  178.  
  179.            // This is a hack to get around vbcc not putting the Version string near
  180.            // the start of the file. If FindBuffer[] = "$VER:" then Version VersionCopy
  181.            // gets confused between the $VER: for FindBuffer and the one for the version
  182.  
  183.            char         FindBuffer[] = " VER:";
  184.            FindBuffer[0] = '$';
  185.  
  186.     if(Buffer = AllocVec(SourceData -> fib_Size, MEMF_ANY)) {
  187.         Read(Source, Buffer, SourceData -> fib_Size);
  188.  
  189.         Search = FindSubBuffer(Buffer, FindBuffer, SourceData -> fib_Size - 6, 6);
  190.  
  191.         if(Search) {
  192.             // Skip the version header
  193.             Search += 6;
  194.  
  195.             // Skip any leading whitespace
  196.             while(*Search && (*Search == ' ')) Search++;
  197.  
  198.             // Remember the start location
  199.             Start = Search;
  200.  
  201.             // Look for the first digit after a space
  202.             do {
  203.                 if(isdigit(*Search) && Spaced) {
  204.                     Complete = TRUE;
  205.                 } else {
  206.                     Spaced = (*Search == ' ');
  207.                     Search++;
  208.                 }
  209.             } while(!Complete && *Search);
  210.  
  211.             if(NewData = AllocVec(sizeof(struct VersionData), MEMF_ANY|MEMF_CLEAR)) {
  212.  
  213.                 // Allocate space for the name and copy it.
  214.                 if(NewData -> Name = AllocVec(Search - Start, MEMF_ANY|MEMF_CLEAR)) {
  215.                     CopyMem(Start, NewData -> Name, Search - Start - 1);
  216.  
  217.                     // got a version number?
  218.                     if(Complete) {
  219.                         NewData -> Version = atol(Search);
  220.  
  221.                         // Try to locate the revision
  222.                         while(*Search && (*Search != '.')) Search++;
  223.  
  224.                         if(*Search == '.') {
  225.                             Search++;
  226.                             Start = Search;
  227.  
  228.                             // whack a NULL in at the cirst non-digit encountered
  229.                             while(isdigit(*Search)) Search++;
  230.                             *Search = '\0';
  231.  
  232.                             NewData -> Revision = atol(Start);
  233.                         }
  234.  
  235.                         FreeVec(Buffer);
  236.                         SetIoErr(0);
  237.                         return(NewData);
  238.                     }
  239.                 } else {
  240.                     ErrorCode = ERROR_NO_FREE_STORE;
  241.                     ShowMessage("Unable to allocate version name buffer");
  242.                 }
  243.                 FreeVec(NewData);
  244.             } else {
  245.                 ErrorCode = ERROR_NO_FREE_STORE;
  246.                 ShowMessage("Unable to allocate version data");
  247.             }
  248.         } else {
  249.             ErrorCode = ERROR_OBJECT_WRONG_TYPE;
  250.             if(!((BOOL)ArgResult[OPT_QUIET])) printf("%s does not contain version information\n", SourceData -> fib_FileName);
  251.         }
  252.  
  253.         FreeVec(Buffer);
  254.     } else {
  255.         ErrorCode = ERROR_NO_FREE_STORE;
  256.         ShowMessage("Unable to allocate search buffer");
  257.     }
  258.  
  259.     SetIoErr(ErrorCode);
  260.     return(NULL);
  261. }/*GFE*/
  262.  
  263.  
  264. /* void FreeVersion(VersionData *)                                           */
  265. /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-                                           */
  266. /* Small routine to free the specified VersionData structure - if the pointer*/
  267. /* is not NULL then the Name field is freed (if required) and the the struct */
  268. /* itself is released.                                                       */
  269. /*                                                                           */
  270. /* Parameters:                                                               */
  271. /*    OldData       The VersionData structure to free.                       */
  272.  
  273. /*GFS*/  void FreeVersion(struct VersionData *OldData)
  274. {
  275.     if(OldData) {
  276.         if(OldData -> Name) FreeVec(OldData -> Name);
  277.         FreeVec(OldData);
  278.     }
  279. }/*GFE*/
  280.  
  281.  
  282. /* LONG FileCopy(STRPTR, STRPTR, ULONG)                                      */
  283. /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=                                      */
  284. /* This does the actual duplication of the source file, copying BufferSize   */
  285. /* chunks of the file until the whole file is copied or the user does a      */
  286. /* CTRL-C to abort it the copy. Errors in reading and writing are detected.  */
  287. /*                                                                           */
  288. /* Parameters:                                                               */
  289. /* SourceName       Name of the file to copy                                 */
  290. /* TargetName       Name of the destination file                             */
  291. /* BufferSize       Size of the copy buffer in bytes.                        */
  292.  
  293. /*GFS*/  LONG FileCopy(STRPTR SourceName, STRPTR TargetName, ULONG BufferSize)
  294. {
  295.     BPTR   SourceFile, DestFile    ;
  296.     UBYTE *CopyBuffer              ;
  297.     LONG   CopyLength              ;
  298.     LONG   WriteLength             ;
  299.     LONG   ReturnCode = RETURN_WARN;
  300.  
  301.     if(CopyBuffer = (UBYTE *)AllocVec(BufferSize, MEMF_ANY)) {
  302.         if(SourceFile = Open(SourceName, MODE_OLDFILE)) {
  303.             if(DestFile = Open(TargetName, MODE_NEWFILE)) {
  304.  
  305.                 // Read & write BufferSize chunks of the file, allowing user abort and
  306.                 // filesystem full handling..
  307.                 do {
  308.                     WriteLength = 0;
  309.                     if(CopyLength  = Read(SourceFile, CopyBuffer, BufferSize)) {
  310.  
  311.                         // Allow user abort..
  312.                         if(!(SetSignal(0, 0) & SIGBREAKF_CTRL_C)) {
  313.                             WriteLength = Write(DestFile  , CopyBuffer, CopyLength);
  314.                         }
  315.                     }
  316.                 } while((CopyLength > 0) && (WriteLength == CopyLength));
  317.  
  318.                 // should catch the last error
  319.                 if((CopyLength < 0) || (WriteLength != CopyLength)) ReturnCode = IoErr();
  320.                 Close(DestFile);
  321.  
  322.                 // Failed to write the last read, erase destination..
  323.                 if((WriteLength != CopyLength) && (CopyLength > 0)) {
  324.                     DeleteFile(TargetName);
  325.  
  326.                     if(ReturnCode && !(BOOL)ArgResult[OPT_QUIET]) {
  327.                         Fault(ReturnCode, NULL, WorkBuffer, 80);
  328.                         printf("Unable to write %s: %s\n", TargetName, WorkBuffer);
  329.                     }
  330.  
  331.                     ShowMessage("Removed incomplete file...\n");
  332.                 }
  333.             } else {
  334.                 ReturnCode = IoErr();
  335.  
  336.                 if(!((BOOL)ArgResult[OPT_QUIET])) {
  337.                     Fault(ReturnCode, NULL, WorkBuffer, 80);
  338.                     printf("Unable to open %s for writing: %s\n", TargetName, WorkBuffer);
  339.                 }
  340.             }
  341.  
  342.             Close(SourceFile);
  343.         // This one shouldn't really happen but anyway....
  344.         } else {
  345.             ReturnCode = IoErr();
  346.  
  347.             if(!((BOOL)ArgResult[OPT_QUIET])) {
  348.                 Fault(ReturnCode, NULL, WorkBuffer, 80);
  349.                 printf("Unable to open %s for reading: %s\n", TargetName, WorkBuffer);
  350.             }
  351.         }
  352.  
  353.         FreeVec(CopyBuffer);
  354.     } else {
  355.         ReturnCode = ERROR_NO_FREE_STORE;
  356.         ShowMessage("Unable to allocate the copy buffer");
  357.     }
  358.  
  359.     return(ReturnCode);
  360. }/*GFE*/
  361.  
  362.  
  363. /* void CopyAttributes(FileInfoblock *, STRPTR)                              */
  364. /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=                              */
  365. /* If the file has been copied then some attributes of the source may need   */
  366. /* to be copied to the target: if the clone has been activated then all the  */
  367. /* file attributes of the source need to be copied, otherwise the only attr. */
  368. /* copied by default is the protection bits - this cane be disabled with a   */
  369. /* flag and other attributes can be activated with the DATE and COM args..   */
  370. /*                                                                           */
  371. /* Parameters:                                                               */
  372. /* SourceInfo       FileInfoBlock containing the source attributes           */
  373. /* TargetName       Name of the target file (must exist!!)                   */
  374.  
  375. /*GFS*/  void CopyAttributes(struct FileInfoBlock *SourceInfo, STRPTR TargetName)
  376. {
  377.     if((BOOL)ArgResult[OPT_CLONE]) {
  378.         SetComment   (TargetName, SourceInfo -> fib_Comment);
  379.         SetFileDate  (TargetName, &SourceInfo -> fib_Date);
  380.         SetProtection(TargetName, SourceInfo -> fib_Protection);
  381.     } else {
  382.         if((BOOL)ArgResult[OPT_COM] )     SetComment   (TargetName, SourceInfo -> fib_Comment);
  383.         if((BOOL)ArgResult[OPT_DATE])     SetFileDate  (TargetName, &SourceInfo -> fib_Date);
  384.         if(!((BOOL)ArgResult[OPT_NOPRO])) SetProtection(TargetName, SourceInfo -> fib_Protection);
  385.     }
  386. }/*GFE*/
  387.  
  388.  
  389. /* LONG VersionCopy(FileInfoBlock *, FileInfoBlock *, STRPTR, STRPTR, ULONG) */
  390. /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
  391. /* This checks the source and target versions and copies the source over the */
  392. /* target if the target version is lower than the source or the target has   */
  393. /* no version information.                                                   */
  394. /*                                                                           */
  395. /* Parameters:                                                               */
  396. /*  SourceFIB       FileInfoBlock containing the source attributes           */
  397. /*  TargetFIB       FileInfoBlock containing the target attributes           */
  398. /* SourceName       Filename of the source file                              */
  399. /* TargetName       Filename of the target                                   */
  400. /* BufferSize       Size of the copy buffer in bytes                         */
  401.  
  402. /*GFS*/  LONG VersionCopy(struct FileInfoBlock *SourceFIB, struct FileInfoBlock *TargetFIB, STRPTR SourceName, STRPTR TargetName, ULONG BufferSize)
  403. {
  404.     struct VersionData   *SourceVers;
  405.     struct VersionData   *TargetVers;
  406.            BPTR           SourceFile;
  407.            BPTR           TargetFile;
  408.            LONG           ErrorCode = RETURN_WARN;
  409.  
  410.     // Open the source and get it's version - these must both succeed..
  411.     if(SourceFile = Open(SourceName, MODE_OLDFILE)) {
  412.         if(SourceVers = FindVersion(SourceFile, SourceFIB)) {
  413.  
  414.             // Open the target & get version info if possible...
  415.             if(TargetFile = Open(TargetName, MODE_OLDFILE)) {
  416.                 if(TargetVers = FindVersion(TargetFile, TargetFIB)) {
  417.  
  418.                     // Don't need these any more...
  419.                     Close(SourceFile);
  420.                     Close(TargetFile);
  421.                     SourceFile = 0L;
  422.  
  423.                     // If the names match, or ignore has been set, then pass through..
  424.                     if((BOOL)ArgResult[OPT_IGNORE] || !strcmp(SourceVers -> Name, TargetVers -> Name)) {
  425.  
  426.                         // Check the version and revision
  427.                         if((SourceVers -> Version > TargetVers -> Version) ||
  428.                            ((SourceVers -> Version == TargetVers -> Version) &&
  429.                             (SourceVers -> Revision > TargetVers -> Revision))) {
  430.  
  431.                             // Source is newer - copy it.
  432.                             ErrorCode = FileCopy(SourceName, TargetName, BufferSize);
  433.                         } else {
  434.  
  435.                             // Neil says he wants an OK for this case...
  436.                             ErrorCode = RETURN_OK;
  437.                             ShowMessage("Target is up to date");
  438.                         }
  439.                     } else {
  440.                         ErrorCode = ERROR_OBJECT_WRONG_TYPE;
  441.                         ShowMessage("Version names in source and target do not match");
  442.                     }
  443.  
  444.                     FreeVersion(TargetVers);
  445.  
  446.                 // No version info in the file.. just write over it.
  447.                 } else {
  448.  
  449.                     // Don't need these now..
  450.                     Close(TargetFile);
  451.                     Close(SourceFile);
  452.                     SourceFile = 0L;
  453.  
  454.                     // Target doesn't contain any version, straight copy
  455.                     ErrorCode = FileCopy((STRPTR)ArgResult[OPT_FROM], TargetName, BufferSize);
  456.  
  457.                     // If the copy was successful then copy the file attributes
  458.                     if(ErrorCode == RETURN_WARN) CopyAttributes(SourceFIB, TargetName);
  459.                 }
  460.             } else {
  461.  
  462.                 // Hmm.. opening the target failed. We know it exists (or we wouldn't be here)
  463.                 // but it won't open so something screwy is going on..
  464.                 ErrorCode = IoErr();
  465.                 if(!((BOOL)ArgResult[OPT_QUIET])) {
  466.                     Fault(ErrorCode, NULL, WorkBuffer, 80);
  467.                     printf("Unable to open target: %s\n", WorkBuffer);
  468.                 }
  469.             }
  470.  
  471.             FreeVersion(SourceVers);
  472.         } else {
  473.             ErrorCode = IoErr();
  474.         }
  475.  
  476.         if(SourceFile) Close(SourceFile);
  477.     } else {
  478.         ErrorCode = IoErr();
  479.         if(!((BOOL)ArgResult[OPT_QUIET])) {
  480.             Fault(ErrorCode, NULL, WorkBuffer, 80);
  481.             printf("Unable to open source: %s\n", WorkBuffer);
  482.         }
  483.     }
  484.  
  485.     return(ErrorCode);
  486. }/*GFE*/
  487.  
  488.  
  489. int main(int argc, char **argv)
  490. {
  491.     struct RDArgs        *ArgsData  ;
  492.     struct FileInfoBlock *SourceFIB ;
  493.     struct FileInfoBlock *TargetFIB ;
  494.            BPTR           SourceLock;
  495.            BPTR           TargetLock;
  496.            STRPTR         TargetName;
  497.            STRPTR         TempString;
  498.            BOOL           FreeTarget = FALSE;
  499.            ULONG          TargetSize = 0L;
  500.            ULONG          BufferSize = 0L;
  501.  
  502.            LONG           ErrorCode  = 0L;
  503.  
  504.     // Can't do anything if the user stuffs up the command line...
  505.     if(ArgsData = ReadArgs(ARGS_TEMPLATE, ArgResult, NULL)) {
  506.  
  507.         // Work out how big the copy buffer should be
  508.         if(ArgResult[OPT_BUFFER]) {
  509.             BufferSize = (*(ULONG *)ArgResult[OPT_BUFFER]) * 512;
  510.         }
  511.  
  512.         if(BufferSize == 0) BufferSize = 102400;
  513.  
  514.         // Allocate the source fib, lock the source and examine it
  515.         if(SourceFIB = AllocDosObject(DOS_FIB, NULL)) {
  516.             if(SourceLock = Lock((STRPTR)ArgResult[OPT_FROM], SHARED_LOCK)) {
  517.                 Examine(SourceLock, SourceFIB);
  518.  
  519.                 // Only continue if the source is a file (no directory copy in this version...)
  520.                 if(SourceFIB -> fib_DirEntryType < 0) {
  521.  
  522.                     // Quick hack to make sure we have a valid destination name..
  523.                     if(strlen((STRPTR)ArgResult[OPT_TO]) == 0) {
  524.                         TargetName = FilePart((STRPTR)ArgResult[OPT_FROM]);
  525.                     } else {
  526.                         // User has specified a destination (ie: not "") but is it a dir?
  527.                         TargetName = (STRPTR)ArgResult[OPT_TO];
  528.  
  529.                         // If the target is a directory we need to add the filename...
  530.                         if(IsDirectory(TargetName)) {
  531.                             TempString = FilePart((STRPTR)ArgResult[OPT_FROM]);
  532.                             TargetSize = strlen(TargetName) + strlen(TempString) + 3;
  533.  
  534.                             // Allocate space for the new name
  535.                             if(TargetName = AllocVec(TargetSize, MEMF_ANY)) {
  536.                                 strcpy(TargetName, (STRPTR)ArgResult[OPT_TO]);
  537.                                 AddPart(TargetName, TempString, TargetSize);
  538.                                 FreeTarget = TRUE;
  539.                             } else {
  540.                                 ErrorCode = ERROR_NO_FREE_STORE;
  541.                                 ShowMessage("Unable to allocate target name buffer");
  542.                             }
  543.                         }
  544.                     }
  545.  
  546.                     if(TargetName) {
  547.  
  548.                         // Alloc fib, lock and examine as usual...
  549.                         if(TargetFIB = AllocDosObject(DOS_FIB, NULL)) {
  550.                             if(TargetLock = Lock(TargetName, SHARED_LOCK)) {
  551.                                 Examine(TargetLock, TargetFIB);
  552.  
  553.                                 // Don't need these no more...
  554.                                 UnLock(TargetLock);
  555.                                 UnLock(SourceLock);
  556.                                 SourceLock = 0L;
  557.  
  558.                                 // Copy only if the version wills it..
  559.                                 ErrorCode = VersionCopy(SourceFIB, TargetFIB, (STRPTR)ArgResult[OPT_FROM], TargetName, BufferSize);
  560.  
  561.                                 // If the copy was successful then copy the file attributes
  562.                                 if(ErrorCode == RETURN_WARN) CopyAttributes(SourceFIB, TargetName);
  563.  
  564.                             } else {
  565.                                 ErrorCode = IoErr();
  566.  
  567.                                 // Can't lock the target - is it just that it doesn't exist....
  568.                                 if(ErrorCode == ERROR_OBJECT_NOT_FOUND) {
  569.  
  570.                                     // Target doesn't exist, straight copy
  571.                                     ErrorCode = FileCopy((STRPTR)ArgResult[OPT_FROM], TargetName, BufferSize);
  572.  
  573.                                     // If the copy was successful then copy the file attributes
  574.                                     if(ErrorCode == RETURN_WARN) CopyAttributes(SourceFIB, TargetName);
  575.  
  576.                                 } else {
  577.  
  578.                                     // .... or is it something worse?
  579.                                     if(!((BOOL)ArgResult[OPT_QUIET])) {
  580.                                         Fault(ErrorCode, NULL, WorkBuffer, 80);
  581.                                         printf("Target not locked: (%ld) %s\n", ErrorCode, WorkBuffer);
  582.                                     }
  583.                                 }
  584.                             }
  585.                             FreeDosObject(DOS_FIB, TargetFIB);
  586.                         }
  587.  
  588.                         if(FreeTarget) FreeVec(TargetName);
  589.                     }
  590.                 } else {
  591.                     ErrorCode = ERROR_OBJECT_WRONG_TYPE;
  592.                     ShowMessage("The source is not a file");
  593.                 }
  594.  
  595.                 if(SourceLock) UnLock(SourceLock);
  596.             } else {
  597.                 ErrorCode = IoErr();
  598.                 if(!((BOOL)ArgResult[OPT_QUIET])) {
  599.                     Fault(ErrorCode, NULL, WorkBuffer, 80);
  600.                     printf("Unable to open %s: %s\n", (STRPTR)ArgResult[OPT_FROM], WorkBuffer);
  601.                 }
  602.             }
  603.  
  604.             FreeDosObject(DOS_FIB, SourceFIB);
  605.         } else {
  606.             ErrorCode = ERROR_NO_FREE_STORE;
  607.             ShowMessage("Unable to allocate source FIB\n");
  608.         }
  609.  
  610.         FreeArgs(ArgsData);
  611.     } else {
  612.         printf("%s: required argument missing\n", argv[0]);
  613.         ErrorCode = ERROR_REQUIRED_ARG_MISSING;
  614.     }
  615.  
  616.     exit(ErrorCode);
  617. }
  618.  
  619.  
  620.  
  621.  
  622.  
  623.  
  624.  
  625.