home *** CD-ROM | disk | FTP | other *** search
/ The Developer Connection…ice Driver Kit for OS/2 3 / DEV3-D1.ISO / source / util2src / ccp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-09-28  |  37.8 KB  |  871 lines

  1. /*============================================================================*
  2.  * main() module: ccp.c - Conditional copy command - OS/2 2.0 version.
  3.  *
  4.  * (C)Copyright IBM Corporation, 1991, 1992, 1993         Brian E. Yoder
  5.  *
  6.  * This program conditionally copies (based on size, modification time
  7.  * and date, and an optional set of flags) source files to a target
  8.  * directory.  See ccp.doc for more information.
  9.  *
  10.  * This DOS version of the program allocates a buffer to use during a file
  11.  * copy.  The OS/2 version uses the DosCopy() subroutine, which (I suppose)
  12.  * supplies its own copy buffer.
  13.  *
  14.  * 05/11/91 - Created, using crc.c as a template.
  15.  * 05/19/91 - Version 1.0 - Initial DOS version completed.
  16.  * 05/20/91 - Version 1.0 - Ported to initial OS/2 version.  The fcopy()
  17.  *            subroutine in this module uses DosCopy() to copy a file.
  18.  *            Also, the OS/2 file information structure defines the time
  19.  *            and date as a bit record and not as a ushort, so the Fdate and
  20.  *            Ftime fields must be handled differently than on DOS.
  21.  * 01/11/92 - Version 1.1 - Add support for -f flag: force the target to
  22.  *            be overwritten if it is readonly.
  23.  * 03/26/92 - Version 1.2 - If there's only one source file specification,
  24.  *            the target directory defaults to . (the current directory).
  25.  * 07/24/92 - Version 1.2 - For OS/2 2.0 and C Set/2.
  26.  * 09/21/92 - Version 1.3 - Supports the -s flag to copy subdirectories.
  27.  *            Also supports the -t flag to list the target as well as source.
  28.  * 11/11/92 - Call the 16-bit DosFindFirst/Next DOS16FIND* APIs.
  29.  *            When scanning a directory on an HPFS drive, the 32-bit APIs
  30.  *            skip over any name that begins with: ! # $ % ( ) - + ,
  31.  * 09/28/93 - If copying from a non-HPFS (such as FAT) filesystem, force
  32.  *            the target name to be lowercase.
  33.  *============================================================================*/
  34.  
  35. static char ver[]  = "ccp: version 1.4";
  36. static char copr[] = "(c)Copyright IBM Corporation, 1993.  All rights reserved.";
  37.  
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <memory.h>
  41. #include <string.h>
  42. #include <malloc.h>
  43. #include <sys/types.h>
  44.  
  45. #include <sys/stat.h>               /* Used by open(), etc. */
  46. #include <fcntl.h>
  47. #include <io.h>
  48.  
  49. #include "util.h"
  50.  
  51. #define CCP_DIFFERENT 0             /* Copy if source is different than target */
  52. #define CCP_LATER     1             /* Copy if source is later than target */
  53.  
  54. #define CPE_FULL      1             /* Error codes from fcopy() */
  55. #define CPE_OTHER     2
  56. #define CPE_MKDIR     3             /* Error if we can't make a directory */
  57.  
  58. static USHORT attrib =              /* Set up attribute for findfirst */
  59.            _A_NORMAL  |
  60.            _A_RDONLY  |                   /* These labels are from <dos.h> */
  61.            _A_HIDDEN  |
  62.            _A_SYSTEM  |
  63.         /* _A_VOLID   |  */               /* OS/2 doesn't support _A_VOLID */
  64.            _A_SUBDIR  |
  65.            _A_ARCH;
  66.  
  67. /* Not needed if we're using DosCopy() */
  68. #define CPLEN_1  32000              /* Copy buffer length: 1st choice */
  69. #define CPLEN_2  2048               /*                     2nd choice */
  70.  
  71. #define XLEN     2048               /* Total length of compiled reg expressions */
  72. static  char     xsrcbuff[XLEN];    /* Buffer to hold compiled regular expressions */
  73.  
  74. #define ELEN     4096               /* Length of expression/filename buffer */
  75. static  char     buff[ELEN+1];      /* Buffer for output filenames and expansions */
  76.  
  77. /*============================================================================*
  78.  * Full prototypes for private (static) functions
  79.  *============================================================================*/
  80.  
  81. static void       syntax       ( void );
  82. static int        excludef     ( char *, int, char ** );
  83. static int        copyit       ( char *, ulong, ushort, ushort, ushort,
  84.                                  char *, int, int );
  85. static int        fcopy        ( char *, ushort, ushort, ushort, char *, int );
  86.  
  87. /*============================================================================*
  88.  * Main Program Entry Point
  89.  *============================================================================*/
  90.  
  91. main(argc, argv)
  92.  
  93. int argc;                           /* arg count */
  94. char *argv[];                       /* arg pointers */
  95.  
  96. {
  97.   int     rc;                       /* Return code storage */
  98.   char   *str;                      /* Pointer to string */
  99.   long    lrc;                      /* Long return code */
  100.   char   *flagstr;                  /* Pointer to string of flags */
  101.  
  102.   char   *tdir;                     /* Pointer to target directory name */
  103.   uint    tlen;                     /* Length of target directory name */
  104.  
  105.   int     srcc;                     /* Count and pointer to array of */
  106.   char  **srcv;                     /* source file specifications */
  107.  
  108.   int     xsrc;                     /* Count and pointer to array of */
  109.   char  **xsrv;                     /* xsource (exclusion) file specifications */
  110.   PATH_NAME *pn;                    /* Pointer returned by decompose() */
  111.  
  112.   int     cnt;                      /* General count */
  113.   char  **charv;                    /* General character pointer vector */
  114.   char   *next_eexpr;               /* Pointer returned by rexpand() */
  115.   char   *xsrccurr;                 /* Current pointer within xsrcbuff[] */
  116.   char   *xsrcnext;                 /* Pointer returned by rcompile() */
  117.  
  118.   int     uniq;                     /* 'uniq' flag for slmake() */
  119.   SLPATH *speclist;                 /* Pointer to linked list of path specs */
  120.   ushort  mflags;                   /* Match flags for slrewind() */
  121.   ushort  recurse;                  /* Recursive directory descent? */
  122.  
  123.   FINFO  *fdata;                    /* Pointers returned by slmatch() */
  124.   SLPATH *pdata;
  125.   SLNAME *ndata;
  126.  
  127.   char   *sourcename;               /* Pointers to name strings */
  128.   char   *targetname;
  129.   char   *basename;
  130.   uint    sourcelen;                /* And the lengths of these strings */
  131.   uint    targetlen;
  132.   uint    baselen;
  133.  
  134.   ushort  sdate;                    /* Source file's time/date */
  135.   ushort  stime;
  136.  
  137.   int     ccopy;                    /* Conditional copy: different/later/etc. */
  138.   int     mustexist;                /* Must target file exist before copying? */
  139.   int     copyfiles;                /* Actually perform the copy? TRUE/FALSE */
  140.   int     showx;                    /* Show names of files excluded (not copied)? */
  141.   int     force;                    /* Force readonly files to be overwritten? */
  142.   int     showtarget;               /* Show target as well as source? */
  143.  
  144.   ulong   copycnt = 0L;             /* No. of files copied */
  145.  
  146. /*----------------------------------------------------------------------------*
  147.  * Set initial values of flags:
  148.  *----------------------------------------------------------------------------*/
  149.  
  150.   uniq      = TRUE;                 /* Build SLPATHs only for unique paths */
  151.   recurse   = FALSE;                /* Don't descend subdirectories */
  152.   ccopy     = CCP_DIFFERENT;        /* Copy if source is different than target */
  153.   mustexist = FALSE;                /* Target file doesn't have to exist */
  154.   copyfiles = TRUE;                 /* Yes, we want to copy files */
  155.   showx     = FALSE;                /* Don't show names of excluded files */
  156.   force     = FALSE;                /* No, don't overwrite readonly target files */
  157.   showtarget= FALSE;                /* No, just show source filenames */
  158.  
  159. /*----------------------------------------------------------------------------*
  160.  * Be sure we have at least one argument:
  161.  *----------------------------------------------------------------------------*/
  162.  
  163.   argc--;                           /* Ignore 1st argument (program name) */
  164.   argv++;
  165.  
  166.   if (argc <= 0) syntax();          /* If no arguments: Display syntax */
  167.  
  168.  /*---------------------------------------------------------------------------*
  169.   * Process flags, if any
  170.   *---------------------------------------------------------------------------*/
  171.  
  172.   flagstr = *argv;                  /* Point 'flagstr' to argument */
  173.   if (*flagstr == '-')              /* If it begins with '-': It's a list of flags */
  174.   {
  175.      flagstr++;                          /* Point past the dash */
  176.  
  177.      while (*flagstr != '\0')            /* For each character in flag string: */
  178.      {
  179.         switch (*flagstr)
  180.         {
  181.            case 'l':
  182.            case 'L':
  183.               ccopy = CCP_LATER;              /* Copy if source is later than target */
  184.               break;
  185.  
  186.            case 'e':
  187.            case 'E':
  188.               mustexist = TRUE;               /* Target file must exist before copying */
  189.               break;
  190.  
  191.            case 'n':
  192.            case 'N':
  193.               copyfiles = FALSE;              /* Don't copy any files */
  194.               break;
  195.  
  196.            case 'x':
  197.            case 'X':
  198.               showx = TRUE;                   /* Show files excluded (not copied) */
  199.               break;
  200.  
  201.            case 'f':
  202.            case 'F':
  203.               force = TRUE;                   /* Force overwriting readonly files */
  204.               break;
  205.  
  206.            case 'g':                          /* 'g' means group in order that */
  207.            case 'G':                          /* filespecs were listed on cmd line */
  208.               uniq = FALSE;
  209.               break;
  210.  
  211.            case 's':                          /* 's' means recurse subdirectories */
  212.            case 'S':
  213.               recurse = TRUE;
  214.               break;
  215.  
  216.            case 't':                          /* 't' means show target filenames */
  217.            case 'T':
  218.               showtarget = TRUE;
  219.               break;
  220.  
  221.            default:
  222.               fprintf(stderr, "Invalid flag '%c'.  For help, enter command with no arguments.\n",
  223.                  *flagstr);
  224.               exit(2);
  225.               break;
  226.         }
  227.         flagstr++;                            /* Check next character */
  228.      }
  229.  
  230.      argc--;                             /* Done with flags: Discard them */
  231.      argv++;
  232.   }
  233.  
  234.  /*---------------------------------------------------------------------------*
  235.   * Store pointer to the name of the target directory, and its length
  236.   *---------------------------------------------------------------------------*/
  237.  
  238.   if (argc <= 0) syntax();          /* If no arguments: Display syntax */
  239.  
  240.   if (argc == 1)                    /* If only one argument: */
  241.   {                                      /* Assume it's a source filespec */
  242.      tdir = ".";                         /* Assume target is current directory */
  243.   }
  244.   else                              /* Else: last argument is target directory */
  245.   {
  246.      tdir = argv[argc-1];                /* Point to last argument: target dir */
  247.      argc--;                             /* Decrement arg count to exclude last arg */
  248.   }
  249.  
  250.   tlen = strlen(tdir);              /* Store length of target directory */
  251.  
  252.  /*---------------------------------------------------------------------------*
  253.   * Find out how many source file specifications we have (at least one needed)
  254.   *---------------------------------------------------------------------------*/
  255.  
  256.   srcc = 0;                         /* Initialize to no source specs */
  257.   srcv = argv;                      /* Point source spec vector to 1st arg */
  258.  
  259.   while (argc > 0)                  /* For each argument: */
  260.   {
  261.      if ((*argv)[0] == '!')             /* If we found a !: */
  262.         break;                               /* The break out of loop */
  263.      srcc++;                            /* We have a source spec: Update count */
  264.      argc--;                            /* Decrement no. of arguments left */
  265.      argv++;                            /* And point to next argument */
  266.   }
  267.  
  268.   if (srcc <= 0)                    /* If no source file specs: Error */
  269.   {
  270.      fprintf(stderr, "ccp: Source file specification(s) are missing.\n");
  271.      return(2);
  272.   }
  273.  
  274.  /*---------------------------------------------------------------------------*
  275.   * Find out how many xsource (exclusion) file specifications we have.
  276.   * If we broke out of the previous loop because of a !, then we may have
  277.   * xsource specs.  We allow the ! and the first xsource spec to either
  278.   * be part of the same argument or be in separate arguements (i.e. we
  279.   * allow: '!*.exe' and '! *.exe').
  280.   *---------------------------------------------------------------------------*/
  281.  
  282.   xsrc = 0;                         /* Assume no xsource specs, then: */
  283.  
  284.   if ( (argc >= 1) &&               /* If there's at least one arg left, */
  285.      ( (*argv)[0] == '!') )         /* and it is a !, then: */
  286.   {                                 /* We have xsource specifications: */
  287.      if ((*argv)[1] != '\0')             /* If ! is not by itself: */
  288.      {                                        /* Then rest of arg is an xsource */
  289.         xsrc = 1;                             /* So we have at least one xsource */
  290.         xsrv = argv;                          /* Point xsrv to current argv */
  291.         (*xsrv)++;                            /* And bump its pointer past the ! */
  292.      }
  293.      else                                /* Else: the ! is by itself: */
  294.         xsrv = argv + 1;                      /* xsrv vector starts at next arg */
  295.  
  296.      argc--;                             /* Decrement arg count */
  297.      argv++;                             /* And point to next argument */
  298.  
  299.      while (argc > 0)                    /* For each argument left: */
  300.      {
  301.         xsrc++;                               /* It's an xsource spec */
  302.         argc--;                               /* Decrement arg count */
  303.         argv++;                               /* And point to next argument */
  304.      }
  305.  
  306.      if (xsrc == 0)                      /* If we have no xsource (but found a !): */
  307.      {                                        /* Error! */
  308.         fprintf(stderr, "ccp: Found ! but there are no xsource specifications.\n");
  309.         return(2);
  310.      }
  311.   }
  312.  
  313.  /*---------------------------------------------------------------------------*
  314.   * TEST: Display source, xsource, and target specifications
  315.   *---------------------------------------------------------------------------*/
  316.  
  317. //printf("\n");
  318. //printf("Target directory: '%s'\n", tdir);
  319. //
  320. //printf("Source specs:\n");
  321. //cnt = srcc;
  322. //charv = srcv;
  323. //while (cnt > 0)
  324. //{
  325. //   printf("   '%s'\n", *charv);
  326. //   cnt--;
  327. //   charv++;
  328. //}
  329. //
  330. //printf("Xsource specs:\n");
  331. //cnt = xsrc;
  332. //charv = xsrv;
  333. //while (cnt > 0)
  334. //{
  335. //   printf("   '%s'\n", *charv);
  336. //   cnt--;
  337. //   charv++;
  338. //}
  339. //
  340. //printf("\n");
  341. //exit(0);
  342.  
  343.  /*---------------------------------------------------------------------------*
  344.   * Be sure that the target directory exists
  345.   *---------------------------------------------------------------------------*/
  346.  
  347.   if (!isdir(tdir))
  348.   {
  349.      fprintf(stderr, "ccp: '%s' is not a valid target directory.\n", tdir);
  350.      return(2);
  351.   }
  352.  
  353.  /*---------------------------------------------------------------------------*
  354.   * Expand all xsource file specifications into regular expressions and compile
  355.   * While we're at it, be sure that none has any drive or path information.
  356.   *
  357.   * Each xsource spec is expanded into a regular expression and stored in
  358.   * the 'xsrcbuff' buffer for compiling.  The compiled regular expressions
  359.   * are stored back-to-back in the 'xsrcbuff' buffer.
  360.   *
  361.   * As each xsource spec is processed, the xsource pointer in the 'xsrv' array
  362.   * is overwritten with a pointer to its compiled regular expression in the
  363.   * buffer.
  364.   *---------------------------------------------------------------------------*/
  365.  
  366.   cnt = xsrc;                       /* Set count to no. of xsource specs */
  367.   charv = xsrv;                     /* And set up vector pointer to 1st one */
  368.   xsrccurr = xsrcbuff;              /* Point xsrccurr to start of buffer */
  369.  
  370.   while (cnt > 0)                   /* For each xsource spec: */
  371.   {
  372.      pn = decompose(*charv);             /* Check for d:path within it */
  373.      if (pn->pathlen != 0)               /* If xsource spec contains d:path: */
  374.      {                                        /* Error! */
  375.         fprintf(stderr, "ccp: !xsource '%s' contains a drive and/or path.\n", *charv);
  376.         return(2);
  377.      }
  378.  
  379.      rc = rexpand(*charv, buff,          /* Expand it into a regular expression */
  380.                   &buff[ELEN+1], &next_eexpr);
  381.      if (rc != 0)
  382.      {
  383.         fprintf(stderr, "ccp: !xsource specification is too long: '%s'\n", *charv);
  384.         return(2);
  385.      }
  386.  
  387.      strupr(buff);                       /* Convert spec to upper case */
  388.      rc = rcompile(buff, xsrccurr,       /* Compile the regular expression */
  389.                    &xsrcbuff[XLEN+1], '\0',  /* rcompile() updates xsrcnext to */
  390.                    &xsrcnext);               /* point to where next one will be stored */
  391.      if (rc != 0)
  392.      {
  393.         fprintf(stderr, "ccp: !xsource specification is not valid: '%s'\n", *charv);
  394.         return(2);
  395.      }
  396.  
  397.      *charv = xsrccurr;                  /* Overwrite with pointer to compiled expr */
  398.      xsrccurr = xsrcnext;                /* And update past compiled expression */
  399.  
  400.      cnt--;                              /* And process next xsource spec */
  401.      charv++;
  402.   }
  403.  
  404.  /*---------------------------------------------------------------------------*
  405.   * Process source file specifications and build specification list
  406.   *---------------------------------------------------------------------------*/
  407.  
  408.   rc = slmake(&speclist, uniq, TRUE, srcc, srcv);
  409.  
  410.   if (rc != 0)                      /* If there was an error: */
  411.   {                                 /* Analyze rc, show msg, and return */
  412.      if (rc == REGX_MEMORY)
  413.         fprintf(stderr, "ccp: Out of memory while processing '%s'.\n",
  414.            slerrspec());
  415.      else
  416.         fprintf(stderr, "ccp: source specification is not valid: '%s'.\n",
  417.            slerrspec());
  418.  
  419.      return(2);
  420.   }
  421.  
  422.  /*---------------------------------------------------------------------------*
  423.   * If we aren't going to actually copy any files, say so now!
  424.   *---------------------------------------------------------------------------*/
  425.  
  426.   if (!copyfiles)
  427.   {
  428.      fprintf(stderr, "ccp: *** No files will actually be copied ***\n");
  429.      fflush(stderr);
  430.   }
  431.  
  432.  /*---------------------------------------------------------------------------*
  433.   * Conditionally copy each matching source file in the specification list
  434.   *---------------------------------------------------------------------------*/
  435.  
  436.   mflags = 0;                       /* Set list of files to match to include: */
  437.   mflags = mflags | SL_NORMAL;           /* Ordinary files only */
  438.  
  439.   slrewind(speclist, mflags, recurse);/* Initialize slmatch() */
  440.   for (;;)                          /* Loop to find all matching files: */
  441.   {
  442.     /*-----------------------------------------------------*
  443.      * Get next matching file, if any
  444.      *-----------------------------------------------------*/
  445.  
  446.      fdata = slmatch(&pdata,             /* Get next matching file */
  447.                      &ndata);
  448.      if (fdata == NULL)                  /* If none: */
  449.         break;                                /* Done */
  450.  
  451.     /*-----------------------------------------------------*
  452.      * Before we store names in buff[], be sure they will
  453.      * fit by calculating the approx. max lengths of names.
  454.      * Lengths are approx. at first, since we don't know
  455.      * if we need a path separator.  Later, we'll find the
  456.      * exact lengths, after calling pathcat().
  457.      *-----------------------------------------------------*/
  458.  
  459.      baselen = strlen(fdata->Fname);     /* Calculate true length of base name */
  460.  
  461.      sourcelen = strlen(pdata->path) + baselen + 1;  /* approx. */
  462.      targetlen = strlen(tdir) + baselen + 1;         /* approx. */
  463.  
  464.      if (recurse)
  465.         targetlen += sourcelen - baselen + 1;        /* very approx. */
  466.  
  467.      if ((baselen + sourcelen + targetlen + 3) > ELEN)
  468.      {                                   /* Add 3 for the null terminators! */
  469.         fprintf(stderr, "ccp: Internal error: Names are too long.\n");
  470.         return(2);
  471.      }
  472.  
  473.     /*-----------------------------------------------------*
  474.      * Store names in the buff[] buffer, as 3 strings:
  475.      *    sourcename,0    Full pathname of source file
  476.      *    targetname,0    Full pathname of target file
  477.      *    basename,0      Uppercase filename portion
  478.      *-----------------------------------------------------*/
  479.  
  480.      sourcename = buff;                  /* Store full path+name of source */
  481.      strcpy(sourcename, pdata->path);
  482.      pathcat(sourcename, fdata->Fname);
  483.      sourcelen = strlen(sourcename);          /* And store its true length */
  484.  
  485.      targetname = sourcename +           /* Store full path+name of target */
  486.                   sourcelen + 1;
  487.      strcpy(targetname, tdir);
  488.  
  489.      if (recurse)                        /* If recursing subdirectories: */
  490.      {
  491.         str = pdata->path +                   /* Point str past base portion */
  492.              pdata->bpathlen;                 /* of the path */
  493.         if (*str == '\\') str++;              /* Bump past path separator, if any */
  494.  
  495.         pathcat(targetname, str);             /* And add the recursed subdir's name */
  496.      }                                        /* to the target path */
  497.  
  498.      pathcat(targetname, fdata->Fname);
  499.      targetlen = strlen(targetname);          /* And store its true length */
  500.  
  501.      if (!pdata->hpfs)                        /* If source is not HPFS: */
  502.         strlwr(targetname);                      /* Make targetname lowercase */
  503.  
  504.      basename = targetname +             /* Store upper-case base name */
  505.                 targetlen + 1;
  506.      strcpy(basename, fdata->Fname);
  507.      strupr(basename);
  508.  
  509.     /*-----------------------------------------------------*
  510.      * TEST: Display name strings
  511.      *-----------------------------------------------------*/
  512. /*
  513.      printf("Copy from: '%s'\n", sourcename);
  514.      printf("Copy to:   '%s'\n", targetname);
  515.      printf("Base name: '%s'\n", basename);
  516.      printf("\n");
  517. */
  518.     /*-----------------------------------------------------*
  519.      * Compare basename against compiled xsource expressions
  520.      * to see if we should exclude this source file
  521.      *-----------------------------------------------------*/
  522.  
  523.      if (excludef(basename, xsrc, xsrv)) /* If we are to exclude this file: */
  524.      {
  525.         if (showx)                            /* If we are to display its name: */
  526.         {
  527.            printf("! %s\n", sourcename);           /* Display its name */
  528.            fflush(stdout);
  529.         }
  530.      }
  531.      else                                /* Else: user didn't exclude file: */
  532.      {
  533.          /*--------------------------------------------*
  534.           * Check to see if we should copy this file
  535.           *--------------------------------------------*/
  536.           memcpy(&sdate,                      /* Store source file's date/time */
  537.                  &fdata->fdateLastWrite,
  538.                  sizeof(ushort));
  539.           memcpy(&stime,
  540.                  &fdata->ftimeLastWrite,
  541.                  sizeof(ushort));
  542.           if (copyit(sourcename,              /* If the file should be copied: */
  543.                      fdata->Fsize,
  544.                      sdate, /*fdata->Fdate,*/
  545.                      stime, /*fdata->Ftime,*/
  546.                      fdata->Fmode,
  547.                      targetname,
  548.                      mustexist,
  549.                      ccopy))
  550.           {
  551.              if (!showtarget)                      /* Display either: */
  552.                 printf("%s\n", sourcename);             /* Just the source */
  553.              else                                  /* or: */
  554.                 printf("%s -> %s\n", sourcename,        /* Both source and target */
  555.                                      targetname);
  556.              fflush(stdout);
  557.              rc = 0;                               /* Init rc to zero */
  558.              if (copyfiles)                        /* If we are to perform the copy: */
  559.              {
  560.                 if (recurse)                            /* If recursing subdirs: */
  561.                 {
  562.                    rc = makepath(targetname);                /* We might need to make */
  563.                    if (rc != 0)                              /* them in the target dir */
  564.                       rc = CPE_MKDIR;
  565.                 }
  566.  
  567.                 if (rc == 0)                            /* If no errors so far: */
  568.                 {
  569.                   rc = fcopy(sourcename,                  /* Perform the copy */
  570.                              sdate,
  571.                              stime,
  572.                              fdata->Fmode,
  573.                              targetname,
  574.                              force);
  575.                 }
  576.  
  577.                 switch (rc)                             /* Check return code from copy: */
  578.                 {
  579.                    case 0:                                   /* If ok: */
  580.                       break;                                      /* Keep going */
  581.                    case CPE_FULL:                            /* If target is full: */
  582.                       fflush(stdout);                             /* Display error msg */
  583.                       fprintf(stderr, "ccp: Target is full when copying to %s\n",
  584.                          targetname);
  585.                       fflush(stderr);
  586.                       return(2);                                  /* Return: can't copy if full */
  587.                       break;
  588.                    case CPE_MKDIR:                           /* If we can't make a dir: */
  589.                       fflush(stdout);                             /* Display error msg */
  590.                       fprintf(stderr, "ccp: Can't make a directory within %s\n",
  591.                          targetname);
  592.                       fflush(stderr);
  593.                       break;
  594.                    default:                                  /* If other error: */
  595.                       fflush(stdout);
  596.                       fprintf(stderr, "ccp: Error while copying to %s\n",
  597.                          targetname);
  598.                       fflush(stderr);
  599.                       break;
  600.                 } /* end of switch stmt to process fcopy() return code */
  601.              } /* end of 'if (copyfiles)' */
  602.  
  603.              if (rc == 0) copycnt++;               /* Update count of files copied */
  604.  
  605.           } /* end of 'if the file should be copied */
  606.           else                                /* Else: ccp says "don't copy file": */
  607.           {
  608.              if (showx)                            /* If we are to display its name: */
  609.              {
  610.                 printf("x %s\n", sourcename);           /* Display its name */
  611.                 fflush(stdout);
  612.              }
  613.           } /* end of check to see if file should be copied */
  614.      } /* end of check to see if user wants to exclude this file */
  615.   } /* end of loop to find all matching files */
  616.  
  617.  /*---------------------------------------------------------------------------*
  618.   * Display (on stderr) all source filespecs that had no matching files
  619.   *---------------------------------------------------------------------------*/
  620.  
  621.   lrc = slnotfound(speclist);       /* Display path\names w/no matching files */
  622.   if (lrc == 0L)                    /* If all fspecs were matched: */
  623.      rc = 0;                             /* Setup for return successfully */
  624.   else                              /* Otherwise:  One or more not found: */
  625.      rc = 2;                             /* Setup for return with error */
  626.  
  627.  /*---------------------------------------------------------------------------*
  628.   * Display (on stdout) how many files we copied
  629.   *---------------------------------------------------------------------------*/
  630.  
  631.   fflush(stdout);                   /* First flush any stdout filenames */
  632.   fprintf(stderr, "%lu file(s) copied.\n", copycnt);
  633.  
  634.   return(rc);                       /* Return with proper exit code */
  635.  
  636. } /* end of main() */
  637.  
  638. /*============================================================================*
  639.  * syntax() - Display command syntax and exit to operating system!
  640.  *============================================================================*/
  641. static void syntax()
  642. {
  643.   fprintf(stderr, "%s\n", ver);
  644.   fprintf(stderr, "Usage: ccp [-flags] source ... [! Xsource ...] targetdir\n");
  645.   fprintf(stderr, "\n");
  646.   fprintf(stderr, "The ccp program conditionally copies files that match the source file\n");
  647.   fprintf(stderr, "specification(s), excluding those whose names match the optional Xsource\n");
  648.   fprintf(stderr, "file specification(s), to the target directory. Source files are copied if\n");
  649.   fprintf(stderr, "they don't exist in the target directory or if they have a different time,\n");
  650.   fprintf(stderr, "date, or size in the target directory. The following flags are supported:\n");
  651.   fprintf(stderr, "\n");
  652.   fprintf(stderr, "  l  If a copy of the source file exists in the target directory, copy the\n");
  653.   fprintf(stderr, "     source file only if it is later than the one in the target directory.\n");
  654.   fprintf(stderr, "\n");
  655.   fprintf(stderr, "  e  Don't copy a source file that doesn't already exist in the\n");
  656.   fprintf(stderr, "     target directory.\n");
  657.   fprintf(stderr, "\n");
  658.   fprintf(stderr, "  f  Force copy even if target file is read-only.\n");
  659.   fprintf(stderr, "\n");
  660.   fprintf(stderr, "  n  Don't copy any files; just list the names of the files that\n");
  661.   fprintf(stderr, "     would have been copied.\n");
  662.   fprintf(stderr, "\n");
  663.   fprintf(stderr, "  s  Descend subdirectories and preserve them in the target directory.\n");
  664. //fprintf(stderr, "\n");
  665. //fprintf(stderr, "  t  Show the names of each target filename as well as each source filename.\n");
  666.   fprintf(stderr, "\n");
  667.   fprintf(stderr, "  x  Also list the names of files that are being excluded (not copied).\n");
  668.   exit(1);
  669. }
  670.  
  671. /*============================================================================*
  672.  * excludef() - Should we exclude the file?
  673.  *
  674.  * REMARKS:
  675.  *   This subroutine compares the base name of a file to a list of compiled
  676.  *   regular expressions.
  677.  *
  678.  * RETURNS:
  679.  *   TRUE: If the file matches one of the regular expressions: Exclude it!
  680.  *   FALSE: If the file matches none of the regular expressions.
  681.  *============================================================================*/
  682.  
  683. static int excludef(
  684.  
  685. char    *name ,                     /* Uppercase version of base filename */
  686. int      regc ,                     /* No. of regular expressions */
  687. char   **regv )                     /* Pointer to array of regular expressions */
  688.  
  689. {
  690.   while (regc > 0)                  /* For each expression in array: */
  691.   {
  692.      if (rmatch(*regv, name) == 0)       /* If we found a match: */
  693.         return(TRUE);                         /* Return TRUE */
  694.      regc--;                             /* Else try next expression */
  695.      regv++;
  696.   }
  697.  
  698.   return(FALSE);                    /* We matched none of the expressions */
  699. }
  700.  
  701. /*============================================================================*
  702.  * copyit() - Should we copy the file?
  703.  *
  704.  * REMARKS:
  705.  *   This subroutine compares source file with the target file (if it exists),
  706.  *   checks the flags passed to it, and determines whether or not we should
  707.  *   copy the file.
  708.  *
  709.  * RETURNS:
  710.  *   TRUE: If the file should be copied.
  711.  *   FALSE: If the file should NOT be copied.
  712.  *
  713.  * SIDE EFFECTS:
  714.  *   If the target file exists but is a directory, this routine returns
  715.  *   FALSE (don't copy) but prints an error message.
  716.  *============================================================================*/
  717.  
  718. static int copyit(
  719.  
  720. char    *source      ,              /* Full pathname of source file */
  721. ulong    ssize       ,              /* Size of source file */
  722. ushort   sdate       ,              /* Date of source file */
  723. ushort   stime       ,              /* Time of source file */
  724. ushort   smode       ,              /* File mode of source file */
  725. char    *target      ,              /* Full pathname of target file */
  726. int      MustExist   ,              /* Must target exist (TRUE, FALSE) */
  727. int      condition   )              /* CCP_DIFFERENT, CCP_LATER ... */
  728.  
  729. {
  730.   int    rc;                        /* Return code storage */
  731.   ushort tdate;                     /* Target file's date/time */
  732.   ushort ttime;
  733.  
  734.   ulong   scnt;                     /* Count used by findfirst */
  735.   ushort  scnt16;
  736.   HDIR    findh;                    /* Handle returned by DosFindFirst() */
  737.  
  738.   FINFO  finfo;                     /* File information structure */
  739.  
  740.   findh = HDIR_CREATE;              /* Set initial value of handle */
  741.   scnt = 1;                         /* Find target file */
  742.   scnt16=1;
  743. //rc = DosFindFirst(target,
  744. ////      &findh, attrib,
  745. ////      &finfo, sizeof(FINFO),
  746. ////      &scnt,
  747. ////      (ULONG)FIL_STANDARD);          /* Was 0L for 1.3. Is 1 for 2.0  */
  748.   rc = DOS16FINDFIRST(target,
  749.           &findh, (USHORT)attrib,
  750.           &finfo, sizeof(FINFO),
  751.           &scnt16, 0L);
  752.   scnt=(ULONG)scnt16;
  753.  
  754.   if ((rc != 0) || (scnt == 0))     /* If target doesn't exist: */
  755.   {
  756.      if (MustExist)                      /* If target must exist: */
  757.         return(FALSE);                        /* Then don't perform a copy */
  758.      else                                /* Else target doesn't have to exist: */
  759.         return(TRUE);                         /* Then we need to copy, period */
  760.   }
  761.   else                              /* Else target exists: */
  762.   {
  763. //// DosFindClose(findh);                /* (Close the handle first) */
  764.      DOS16FINDCLOSE(findh);              /* (Close the handle first) */
  765.      if ((finfo.Fmode & _A_SUBDIR) != 0) /* Be sure it's not a directory */
  766.      {
  767.         fprintf(stderr, "ccp: Target file is a directory: %s\n", target);
  768.         fflush(stderr);
  769.         return(FALSE);
  770.      }
  771.  
  772.       memcpy(&tdate,                      /* Store target file's date/time */
  773.              &finfo.fdateLastWrite,
  774.              sizeof(ushort));
  775.       memcpy(&ttime,
  776.              &finfo.ftimeLastWrite,
  777.              sizeof(ushort));
  778.  
  779.      switch (condition)                  /* Check condition for copy: */
  780.      {
  781.         case CCP_DIFFERENT:                   /*---- Time/date/size must be different: */
  782.            if (ssize != finfo.Fsize) return(TRUE);
  783.            if (sdate != tdate) return(TRUE);
  784.            if (stime != ttime) return(TRUE);
  785.            return(FALSE);                          /* Source/target are the same */
  786.            break;
  787.  
  788.         case CCP_LATER:                       /*---- Source must be later: */
  789.           /*---------------------------------------------*
  790.            *   The source is later than the target if:
  791.            *      (date(source) > date(target)), or:
  792.            *      (date(source) == date(target)) &&
  793.            *      (time(source) > time(target))
  794.            *---------------------------------------------*/
  795.            if (sdate > tdate) return(TRUE);
  796.            if ((sdate == tdate) &&
  797.                (stime > ttime)) return(TRUE);
  798.            return(FALSE);                          /* Source is same or earlier */
  799.            break;
  800.      }
  801.   }
  802.  
  803.   return(FALSE);                    /* Will only get here if 'condition' is bad */
  804. }
  805.  
  806. /*============================================================================*
  807.  * fcopy() - Copy file (OS/2 version).
  808.  *
  809.  * REMARKS:
  810.  *   This subroutine copies the source file to the target file, overwriting
  811.  *   the target file if it already exists.
  812.  *
  813.  *   The OS/2 version requires no copy buffer, since it uses OS/2's
  814.  *   OS/2's DosCopy() subroutine.
  815.  *
  816.  *   If an error occurs, the target file (or what there is of it) is erased.
  817.  *
  818.  * RETURNS:
  819.  *   0         : Successful.
  820.  *   CPE_FULL  : The target is full.
  821.  *   CPE_OTHER : Other error.
  822.  *============================================================================*/
  823.  
  824. #define _COPY_OP   0x0001           /* Replace target, Copy even if target exists */
  825.  
  826. static int fcopy(
  827.  
  828. char    *source ,                   /* Full pathname of source file */
  829. ushort   sdate  ,                   /* Date of source file */
  830. ushort   stime  ,                   /* Time of source file */
  831. ushort   smode  ,                   /* File mode of source file */
  832. char    *target ,                   /* Full pathname of target file */
  833. int      force  )                   /* Force copy if target is readonly? */
  834.  
  835. {
  836.   int    rc;                        /* Return code storage */
  837.   int    cperr;                     /* File copy error code */
  838.  
  839.  /*---------------------------------------------------------------------------*
  840.   * Copy the source file to the target file:
  841.   *---------------------------------------------------------------------------*/
  842.  
  843.   cperr = DosCopy(source, target,   /* Copy the file */
  844.                  _COPY_OP);
  845.  
  846.  /*---------------------------------------------------------------------------*
  847.   * If access to target file was denied and force is TRUE, make file
  848.   * writable and try again
  849.   *---------------------------------------------------------------------------*/
  850.  
  851.   if ((cperr == ERROR_ACCESS_DENIED) &&
  852.      (force == TRUE))               /* If target is readonly and force is TRUE: */
  853.   {
  854.      cperr = chmod(target, S_IWRITE);    /* Make target writable */
  855.      if (cperr == 0)
  856.      {
  857.         cperr = DosCopy(source, target,  /* Again, copy the file */
  858.                        _COPY_OP);
  859.      }
  860.   }
  861.  
  862.  /*---------------------------------------------------------------------------*
  863.   * Done: Check for errors and return
  864.   *---------------------------------------------------------------------------*/
  865.  
  866.   if (cperr != 0)                   /* If error: */
  867.      return(CPE_OTHER);                  /* Return error code */
  868.  
  869.   return(0);                        /* Done with copy */
  870. }
  871.