home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 329_01 / cut.c < prev    next >
C/C++ Source or Header  |  1988-12-16  |  11KB  |  517 lines

  1. /*-
  2.  * cut - a recreation of the Unix(Tm) cut(1) command.
  3.  *
  4.  * syntax:  cut -cLIST[ file1 ...]
  5.  *        cut -fLIST [-d char][ -s][ file1 ...]
  6.  *
  7.  *    Copyright (C) 1984 by David M. Ihnat
  8.  *
  9.  * This program is a total rewrite of the Bell Laboratories Unix(Tm)
  10.  * command of the same name, as of System V.  It contains no proprietary
  11.  * code, and therefore may be used without violation of any proprietary
  12.  * agreements whatsoever.  However, you will notice that the program is
  13.  * copyrighted by me.  This is to assure the program does *not* fall
  14.  * into the public domain.  Thus, I may specify just what I am now:
  15.  * This program may be freely copied and distributed, provided this notice
  16.  * remains; it may not be sold for profit without express written consent of
  17.  * the author.
  18.  * Please note that I recreated the behavior of the Unix(Tm) 'cut' command
  19.  * as faithfully as possible; however, I haven't run a full set of regression
  20.  * tests.  Thus, the user of this program accepts full responsibility for any
  21.  * effects or loss; in particular, the author is not responsible for any losses, * explicit or incidental, that may be incurred through use of this program.
  22.  *
  23.  * I ask that any bugs (and, if possible, fixes) be reported to me when
  24.  * possible.  -David Ihnat (312) 784-4544 ihuxx!ignatz
  25. **
  26. **    Roberto Artigas Jr
  27. **    P.O. Box 281415
  28. **    Memphis, TN 38168-1415
  29. **    work: 901-762-6092
  30. **    home: 901-373-4738
  31. **
  32. **    1988.12.08 - Get to run under OS/2.
  33. **        Used C/2 version 1.10 under OS/2 E 1.1
  34. **        cl -c -AL cut.c
  35. **        link cut,/noi,cut,llibce+os2, ;
  36. **
  37. **    Worked out includes
  38. **    Fixed command line processing problem
  39. **+
  40. */
  41. #define    CPM    0
  42. #define    DOS    0
  43. #define    OS2    1
  44.  
  45. #include    <stdio.h>
  46.  
  47. #if    CPM
  48. extern int errno;
  49. #endif
  50.  
  51. #if    CPM
  52. #define    _MAXSZ        512
  53. #else
  54. #define    _MAXSZ        (BUFSIZ*4)
  55. #endif
  56.  
  57. #if    DOS || OS2
  58. #include    <ctype.h>
  59. #include    <stdlib.h>
  60. #include    <string.h>
  61. #endif
  62.  
  63. /* I'd love to use enums, but not everyone has them.  Portability, y'know. */
  64. #define BADLIST        1
  65. #define NODELIM        2
  66. #define NOFIELDS    3
  67. #define USAGE        4
  68. #define BADFILE        5
  69. #define BACKERR        6
  70. #define TOOLONG        7
  71.  
  72. #define    TAB    '\t'
  73. #define BACKSP    0x8
  74. #define COMMAND "cut"
  75.  
  76. #define    IGNOREIT    0
  77. #define CUTIT        1
  78.  
  79. char outbuf[_MAXSZ];               /* Processed output buffer */
  80. char rawbuf[_MAXSZ];               /* Raw holding buffer for field mode */
  81. #define    FLDFLAG    fields[0]
  82. short int fields[_MAXSZ];           /* Max number of fields or line
  83.                         * length */
  84.  
  85. char *cmdnam;
  86.  
  87. short int cflag,
  88.  fflag,
  89.  sflag;
  90. char delim = TAB;
  91.  
  92. /*
  93.  * Function prototypes 
  94.  */
  95. void dofile();
  96. void prerr();
  97. int setflds();
  98.  
  99. /*
  100.  * Begin program here 
  101.  */
  102. int 
  103. main(argc, argv)
  104. int argc;
  105. char **argv;
  106. {
  107.     FILE *fileptr;
  108.     FILE *fopen();
  109.     int filecnt;
  110.  
  111.     cflag = fflag = sflag = 0;
  112.  
  113. #if    CPM
  114.     cmdnam = COMMAND;
  115. #else
  116.     cmdnam = *argv;
  117. #endif
  118.  
  119.     /* Skip invocation name */
  120.     argv++;
  121.     argc--;
  122.  
  123.     if (!argc)
  124.     prerr(USAGE, NULL);
  125.  
  126.     /* Most compilers initialize storage to zero; but don't count on it. */
  127.  
  128.     for (filecnt = 0; filecnt < _MAXSZ; filecnt++)
  129.     fields[filecnt] = IGNOREIT;
  130.  
  131.     /* First, parse input options */
  132.  
  133.     while (argv[0] && argv[0][0] == '-')
  134.     {
  135.     switch (argv[0][1])
  136.     {
  137.     case 'c':
  138.     case 'C':
  139.         /* Build the character position list */
  140.         if (fflag || cflag)
  141.         prerr(USAGE, NULL);
  142.         else
  143.         {
  144.         cflag++;
  145.         setflds(&argv[0][2]);
  146.         }
  147.         break;
  148.  
  149.     case 'f':
  150.     case 'F':
  151.         /* Build the field position list */
  152.         if (fflag || cflag)
  153.         prerr(USAGE, NULL);
  154.         else
  155.         {
  156.         fflag++;
  157.         setflds(&argv[0][2]);
  158.         }
  159.         break;
  160.  
  161.     case 'd':
  162.     case 'D':
  163.         /* New delimiter */
  164.         delim = argv[0][2];
  165.         if (delim == '\0')
  166.         prerr(NODELIM, NULL);
  167.  
  168.         break;
  169.  
  170.     case 's':
  171.     case 'S':
  172.         sflag++;
  173.         break;
  174.  
  175.     default:
  176.         prerr(USAGE, NULL);
  177.     }
  178.     argv++;
  179.     argc--;
  180.     }
  181.  
  182.     /* Finished all setup.  If no fields selected, tell them and exit. */
  183.     if (!(cflag | fflag))
  184.     prerr(BADLIST, NULL);
  185.  
  186.     if (!FLDFLAG)
  187.     prerr(NOFIELDS, NULL);
  188.  
  189.     /*
  190.      * If no files specified, process stdin.  Otherwise, process on a
  191.      * file-by-file basis. 
  192.      */
  193.     if (argc == 0)
  194.     dofile(stdin);
  195.     else
  196.     for (filecnt = 0; filecnt < argc; filecnt++, argv++)
  197. #if    DOS || OS2
  198.         if ((fileptr = fopen(argv[0], "rb")) == (FILE *) NULL)
  199. #else
  200.         if ((fileptr = fopen(argv[0], "r")) == (FILE *) NULL)
  201. #endif
  202.         prerr(BADFILE, argv);
  203.         else
  204.         {
  205.         dofile(fileptr);
  206.         fclose(fileptr);
  207.         }
  208.     return (0);
  209. }
  210.  
  211. int 
  212. setflds(fldstr)
  213. char *fldstr;
  214. {
  215.     /*
  216.      * The string, character or field, must have one of the following
  217.      * formats: 
  218.      *
  219.      * n n,m[,...]    where n<m a-b        where a<b -n,m        where n<m;
  220.      * implies 1-n n-        where - implies to end of line or last field 
  221.      */
  222.     int index,
  223.      minflag,
  224.      value,
  225.      fldset;
  226.  
  227.     minflag = 0;
  228.     value = 0;
  229.     index = 1;
  230.     FLDFLAG = 0;
  231.  
  232.     for (;;)
  233.     {
  234.     switch (*fldstr)
  235.     {
  236.     case '-':
  237.         /* Starting a range */
  238.         if (minflag)
  239.         prerr(BADLIST, NULL);
  240.         minflag++;
  241.         fldstr++;
  242.  
  243.         if (value)
  244.         {
  245.         if (value >= _MAXSZ)
  246.             prerr(BADLIST, NULL);
  247.  
  248.         index = value;
  249.         } else
  250.         index = 1;
  251.  
  252.         value = 0;
  253.         break;
  254.  
  255.     case ',':
  256.     case '\0':
  257.         /* Ending the string, or this field/column sublist */
  258.         if (minflag)           /* Some damnable range */
  259.         {                   /* Ranges are nasty.  Possibles:
  260.                         * -n,a-n,n-.  In any case, index
  261.                         * contains the start of the range. */
  262.         if (!value)
  263.         {               /* From index to EOL */
  264.  
  265.             FLDFLAG = index;
  266.             fldset++;
  267.             value = 0;
  268.         } else
  269.         {
  270.             if (value >= _MAXSZ)
  271.             prerr(BADLIST, NULL);
  272.  
  273.             if (value < index)
  274.             prerr(BADLIST, NULL);
  275.  
  276.             /* Already a TOEOL sequence? */
  277.             if (FLDFLAG)
  278.             {
  279.             /*
  280.              * Yes.  Now...is the new sequence already contained
  281.              * by the old one? If so, no processing is
  282.              * necessary. 
  283.              */
  284.             if (FLDFLAG > index)
  285.             {
  286.                 /*
  287.                  * No, the new sequence starts before the old.
  288.                  * Does the range extend into the current EOL
  289.                  * range? If so, simply move the EOL marker. 
  290.                  */
  291.                 if (FLDFLAG < value)
  292.                 {
  293.                 FLDFLAG = index;
  294.                 } else
  295.                 /* Simple range. Fill it. */
  296.                 for (; index <= value; index++)
  297.                     fields[index] = CUTIT;
  298.  
  299.                 /* In any case, some fields were selected. */
  300.                 fldset++;
  301.             }
  302.             } else           /* Ok, no TOEOL sequence */
  303.             {
  304.             for (; index <= value; index++)
  305.             {
  306.                 fields[index] = CUTIT;
  307.             }
  308.             fldset++;
  309.             }
  310.             value = 0;
  311.         }
  312.         minflag = 0;           /* Reset the field-in-progress flag. */
  313.         } else
  314.         if (value)
  315.         {
  316.         if (value >= _MAXSZ)
  317.             prerr(BADLIST, NULL);
  318.  
  319.         fields[value] = CUTIT;
  320.         value = 0;
  321.         fldset++;
  322.         }
  323.         if (*fldstr == '\0')
  324.         {
  325.         /*
  326.          * Last bit of processing.  If there was an EOL, fill the
  327.          * array from the EOL point.  In any case, if there were any
  328.          * fields selected, leave the FLDFLAG value non-zero on
  329.          * return. 
  330.          */
  331.         if (FLDFLAG)
  332.             for (index = FLDFLAG; index < _MAXSZ; index++)
  333.             fields[index] = CUTIT;
  334.  
  335.         if (fldset)
  336.             FLDFLAG = 1;
  337.  
  338.         return (0);
  339.         }
  340.         fldstr++;
  341.         break;
  342.  
  343.     default:
  344.         if ((*fldstr < '0') || (*fldstr > '9'))
  345.         prerr(BADLIST, NULL);
  346.  
  347.         else
  348.         {
  349.         value = 10 * value + *fldstr - '0';
  350.         fldstr++;
  351.         }
  352.     }
  353.     }
  354. }
  355.  
  356. void 
  357. dofile(fno)
  358. FILE *fno;
  359. {
  360.     /*
  361.      * This will process the input files according to the rules specified in
  362.      * the fields array. 
  363.      */
  364.  
  365.     int charcnt,
  366.      poscnt,
  367.      bflag,
  368.      doneflag,
  369.      fldfound;
  370.     register int c;
  371.  
  372.     char *inbufptr,
  373.     *rawbufptr;
  374.  
  375.     do
  376.     {
  377.     inbufptr = outbuf;
  378.     rawbufptr = rawbuf;
  379.     charcnt = bflag = doneflag = fldfound = 0;
  380.     poscnt = 1;
  381.  
  382.     do
  383.     {
  384.         c = fgetc(fno);
  385.         if (c == EOF)
  386.         {
  387.         /* That's it for this file or stream */
  388.         doneflag++;
  389.         break;
  390.         }
  391.         if (cflag)
  392.         {
  393.         /*
  394.          * In character scan mode.  Look to see if it's an
  395.          * NROFF-type underlined character; if so, then don't count
  396.          * the backspace. Notice that this could cause a buffer
  397.          * overflow in the worst case situation... but that's MOST
  398.          * unlikely. 
  399.          */
  400.  
  401.         if (c == BACKSP)
  402.         {
  403.             if (bflag)
  404.             prerr(BACKERR);
  405.             else
  406.             {
  407.             bflag++;
  408.             *inbufptr++ = (char) c;
  409.             }
  410.         } else
  411.         {
  412.             /*
  413.              * Valid character.  If it's to be sent, stow it in the
  414.              * outbuffer. 
  415.              */
  416.             bflag = 0;
  417.  
  418.             if (++charcnt == (_MAXSZ - 1))
  419.             prerr(TOOLONG);
  420.  
  421.             if (fields[charcnt] && (c != '\n'))
  422.             *inbufptr++ = (char) c;
  423.         }
  424.         } else
  425.         {
  426.         /*
  427.          * Field processing.  In this case, charcnt does indicate
  428.          * processed characters on the current line, but that is
  429.          * all.  Notice that ALL characters are initially stowed in
  430.          * the raw  buffer, until at least one field has been found. 
  431.          */
  432.         if (fields[poscnt])
  433.         {
  434.             /*
  435.              * Ok, working on a field.  It, and its terminating
  436.              * delimiter, go only into the processed buffer. 
  437.              */
  438.             fldfound = 1;
  439.             if (c != '\n')
  440.             *inbufptr++ = (char) c;
  441.         } else
  442.         if (!fldfound)
  443.         {
  444.             charcnt++;
  445.             if (c != '\n')
  446.             *rawbufptr++ = (char) c;
  447.         }
  448.         /*
  449.          * In any case, if a delimiter, bump the field indicator. 
  450.          */
  451.         if (c == delim)
  452.             poscnt++;
  453.         }
  454.     } while (c != '\n');
  455.  
  456.     if ((cflag && charcnt) || (fflag && fldfound))
  457.     {
  458.         /*
  459.          * No matter what mode, something was found. Print it. 
  460.          */
  461.  
  462.         if (fflag && (*(inbufptr - 1) == delim))
  463.         --inbufptr;           /* Supress trailing delimiter */
  464.  
  465.         *inbufptr = '\0';           /* But null-terminate the line. */
  466.         puts(outbuf);
  467.     } else
  468.     if ((fflag && (!sflag)) && charcnt)
  469.     {
  470.         /*
  471.          * In this case, a line with some characters, no delimiters, and
  472.          * no supression.  Print it. 
  473.          */
  474.  
  475.         *rawbufptr = '\0';
  476.         puts(rawbuf);
  477.     }
  478.     } while (!doneflag);
  479. }
  480.  
  481. void 
  482. prerr(etype, estring)
  483. int etype;
  484. char *estring;
  485. {
  486.     switch (etype)
  487.     {
  488.     case BADLIST:
  489.     fprintf(stderr, "%s : bad list for c/f option\n", cmdnam);
  490.     break;
  491.  
  492.     case USAGE:
  493.     fprintf(stderr, "Usage: %s [-s] [-d<char>] {-c<list> | -f<list>} [file ...]\n", cmdnam);
  494.     break;
  495.  
  496.     case NOFIELDS:
  497.     fprintf(stderr, "%s : no fields\n", cmdnam);
  498.     break;
  499.  
  500.     case NODELIM:
  501.     fprintf(stderr, "%s : no delimiter\n", cmdnam);
  502.     break;
  503.  
  504.     case BADFILE:
  505.     fprintf(stderr, "Cannot open: %s : %s\n", cmdnam, estring);
  506.     break;
  507.  
  508.     case BACKERR:
  509.     fprintf(stderr, "%s : cannot handle multiple adjacent backspaces\n", cmdnam);
  510.     break;
  511.  
  512.     case TOOLONG:
  513.     fprintf(stderr, "%s : line too long\n", cmdnam);
  514.     }
  515.     exit(2);
  516. }
  517.