home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / snip9707.zip / CSPLIT.C < prev    next >
C/C++ Source or Header  |  1997-07-05  |  38KB  |  1,252 lines

  1. /* +++Date last modified: 05-Jul-1997 */
  2.  
  3. /*
  4.  * Donated to public domain
  5.  *
  6.  * Designation:  CSplit
  7.  *
  8.  * Description:  This program is used to process source files for
  9.  *               the purpose of transmission through a FidoNet (tm)
  10.  *               echo in such a manner as to circumvent over-size
  11.  *               message problems.
  12.  *
  13.  *  This program implements the following specifications:
  14.  *
  15.  *  1) a. Combine multiple source files.
  16.  *     b. Split source into 90 line sections.
  17.  *     c. Add header and trailer marker lines delimiting each
  18.  *        section and file.
  19.  *  2) a. Delete any trailing whitespace.
  20.  *     b. Replace tabs with spaces honoring tabstop boundaries.
  21.  *     c. Default to 4 columns per tabstop.
  22.  *     d. Allow user to specify alternate tabstop column number.
  23.  *  3) a. Wrap lines longer than 75 characters long using the C
  24.  *        "\ at the end of a line" notation (using space break).
  25.  *     b. Distinguish wrapped lines from user-continued lines by
  26.  *        inserting a line with a single "\" character between the
  27.  *        two lines that contain the wrapped text.
  28.  *  4) a. Calculate a CRC for each section and include it in the
  29.  *        section trailer marker lines.
  30.  *  5) a. Provide a help display for program usage when the
  31.  *        program is executed without parameters.
  32.  *  6) a. Provide as detailed of explanation as possible when
  33.  *        an unexpected condition occurs.
  34.  *     b. Attempt to continue execution for all but the most severe
  35.  *        errors.
  36.  *
  37.  *
  38.  * Syntax:
  39.  *
  40.  * Split:     CSPLIT  [/tn] [/wn] [/ln] [/sc]  outfile  src.ext [ ... ]
  41.  *
  42. |* Extract:   CSPLIT  /x  infile  [ ... ]
  43.  *
  44.  * Where:      /t n - For TAB character expansion, the number of columns
  45.  *                    for each TAB stop.  (defaults to every 4 columns)
  46.  *             /w n - For width control, the column number to be used for
  47.  *                    breaking long lines.  (the default is column 75)
  48.  *             /l n - For length control, the number of lines to limit
  49. |*                    each section or 0 for no split.  (default is 90)
  50.  *             /s c - Use 'c' as an alternate line separator character
  51.  *                    instead of the default separator character, '>'.
  52. |*                    Ignored for extraction - matches separator found
  53. |*                    in extract file.  However, the extract file must
  54. |*                    only use one separator character.
  55.  *           infile - Input file name.  An extension indicates that the
  56.  *                    file contains the sections in proper consecutive
  57. |*                    order ready for extraction.  Otherwise, infile.001,
  58. |*                    infile.002, etc., will be used.
  59. |*          outfile - Name of the output file(s).  The extension will
  60. |*                    be ignored if specified and each output file will
  61. |*                    be named such that the section number will be the
  62. |*                    extension (e.g., outfile.001, outfile.002, etc..)
  63.  *          src.ext - The first source file..etc  Wildcard filespecs are
  64.  *                    supported only under non-ANSI compiling conditions.
  65.  *
  66. |* Notes:  Paths are supported for all filenames, however, the paths
  67. |*         are not preserved internally during the split operation.
  68. |*         The extraction process will therefore only create files in
  69. |*         the current directory.
  70.  *
  71.  * Revision History:  ( thanks to all the contributors )
  72.  *
  73.  * 08/31/93  Fred Cole  Original draft
  74.  * 09/05/93  Fred Cole  Added CRC calculation and extraction ability.
  75.  *                      Fixed a line wrap problem.
  76.  * 09/14/93  Fred Cole  Added conditional compilation directives to
  77.  *                      allow non-ANSI filespec support.  Squashed an
  78.  *                      extract() function bug.
  79.  * 11/21/93  Thad Smith Test for incomplete input file on extraction.
  80.  *                      Remove spaces added in message transmission.
  81.  *                      Default to 90 lines/section.  Fix tab expansion.
  82.  * 12/03/93  Fred Cole  Fixed a cleanup() function bug.
  83.  * 12/09/93  Keith Campbell / TS
  84.  *                      Fixed bug with options preceded by '-' and fixed
  85.  *                      tempfile opening bug.
  86.  * 01/02/94  David Nugent / FC
  87.  *                      Additions for findfirst/findnext support for
  88.  *                      MSC6 (& 7) and OS/2 in initlist() routine and
  89.  *                      portable.h header file.
  90.  * 01/02/94  Auke Reitsma / FC
  91.  *                      Increased number of chars read in to prevent line
  92.  *                      numbers from becoming out-of-sync with input.
  93.  * 01/12/94  Chad Wagner / FC
  94.  *                      Correction to initlist() function to eliminate
  95.  *                      redundant line increment.
  96.  *--- v2.0 --------------------------------------------------------------
  97.  * 07/23/94  Keith Campbell / FC
  98.  *                      Modified to not abort extraction when a CRC
  99.  *                      mismatch occurs - just issue warning message.
  100.  * 07/30/94  Auke Reitsma / FC
  101.  *                      Added multiple file extraction ability.
  102.  * 09/17/94  Keith Campbell / FC
  103.  *                      Added version separator lines.
  104.  * 10/28/94  Bob Stout / FC
  105.  *                      Added separator character, width and length
  106.  *                      command line options.
  107.  * 12/18/94  Fred Cole  Revised code to facilitate maintenance.
  108.  * 12/27/94  Fred Cole  Limited the minimum width for breaking long
  109.  *                      lines to column 55 since this is the length
  110.  *                      of the longest separator line.
  111.  * 01/15/95  David Gersic / FC
  112.  *                      Modified the line wrap logic to handle long
  113.  *                      sequences of characters lacking whitespace.
  114.  *--- v2.1 --------------------------------------------------------------
  115.  * 10/30/95  Phi Nguyen / FC
  116.  *                      Added file extraction messages.
  117.  * 10/31/95  Fred Cole  Added ability to extract unconcatenated files.
  118.  *                      Added path support except for extracted files
  119.  *                      ( i.e., paths are not preserved internally ).
  120.  * 11/06/95  Fred Cole  Corrected tabstop calculation on wrapped lines.
  121.  * 11/07/95  Fred Cole  Increased max section length to SHRT_MAX lines.
  122.  * 11/08/95  Bob Stout / FC
  123.  *                      Disable the split logic when a 0 section length
  124.  *                      is specified with the /L command line option.
  125.  *--- v2.2 --------------------------------------------------------------
  126.  * 11/22/95  Fred Cole  Modified logic to ignore leading whitespace added
  127.  *                      to separator lines ( mail reader quoting? ).
  128.  * 11/22/95  Doug Nazar (DN2) / FC
  129.  *                      Modified sscanf() format specifiers to correctly
  130.  *                      convert CRC values for 32 bit platforms.
  131.  * 11/22/95  Fred Cole  Changed TRUE/FALSE enum to macros to accommodate
  132.  *                      systems where these identifiers already exist.
  133.  * 11/29/95  Bob Stout / FC
  134.  *                      Added unsigned casts to allow signed 'length' to
  135.  *                      be compared to SHRT_MAX on 16-bit platforms.
  136.  * 11/29/95  Doug Nazar (DN2) / FC
  137.  *                      Added setbuf() statement to unbuffer stdout.
  138.  * 06/02/96  Fred Cole  Renamed TRUE/FALSE macros to avoid possible
  139.  *                      identifier conflicts.
  140.  * 06/02/96  Darin McBride / FC
  141.  *                      Modified logic to allow source files located on
  142.  *                      on another drive to be processed.  This change
  143.  *                      assumes that ':' is not a valid character for a
  144.  *                      file name.
  145.  * 06/02/96  Fred Cole  Corrected error in extract logic when file does
  146.  *                      not exist.
  147.  */
  148.  
  149. #include <ctype.h>
  150. #include <stdio.h>
  151. #include <stdlib.h>
  152. #include <string.h>
  153. #include <limits.h>
  154. #include "csplit.h"
  155.  
  156. char tempfile[MAXFSPEC + 1]; /* necessary evils - global variables */
  157. FILE *finp = NULL;
  158. FILE *fout = NULL;
  159. FILE *ftmp = NULL;
  160. SLST *head = NULL;
  161. SLST *cur  = NULL;
  162.  
  163. int main (int argc, char *argv[])
  164. {
  165.   char     sepchar = SEP_CDEF;
  166.   char     outfile[MAXFSPEC + 1];
  167.   char    *sptr    = 0;
  168.   int      argndx  = 1;
  169.   int      chr     = 0;
  170.   int      extract = B_FALSE;
  171.   int      length  = LENDEF;
  172.   int      retc    = 0;
  173.   int      tabstop = TABDEF;
  174.   int      width   = WIDDEF;
  175.  
  176.  
  177.   setbuf (stdout, 0);                  /* DN2: buffered output fix */
  178.   printf ("\nCSplit %s  (pd) 1993-1996 by Fred Cole\n", VERSION);
  179.   printf ("This executable program is public domain.\n\n");
  180.  
  181.   if (1 == argc)
  182.   {
  183.     disp_help ();
  184.     return SYNTAX;
  185.   }
  186.  
  187.   while (('/' == argv[argndx][0]) || ('-' == argv[argndx][0]))
  188.   {
  189.     chr = toupper (argv[argndx][1]);
  190.  
  191.     switch (chr)
  192.     {
  193.       case '?':
  194.       case 'H':                   /* /H,/? help option */
  195.         disp_help ();
  196.         return SYNTAX;
  197.  
  198.       case 'X':                   /* /X extract option */
  199.         extract = B_TRUE;
  200.         break;
  201.  
  202.       case 'T':                   /* /T tab option */
  203.         tabstop = atoi (&argv[argndx][2]);
  204.  
  205.         if ((tabstop < TABMIN) || (tabstop > TABMAX))
  206.           printf ("Invalid tab parameter \"%s\" (%d-%d).\n", argv[argndx], TABMIN, TABMAX);
  207.  
  208.         if (tabstop < TABMIN)
  209.           tabstop = TABMIN;
  210.         else if (tabstop > TABMAX)
  211.           tabstop = TABMAX;
  212.  
  213.         break;
  214.  
  215.       case 'W':                   /* /W width option */
  216.         width = atoi (&argv[argndx][2]);
  217.  
  218.         if ((width < WIDMIN) || (width > WIDMAX))
  219.           printf ("Invalid width parameter \"%s\" (%d-%d).\n", argv[argndx], WIDMIN, WIDMAX);
  220.  
  221.         if (width < WIDMIN)
  222.           width = WIDMIN;
  223.         else if (width > WIDMAX)
  224.           width = WIDMAX;
  225.  
  226.         break;
  227.  
  228.       case 'L':                   /* /L length option */
  229.         if (('0' == argv[argndx][2]) && ('\0' == argv[argndx][3]))
  230.           length = 0;
  231.         else
  232.         {
  233.           length = atoi (&argv[argndx][2]);
  234.  
  235.           if ((length < LENMIN) || ((unsigned)length > LENMAX))
  236.             printf ("Invalid length parameter \"%s\" (0,%d-%d).\n", argv[argndx], LENMIN, LENMAX);
  237.  
  238.           if (length < LENMIN)
  239.             length = LENDEF;
  240.           else if ((unsigned)length > LENMAX)
  241.             length = LENMAX;
  242.         }
  243.  
  244.         break;
  245.  
  246.       case 'S':                   /* /S separator character option */
  247.         sscanf (&argv[argndx][2], "%c", &sepchar);
  248.  
  249.         if (0 == isgraph (sepchar))
  250.         {
  251.           printf ("Invalid input parameter \"%s\".\n", argv[argndx]);
  252.           sepchar = SEP_CDEF;
  253.         }
  254.         break;
  255.  
  256.       default:
  257.         printf ("Ignoring unknown input parameter \"%s\".\n", argv[argndx]);
  258.     }
  259.     ++argndx;
  260.   }
  261.  
  262.   if (B_TRUE == extract)
  263.   {
  264.     if (argndx == argc)
  265.     {
  266.       printf ("No file argument specified for extraction.\n");
  267.       return SYNTAX;
  268.     }
  269.  
  270.     /* AR: handle multiple files; break on error */
  271.     for ( ; argndx < argc; ++argndx)
  272.       if (NOERR != (retc = extr_file (argv[argndx], sepchar)))
  273.         break;
  274.  
  275.     cleanup ();
  276.     return retc;
  277.   }
  278.  
  279.   if ((argc - argndx) < 2)
  280.   {
  281.     printf ("Missing input and/or output file name arguments.\n");
  282.     disp_help ();
  283.     return SYNTAX;
  284.   }
  285.  
  286.   if (NULL != (sptr = strrchr (argv[argndx], '\\')))  /* ignore path */
  287.   {
  288.     if (NULL != (sptr = strchr (sptr, '.')))
  289.       *sptr = '\0';                              /* truncate any ext */
  290.   }
  291.   else if (NULL != (sptr = strchr (argv[argndx], '.')))
  292.   {
  293.     *sptr = '\0';                                /* truncate any ext */
  294.   }
  295.  
  296.   if (strlen (argv[argndx]) > MAXFSPEC)
  297.   {
  298.     printf ("Output file name argument too long.\n");
  299.     return SYNTAX;
  300.   }
  301.  
  302.   strncpy (outfile, argv[argndx], MAXFSPEC);
  303.   outfile[MAXFSPEC] = '\0';                      /* ensure termination */
  304.  
  305.   if (NOERR != (retc = init_list (argc, argv, ++argndx)))
  306.   {
  307.     cleanup ();
  308.     return retc;
  309.   }
  310.  
  311.   retc = split_src (head, outfile, length, width, tabstop, sepchar);
  312.   cleanup ();
  313.   return retc;
  314. }
  315.  
  316. /*
  317.  * add_list - Add a file name to linked list of files to be processed.
  318.  */
  319. SLST *add_list (char *fname)
  320. {
  321.   SLST *new = NULL;
  322.  
  323.   if (NULL == (new = (SLST *)malloc (sizeof(SLST))))
  324.   {
  325.     puts ("Error allocating memory.\n");
  326.   }
  327.   else
  328.   {
  329.     strcpy (new->srcfile, fname);
  330.     new->next = NULL;
  331.  
  332.     if (NULL == cur)
  333.       head = new;
  334.     else
  335.       cur->next = new;
  336.   }
  337.   cur = new;
  338.   return cur;
  339. }
  340.  
  341. /*
  342.  * cleanup - Just a convenient way to provide centralized housekeeping.
  343.  */
  344. void cleanup (void)
  345. {
  346.   free_list ();
  347.  
  348.   if (NULL != finp)  fclose (finp);
  349.   if (NULL != fout)  fclose (fout);
  350.   if (NULL != ftmp)  fclose (ftmp);
  351.  
  352.   /* Now, does it really exist? */
  353.   if (NULL != (ftmp = fopen (tempfile, "r")))
  354.   {
  355.     fclose (ftmp);
  356.     remove (tempfile);
  357.   }
  358. }
  359.  
  360. /*
  361.  * csp_fgets - A custom fgets() function that expands
  362.  *             tabs, deletes trailing whitespace and
  363.  *             performs line wrapping.
  364.  */
  365. char *csp_fgets (char *s, int len, FILE * fp, int tabstop)
  366. {
  367.   static char  sbuf[LLENMAX * 2]; /* big enough for TAB expansion */
  368.   static char *beg  = sbuf;
  369.   static int   tofs = 0;
  370.   static int   wrap = B_FALSE;
  371.  
  372.   char *e = 0;
  373.   char *w = 0;
  374.   char *p = s;
  375.   char *q = 0;
  376.  
  377.   int   ch     = 0;
  378.   int   cnt    = 0;
  379.   int   i      = 0;
  380.   int   spaces = 0;
  381.  
  382.   if (B_TRUE == wrap)             /* if line wrap */
  383.   {
  384.     tofs += (int)(beg - sbuf);    /* adj. TAB column offset */
  385.     strcpy (s, "\\\n");
  386.     memmove (sbuf, beg, strlen (beg) + 1); /* DG: Modification for  */
  387.     beg  = sbuf;                           /* DG: long lines w/o WS */
  388.     wrap = B_FALSE;
  389.     return s;
  390.   }
  391.  
  392.   while ((cnt < len-1) && ('\n' != ch))
  393.   {                                    /* get next char from buffer */
  394.     if (0 == (ch = *beg++))            /* if buffer empty */
  395.     {
  396.       memset (sbuf, 0, sizeof (sbuf));
  397.       beg = fgets (sbuf, LLENMAX, fp); /* grab another string */
  398.  
  399.       if (NULL == beg)                 /* if end of file... */
  400.       {
  401.         beg = sbuf;
  402.         *beg = 0;
  403.  
  404.         if (0 == cnt)
  405.           return NULL;                 /*  and buffer empty */
  406.       }
  407.       else
  408.       {
  409.         w = e = &sbuf[i = strlen (sbuf)]; /* find 1st trailing ws char */
  410.  
  411.         while ((w > sbuf) && (isspace (*(w - 1))))
  412.           --w;
  413.  
  414.         if (('\n' == *(e - 1)) || /* if terminated w/newline char */
  415.             (i < (len-1)))        /* or unterminated short line */
  416.         {
  417.           *w++ = '\n';            /* terminate with newline char */
  418.         }
  419.  
  420.         *w = 0;
  421.         ch = *beg++;
  422.       }
  423.     }
  424.  
  425.     if (ch == '\t')               /* if TAB character */
  426.     {                             /* space to next tab stop */
  427.       /*
  428.        * TS: The following code has been changed to pad to the next
  429.        * tab stop, rather than insert a fixed number of spaces.
  430.        */
  431.       spaces = tabstop - (((int)(tofs + beg - sbuf) - 1) % tabstop);
  432.       memmove (beg + spaces - 1, beg, strlen (beg) + 1);
  433.  
  434.       for (q = beg - 1; spaces > 0; --spaces)
  435.         *q++ = ' ';
  436.  
  437.       ch = ' ';                   /* change TAB to space */
  438.     }
  439.  
  440.     *p++ = (char)ch;              /* update output string */
  441.     ++cnt;
  442.  
  443.     if ((cnt == len - 1) && ('\n' != ch))   /* if need to wrap line */
  444.     {
  445.       beg -= 2;                   /* make room for "\\\n" characters */
  446.       e = beg;
  447.       p -= 2;
  448.       q = p;
  449.  
  450.       /* unget characters to 1st previous space */
  451.       while ((e > sbuf) && (' ' != *(e - 1)))
  452.       {
  453.         --q;
  454.         --e;
  455.       }
  456.  
  457.       /*if (((beg - e) < len) && (e != sbuf))*/
  458.       if (e != sbuf)              /* if wrap on space char */
  459.       {                           /* ( else wrap asis ) */
  460.         p = q;
  461.         beg = e;
  462.       }
  463.  
  464.       *p++ = '\\';                /* terminate current line */
  465.       *p++ = '\n';
  466.       wrap = B_TRUE;              /* flag wrap line pending */
  467.     }
  468.   } /*end_while*/
  469.  
  470.   if ('\n' == ch)
  471.     tofs = 0;
  472.  
  473.   *p = 0;
  474.   return s;
  475. }
  476.  
  477. /*
  478.  * disp_help - Display help screen to user.
  479.  */
  480. void disp_help (void)
  481. {
  482.   puts ("This utility processes C/C++ source files for the purpose of transmission");
  483.   puts ("through a FidoNet (tm) echo so as to circumvent oversize message problems.\n");
  484.   puts ("Split:    CSplit  [ [/tn] [/wn] [/ln] [/sc] ]  outfile  srcfile [ ... ]");
  485.   puts ("Extract:  CSplit  /x  infile  [ ... ]\n");
  486.   puts ("Where:      /t n - For TAB character expansion, the number of columns");
  487. printf ("                   for each TAB stop.  (min/default/max: %d/%d/%d)\n", TABMIN, TABDEF, TABMAX);
  488.   puts ("            /w n - For width control, the column number to be used for");
  489. printf ("                   breaking long lines.  (min/default/max: %d/%d/%d)\n", WIDMIN, WIDDEF, WIDMAX);
  490.   puts ("            /l n - For length control, the number of lines to limit");
  491. printf ("                   each section.  (0 disable, min/default/max: %d/%d/%d)\n", LENMIN, LENDEF, LENMAX);
  492.   puts ("            /s c - Use 'c' as the line separator character instead");
  493. printf ("                   of the default separator character, '%c'.\n", SEP_CDEF);
  494.   puts ("         outfile - Output file name.  The extension will be the");
  495.   puts ("                   sequential section number.");
  496. #if defined(__STDC__)
  497.   puts ("         srcfile - Source file(s)  (no wildcard support)");
  498. #else
  499.   puts ("         srcfile - Source file(s)  (wildcards supported)");
  500. #endif
  501.   puts ("          infile - Input file name.  An extension indicates file contains the");
  502.   puts ("                   sections in proper consecutive order ready for extraction.");
  503.   puts ("                   Otherwise, infile.001, infile.002, etc., will be used.");
  504. }
  505.  
  506. /*
  507.  * extr_file - This function processes a properly concatenated file
  508.  *             of consequtive sections to extract the original source.
  509.  */
  510. int extr_file (char *infile, char sepchar)
  511. {
  512.   static char  line [WIDMAX * 2];
  513.   static char  line2[WIDMAX * 2];
  514.  
  515.   char         outfile[MAXFSPEC + 1];
  516.   char         sep_ln[32];
  517.   char        *sptr = 0;
  518.   char         tchar      = 0;
  519.  
  520.   int          curpart    = 0;
  521.   int          found      = B_FALSE;
  522.   int          indx       = 0;
  523.   int          in_section = B_FALSE;
  524.   int          len        = 0;
  525.   int          key        = 0;
  526.   int          lines      = 0;
  527.   int          maxpart    = 0;
  528.   int          pos_wrap   = B_FALSE;
  529.   int          sepmax     = 0;
  530.   int          seppart    = 0;
  531.   int          sep_id_len = 0;
  532.   int          temp       = 0;
  533.  
  534.   unsigned short crc = 0;
  535.   unsigned short sepcrc = 0;
  536.  
  537.  
  538.   for (temp = 0; temp < SEP_CLEN; ++temp)
  539.     sep_ln[temp] = (char)sepchar;
  540.  
  541.   sep_ln[temp++] = ' ';
  542.   strcpy (&sep_ln[temp], SEP_ID);
  543.   sep_id_len = strlen (sep_ln);
  544.  
  545.   if (NULL == (finp = fopen (infile, "r")))
  546.   {
  547.     char fname[MAXFSPEC + 1];
  548.  
  549.     if ((NULL == (sptr = strrchr (infile, '\\'))) && /* ignore path */
  550.         (NULL == (sptr = strrchr (infile, ':'))))
  551.     {
  552.       sptr = infile;                        /* V2.2#3 correction */
  553.     }
  554.  
  555.     if (NULL != strchr (sptr, '.'))         /* if extension exists */
  556.     {
  557.       printf ("Error opening input file for extraction.\n");
  558.       return FILEIO;
  559.     }
  560.  
  561.     if ((strlen (infile) + 4) > MAXFSPEC)   /* if file spec too large */
  562.     {
  563.       printf ("Input file name argument too long.\n");
  564.       return SYNTAX;
  565.     }
  566.  
  567.     indx = 1;
  568.     sprintf (fname, "%s.%03d", infile, indx);
  569.  
  570.     if (NULL != (finp = fopen (fname, "r")))
  571.     {
  572.       sprintf (tempfile, "%s.CSP", infile);
  573.  
  574.       if (NULL == (ftmp = fopen (tempfile, "w")))
  575.       {
  576.         printf ("Unable to open \"%s\" temp file.\n", tempfile);
  577.         return FILEIO;
  578.       }
  579.  
  580.       printf ("Processing input files: %s ...\n", fname);
  581.  
  582.       do
  583.       {
  584.         while (NULL != fgets (line, sizeof (line), finp))
  585.           fputs (line, ftmp);
  586.  
  587.         fclose (finp);
  588.         ++indx;
  589.         sprintf (fname, "%s.%03d", infile, indx);
  590.         finp = fopen (fname, "r");
  591.  
  592.       } while (NULL != finp);
  593.  
  594.       fclose (ftmp);
  595.  
  596.       if (NULL == (finp = fopen (tempfile, "r")))
  597.       {
  598.         printf ("Error opening temp file \"%s\" for extraction.\n", outfile);
  599.         return FILEIO;
  600.       }
  601.     }
  602.     else
  603.     {
  604.       printf ("Error opening input file \"%s\" for extraction.\n", infile);
  605.       return FILEIO;
  606.     }
  607.   }
  608.   else
  609.   {
  610.     printf ("Processing input file: %s\n", infile);
  611.   }
  612.  
  613.   crc = curpart = maxpart = lines = 0;
  614.   in_section = pos_wrap = B_FALSE;
  615.   fout = NULL;
  616.   initcrctab ();
  617.   *line2 = 0;
  618.  
  619.   while (NULL != finp)
  620.   {
  621.     /* AR: increased line input size */
  622.     sptr = fgets (line, sizeof (line), finp);
  623.  
  624.     if (NULL == sptr)
  625.     {
  626.       if (feof (finp))            /* end of file */
  627.       {
  628.         fclose (finp);
  629.         finp = NULL;
  630.       }
  631.       else
  632.       {
  633.         if (lines)
  634.           printf ("(line %d) ", lines);
  635.  
  636.         printf ("Unable to read from input file:  %s\n", infile);
  637.         return FILEIO;
  638.       }
  639.     }
  640.     else                          /* process line */
  641.     {
  642.       /* TS: eliminate any added trailing spaces */
  643.       for (indx = strlen (sptr) - 1; indx && (' ' == line[indx - 1]); --indx)
  644.         continue;
  645.  
  646.       line[indx]   = '\n';
  647.       line[indx+1] = '\0';
  648.       ++lines;
  649.  
  650.       /*
  651.        * Find 1st separator line to determine if correct separator
  652.        * character is being used and use the one that was found.
  653.        */
  654.       if (!found && (0 != strstr (line, SEP_ID)))
  655.       {
  656.         for (temp = 0; isspace (line[temp]); ++temp)
  657.           ;  /* null statement */
  658.  
  659.         tchar = line[temp];                 /* skip leading ws */
  660.  
  661.         if (sepchar != tchar)
  662.           for (temp = 0; temp < SEP_CLEN; ++temp)
  663.             sep_ln[temp] = tchar;
  664.  
  665.         found = B_TRUE;
  666.       }
  667.  
  668.       if (0 != (sptr = strstr (line , sep_ln))) /* if separator line */
  669.       {
  670.         sptr += sep_id_len;
  671.  
  672.         if (sptr == strstr (sptr, SEP_BF))  /* if begin file */
  673.         {
  674.           if (NULL != fout)
  675.           {
  676.             printf ("(line %d) ", lines);
  677.             puts ("Encountered 2nd \"Begin file\" separator");
  678.             puts ("line before expected \"End file\" separator line:");
  679.             puts (line);
  680.             return PROCESS;
  681.           }
  682.  
  683.           sptr += strlen (SEP_BF);
  684.  
  685.           if (1 != sscanf (sptr, "%s", outfile))
  686.           {
  687.             printf ("(line %d) ", lines);
  688.             puts ("Unable to parse filename from separator line:");
  689.             puts (line);
  690.             return PROCESS;
  691.           }
  692.  
  693.           if (NULL != (fout = fopen (outfile, "r")))
  694.           {
  695.             key = 0;
  696.             printf ("\nOutput file already exists:  %s\n", outfile);
  697.  
  698.             do
  699.             {
  700.               printf ("Overwrite? (y/n) ");
  701.               key = getchar ();
  702.               puts ("");
  703.               temp = key;
  704.  
  705.               while (temp != '\n')
  706.                 temp = getchar();  /* eat any/all extra keystrokes */
  707.  
  708.               if (('n' == key) || ('N' == key))
  709.                 return ABORT;
  710.  
  711.             } while (('y' != key) && ('Y' != key));
  712.  
  713.             if (NULL == freopen (outfile, "w", fout))
  714.             {
  715.               printf ("Unable to open file for output:  %s\n", outfile);
  716.               return FILEIO;
  717.             }
  718.           }
  719.           else
  720.           {
  721.             if (NULL == (fout = fopen (outfile, "w")))
  722.             {
  723.               printf ("Unable to open file for output:  %s\n", outfile);
  724.               return FILEIO;
  725.             }
  726.           }
  727.  
  728.           printf ("Extracting file %s\n", outfile);   /* Phi Nguyen */
  729.           crc = updcrc (crc, (unsigned char *)line, strlen (line));
  730.         }
  731.         else if (sptr == strstr (sptr, SEP_EF))  /* if end file */
  732.         {
  733.           if (NULL == fout)
  734.           {
  735.             printf ("(line %d) ", lines);
  736.             puts ("Encountered an \"End file\" separator line");
  737.             puts ("before a \"Begin file\" separator line:");
  738.             puts (line);
  739.             return PROCESS;
  740.           }
  741.  
  742.           if (fclose (fout))
  743.           {
  744.             printf ("Unable to close output file:  %s\n", outfile);
  745.             return FILEIO;
  746.           }
  747.  
  748.           fout = NULL;
  749.           crc = updcrc (crc, (unsigned char *)line, strlen (line));
  750.         }
  751.         else if (sptr == strstr (sptr, SEP_BP))  /* if begin part */
  752.         {
  753.           if (B_TRUE == in_section)
  754.           {
  755.             printf ("(line %d) ", lines);
  756.             puts ("Encountered 2nd \"Begin part\" separator");
  757.             puts ("line before expected \"End part\" separator line:");
  758.             puts (line);
  759.             return PROCESS;
  760.           }
  761.           sptr += strlen (SEP_BP);
  762.  
  763.           if (2 != sscanf (sptr, "%d/%d", &seppart, &sepmax))
  764.           {
  765.             printf ("(line %d) ", lines);
  766.             puts ("Unable to parse \"n/x\" from separator line:");
  767.             puts (line);
  768.             return PROCESS;
  769.           }
  770.  
  771.           if (0 == maxpart)
  772.             maxpart = sepmax;
  773.  
  774.           if (curpart+1 != seppart)
  775.           {
  776.             printf ("(line %d) ", lines);
  777.             printf ("Encountered part %d while expecting part %d:\n", seppart, curpart+1);
  778.             puts (line);
  779.             return PROCESS;
  780.           }
  781.           in_section = B_TRUE;
  782.           ++curpart;
  783.         }
  784.         else if (sptr == strstr (sptr, SEP_EP))  /* if end part */
  785.         {
  786.           if (B_FALSE == in_section)
  787.           {
  788.             printf ("(line %d) ", lines);
  789.             puts ("Encountered 2nd \"End part\" separator line");
  790.             puts ("before expected \"Begin part\" separator line:");
  791.             puts (line);
  792.             return PROCESS;
  793.           }
  794.           sptr += strlen (SEP_EP);
  795.           sptr = strstr (sptr, ": ");
  796.  
  797.           if (1 != sscanf (sptr+2, "%hx", &sepcrc))   /* DN2: 32 bit fix */
  798.           {
  799.             printf ("(line %d) ", lines);
  800.             puts ("Corrupt CRC in separator line:");
  801.             puts (line);
  802.             return PROCESS;
  803.           }
  804.  
  805.           if (crc != sepcrc)
  806.           {
  807.             printf ("(line %d) ", lines);
  808.             printf ("Calculated CRC mismatch (0x%04x):\n", crc);
  809.             puts (line);           /* KC: report CRC mismatch only */
  810.           }
  811.           crc = 0;
  812.           in_section = B_FALSE;
  813.  
  814.           if (curpart == maxpart)                /* if finished */
  815.             break;
  816.         }
  817.         else
  818.         {
  819.           if (sptr != strstr (sptr, SEP_VR))     /* if not version */
  820.           {
  821.             printf ("(line %d) ", lines);
  822.             puts ("Unrecognized separator line:");
  823.             puts (line);
  824.             return PROCESS;
  825.           }
  826.         }
  827.       }
  828.       else                        /* else process data line */
  829.       {
  830.         if (B_TRUE == in_section) /* save only file data */
  831.         {
  832.           len = strlen (line);
  833.           crc = updcrc (crc, (unsigned char *)line, len);
  834.  
  835.           if (B_TRUE == pos_wrap) /* if possible line wrap in progress */
  836.           {
  837.             if (0 == strncmp (line, "\\\n", 2))  /* if wrapped line */
  838.             {
  839.               strcpy (line, line2);
  840.               line[strlen (line) - 2] = 0;  /* remove wrap EOL */
  841.             }
  842.             else
  843.             {
  844.               strcat (line2, line);
  845.               strcpy (line, line2);
  846.             }
  847.             pos_wrap = B_FALSE;
  848.           }
  849.           else  if ('\\' == line[len - 2]) /* if possible wrapped line */
  850.           {
  851.             strcpy (line2, line);
  852.             pos_wrap = B_TRUE;
  853.           }
  854.  
  855.           if ((B_FALSE == pos_wrap) &&
  856.               ((NULL == fout) || (EOF == fputs (line, fout))))
  857.           {
  858.             puts ("Error writing output\n");
  859.             return FILEIO;
  860.           }
  861.         }
  862.       }
  863.     }
  864.   } /*end_while*/
  865.  
  866.   /* TS: Test for incompete processing. */
  867.   if (B_TRUE == in_section)
  868.   {
  869.     printf ("Error: end of input while processing section %d of %d\n", seppart, sepmax);
  870.     return PROCESS;
  871.   }
  872.  
  873.   if (seppart != sepmax)
  874.   {
  875.     printf ("Error: end of input after processing section %d of %d\n", seppart, sepmax);
  876.     return PROCESS;
  877.   }
  878.   return NOERR;
  879. }
  880.  
  881. /*
  882.  * free_list - This function simply frees each linked list item.
  883.  */
  884. void free_list (void)
  885. {
  886.   while (NULL != head)
  887.   {
  888.     cur = head->next;
  889.     free (head);
  890.     head = cur;
  891.   }
  892. }
  893.  
  894. /*
  895.  * init_list - This function creates a linked list of input source
  896.  *             files.  Wildcard specifications are accommodated when
  897.  *             ANSI mode is not in effect.
  898.  */
  899. int init_list (int argc, char **argv, int argo)
  900. {
  901.   int i;
  902. #if !defined(__STDC__)
  903.   char         filename[MAXFSPEC + 1];
  904.   char         path[PATHMAX + 1];
  905.   char        *sptr;
  906.   int          done;
  907.   DOSFileData  fd;
  908. #endif
  909.  
  910.   for (i = argo; i < argc; ++i)             /* process CL arguments */
  911.   {
  912. #if !defined(__STDC__)
  913.     if (strlen (argv[i]) > (MAXFSPEC - FNAMELEN))
  914.     {
  915.       printf ("Input file argument too long:  %s\n", argv[i]);
  916.       return SYNTAX;
  917.     }
  918.  
  919.     done = FIND_FIRST(argv[i], 0x20, &fd);  /* david nugent */
  920.  
  921.     if (done)
  922.     {
  923.       printf ("Error with filespec: %s\n", argv[i]);
  924.       return PROCESS;
  925.     }
  926.  
  927.     strcpy (path, argv[i]);                 /* preserve path */
  928.  
  929.     if (NULL != (sptr = strrchr (path, '\\')))
  930.       *(sptr + 1) = '\0';
  931.     else
  932.       path[0] = '\0';
  933.  
  934.     while (!done)
  935.     {
  936.       if ('\0' != path[0])
  937.       {
  938.         strcpy (filename, path);
  939.  
  940.         if (NULL != (sptr = strrchr (ff_name(&fd), '\\')))
  941.           strcat (filename, sptr + 1);      /* just in case */
  942.         else
  943.           strcat (filename, ff_name(&fd));
  944.       }
  945.       else
  946.         strcpy (filename, ff_name(&fd));
  947.  
  948.       if (NULL == add_list (filename))      /* david nugent */
  949.         return MEMORY;
  950.  
  951.       done = FIND_NEXT(&fd);
  952.     }
  953.     FIND_END(&fd);                          /* david nugent */
  954. #else
  955.     if (strlen (argv[i]) > MAXFSPEC)
  956.     {
  957.       printf ("Input file argument too long:  %s\n", argv[i]);
  958.       return SYNTAX;
  959.     }
  960.  
  961.     if (NULL == add_list (argv[i]))
  962.       return MEMORY;
  963.  
  964. #endif
  965.   }
  966.   return NOERR;
  967. }
  968.  
  969. /*
  970.  * split_src - This function takes a linked list of input source
  971.  *             files, concatenates the source and then splits it
  972.  *             into sections of controlled size.
  973.  */
  974. int split_src (SLST *filelist, char *outfname, int length, int width, int tabstop, char sepchar)
  975. {
  976.   char           line[WIDMAX + 1];
  977.   char          *ext     = 0;
  978.   char           filename[MAXFSPEC + 1];
  979.   char           outfile[MAXFSPEC + 1];
  980.   char           sep_ln[32];
  981.   char          *sptr    = 0;
  982.   int            curpart = 0;
  983.   int            key     = 0;
  984.   int            lines   = 0;
  985.   int            maxpart = 1;
  986.   int            retc    = 0;
  987.   int            temp    = 0;
  988.   unsigned short crc     = 0;
  989.  
  990.  
  991.   strcpy (outfile, outfname);
  992.  
  993.   if (NULL == (ext = strchr (outfile, '.')))     /* ignore any ext */
  994.     ext = &outfile[strlen (outfile)];
  995.  
  996.   *ext = 0;                                      /* make temp file name */
  997.   strcpy (tempfile, outfile);
  998.   strcat (tempfile, ".CSP");
  999.  
  1000.   if (NULL == (ftmp = fopen (tempfile, "w")))
  1001.   {
  1002.     printf ("Error creating temp file:  %s\n", tempfile);
  1003.     return FILEIO;
  1004.   }
  1005.  
  1006.   for (temp = 0; temp < 10; ++temp)
  1007.     sep_ln[temp] = (char)sepchar;
  1008.  
  1009.   sep_ln[temp] = '\0';
  1010.  
  1011.   for (cur = filelist, lines = 0; NULL != cur; cur = cur->next)
  1012.   {
  1013.     if (NULL == finp)
  1014.     {
  1015.       if (NULL == (finp = fopen (cur->srcfile, "r")))
  1016.       {
  1017.         printf ("Error opening source file:  %s\n", cur->srcfile);
  1018.         return FILEIO;
  1019.       }
  1020.  
  1021.       if ((NULL != (sptr = strrchr (cur->srcfile, '\\'))) ||
  1022.           (NULL != (sptr = strrchr (cur->srcfile, ':')))) /* Darin McBride */
  1023.         strcpy (filename, sptr+1);
  1024.       else
  1025.         strcpy (filename, cur->srcfile);
  1026.  
  1027.       retc = fprintf (ftmp, "%s %s%s%s %s\n", sep_ln, SEP_ID, SEP_BF, filename, sep_ln);
  1028.  
  1029.       if (0 == retc)
  1030.       {
  1031.         puts ("Error writing output\n");
  1032.         return FILEIO;
  1033.       }
  1034.       ++lines;
  1035.     }
  1036.  
  1037.     while (NULL != finp)
  1038.     {
  1039.       /*
  1040.        * The function csp_fgets() is equivalent to fgets() in that it
  1041.        * too reads n-1 characters or up to a newline character.  This
  1042.        * function additionally expands TAB characters, deletes trailing
  1043.        * whitespace and wraps lines exceeding the specified length.
  1044.        */
  1045.       if (NULL == csp_fgets (line, width, finp, tabstop))
  1046.       {
  1047.         if (feof (finp))
  1048.         {
  1049.           fclose (finp);
  1050.           finp = NULL;
  1051.           retc = fprintf (ftmp, "%s %s%s%s %s\n", sep_ln, SEP_ID, SEP_EF, filename, sep_ln);
  1052.  
  1053.           if (0 == retc)
  1054.           {
  1055.             puts ("Error writing output\n");
  1056.             return FILEIO;
  1057.           }
  1058.           ++lines;      /* adjust line count */
  1059.         }
  1060.         else
  1061.         {
  1062.           puts ("Error reading input\n");
  1063.           return FILEIO;
  1064.         }
  1065.       }
  1066.       else
  1067.       {
  1068.         if (EOF == fputs (line, ftmp))
  1069.         {
  1070.           puts ("Error writing output\n");
  1071.           return FILEIO;
  1072.         }
  1073.         ++lines;
  1074.       }
  1075.     } /*end_while*/
  1076.   } /*end_for*/
  1077.  
  1078.   if (0 != length)
  1079.   {
  1080.     /* There are 3 lines of overhead per section. */
  1081.     maxpart = lines / (length - 3);
  1082.  
  1083.     if ((lines % (length - 3)) > 0)
  1084.       ++maxpart;                       /* for partial section */
  1085.   }
  1086.  
  1087.   curpart = 1;
  1088.   sprintf (ext, ".%03d", curpart);     /* make 1st output file name */
  1089.  
  1090.   /* warn user if the output filename is already in use */
  1091.   if (NULL != (fout = fopen (outfile, "r")))
  1092.   {
  1093.     key = 0;
  1094.     printf ("Output file already exists:  %s\n", outfile);
  1095.  
  1096.     do
  1097.     {
  1098.       printf ("Overwrite? (y/n): ");
  1099.       key = (toupper) (getchar ());    /* prevent using toupper macro */
  1100.       puts ("");
  1101.       temp = key;
  1102.  
  1103.       while (temp != '\n')
  1104.       {
  1105.         temp = getchar ();             /* eat all extra keystrokes */
  1106.       }
  1107.  
  1108.       if ('N' == key)
  1109.         return ABORT;
  1110.  
  1111.     } while ('Y' != key);
  1112.  
  1113.     fclose (fout);
  1114.     fout = NULL;
  1115.   }
  1116.  
  1117.   if (NULL == freopen (tempfile, "r", ftmp))
  1118.   {
  1119.     printf ("Error reopening temp file:  %s\n", tempfile);
  1120.     return FILEIO;
  1121.   }
  1122.  
  1123.   initcrctab ();
  1124.  
  1125.   while (NULL != ftmp)
  1126.   {
  1127.     lines = 0;
  1128.  
  1129.     if (NULL == fout)
  1130.     {
  1131.       sprintf (ext, ".%03d", curpart); /* make output file name */
  1132.  
  1133.       if (NULL == (fout = fopen (outfile, "w")))
  1134.       {
  1135.         printf ("Error opening output file:  %s\n", outfile);
  1136.         return FILEIO;
  1137.       }
  1138.  
  1139.       retc = fprintf (fout, "%s %s%s%s %s\n", sep_ln, SEP_ID, SEP_VR, VERSION, sep_ln);
  1140.  
  1141.       if (0 == retc)
  1142.       {
  1143.         puts ("Error writing output\n");
  1144.         return FILEIO;
  1145.       }
  1146.  
  1147.       ++lines;
  1148.       retc = fprintf (fout, "%s %s%s%d/%d %s\n", sep_ln, SEP_ID, SEP_BP, curpart, maxpart, sep_ln);
  1149.  
  1150.       if (0 == retc)
  1151.       {
  1152.         puts ("Error writing output\n");
  1153.         return FILEIO;
  1154.       }
  1155.  
  1156.       ++lines;
  1157.     }
  1158.  
  1159.     crc = 0;
  1160.  
  1161.     while (((0 == length ) || (lines < (length - 1))) &&
  1162.            (NULL != ftmp))
  1163.     {
  1164.       if (NULL == fgets (line, WIDMAX, ftmp))
  1165.       {
  1166.         if (feof (ftmp))
  1167.         {
  1168.           fclose (ftmp);
  1169.           ftmp = NULL;
  1170.         }
  1171.         else
  1172.         {
  1173.           puts ("Error reading input\n");
  1174.           return FILEIO;
  1175.         }
  1176.       }
  1177.       else
  1178.       {
  1179.         crc = updcrc (crc, (unsigned char *)line, strlen (line));
  1180.  
  1181.         if (EOF == fputs (line, fout))
  1182.         {
  1183.           puts ("Error writing output\n");
  1184.           return FILEIO;
  1185.         }
  1186.  
  1187.         ++lines; /* increment line count */
  1188.       }
  1189.     } /*end_while*/
  1190.  
  1191.     if (0 == fprintf (fout, "%s %s%s%d/%d crc: %04x %s\n", sep_ln, SEP_ID, SEP_EP, curpart, maxpart, crc, sep_ln))
  1192.     {
  1193.       puts ("Error writing output\n");
  1194.       return FILEIO;
  1195.     }
  1196.  
  1197.     fclose (fout);
  1198.     fout = NULL;
  1199.     ++curpart;
  1200.   } /*end_while*/
  1201.  
  1202.   return NOERR;
  1203. }
  1204.  
  1205.  
  1206. /*
  1207.  * CRC-16f.c, from Snippets.  Calculate, intelligently, the CRC
  1208.  * of a dataset incrementally given a buffer full at a time.
  1209.  * Initialize crc to 0 for XMODEM, -1 for CCITT.
  1210.  */
  1211.  
  1212. /*
  1213.  * P, the CRC polynomial, is used by XMODEM (almost CCITT).
  1214.  * If you change P, you must change crctab[]'s initial value
  1215.  * to what is printed by initcrctab().
  1216.  */
  1217. #define  P 0x1021
  1218. #define  W 16       /* number of bits in CRC: don't change it */
  1219. #define  B 8        /* number of bits per char: don't change it */
  1220.  
  1221. unsigned short crctab[1<<B];
  1222.  
  1223. void initcrctab (void)
  1224. {
  1225.   register b, v, i;
  1226.  
  1227.   for (b = 0; b <= (1<<B) - 1; ++b)
  1228.   {
  1229.     for (v = b<<(W-B), i = B; --i >= 0; )
  1230.     {
  1231.       v = v & 0x8000 ? (v<<1)^P : v<<1;
  1232.     }
  1233.  
  1234.     crctab[b] = v;
  1235.   }
  1236. }
  1237.  
  1238. unsigned short updcrc (unsigned short icrc, unsigned char *icp, unsigned int icnt)
  1239. {
  1240.   register unsigned short crc = icrc;
  1241.   register unsigned char  *cp = icp;
  1242.   register unsigned int   cnt = icnt;
  1243.  
  1244.   while (cnt--)
  1245.   {
  1246.     crc = (crc<<B) ^ crctab[(crc>>(W-B)) ^ *cp++];
  1247.   }
  1248.  
  1249.   return crc;
  1250. }
  1251.  
  1252.