home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 2 BBS / 02-BBS.zip / csplit21.zip / CSPLIT.C next >
C/C++ Source or Header  |  1995-11-24  |  36KB  |  1,219 lines

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