home *** CD-ROM | disk | FTP | other *** search
/ Borland Programmer's Resource / Borland_Programmers_Resource_CD_1995.iso / ntcode / gnugrep / fgrep.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-19  |  14.4 KB  |  721 lines

  1. /* fgrep.c - grep program built around matcher.
  2.    Copyright 1989 Free Software Foundation
  3.           Written August 1989 by Mike Haertel.
  4.  
  5.    This program is free software; you can redistribute it and/or modify
  6.    it under the terms of the GNU General Public License as published by
  7.    the Free Software Foundation; either version 1, or (at your option)
  8.    any later version.
  9.  
  10.    This program is distributed in the hope that it will be useful,
  11.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.    GNU General Public License for more details.
  14.  
  15.    You should have received a copy of the GNU General Public License
  16.    along with this program; if not, write to the Free Software
  17.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19.    The author may be reached (Email) at the address mike@ai.mit.edu,
  20.    or (US mail) as Mike Haertel c/o Free Software Foundation. */
  21.  
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <ctype.h>
  25. #include <errno.h>
  26. #include <fcntl.h>
  27. #include <limits.h>
  28. #include <string.h>
  29. #ifdef __TURBOC__
  30. #include <alloc.h>
  31. #else
  32. #include <malloc.h>
  33. #endif /*__TURBOC__*/
  34.  
  35. #include "kwset.h"
  36.  
  37. #define NCHAR (UCHAR_MAX + 1)
  38.  
  39. #if defined(__TURBOC__) || defined(__BORLANDC__)
  40. int _stklen = 16384;
  41. #endif
  42.  
  43. /*  from getopt()  */
  44. extern char *optarg;
  45. extern int optind;
  46.  
  47. /* For error messages. */
  48. static int error_seen;
  49.  
  50. /* Flags controlling the style of output. */
  51. static int out_silent;        /* Suppress all normal output. */
  52. static int out_invert;        /* Print nonmatching stuff. */
  53. static int out_file;        /* Print filenames. */
  54. static int out_line;        /* Print line numbers. */
  55. static int out_byte;        /* Print byte offsets. */
  56. static int out_before;        /* Lines of leading context. */
  57. static int out_after;        /* Lines of trailing context. */
  58.  
  59. /* Print MESG and possibly the error string for ERRNUM.  Remember
  60.    that something awful happened. */
  61. static void
  62. error(mesg, errnum)
  63. const char *mesg;
  64. int errnum;
  65. {
  66.     if ( errnum )
  67.         fprintf(stderr, "fgrep: %s: %s\n", mesg, strerror(errnum));
  68.     else
  69.         fprintf(stderr, "fgrep: %s\n", mesg);
  70.     error_seen = 1;
  71. }
  72.  
  73. /* Like error(), but die horribly after printing. */
  74. static void
  75. fatal(mesg, errnum)
  76. const char *mesg;
  77. int errnum;
  78. {
  79.     error(mesg, errnum);
  80.     exit(2);
  81. }
  82.  
  83. /* Interface to handle errors and fix library lossage. */
  84. static void *
  85. xmalloc(size)
  86. size_t size;
  87. {
  88.     void *result;
  89.  
  90.     result = malloc(size);
  91.     if ( size && !result )
  92.         fatal("memory exhausted", 0);
  93.     return ( result );
  94. }
  95.  
  96. /* Interface to handle errors and fix some library lossage. */
  97. static void *
  98. xrealloc(ptr, size)
  99. void *ptr;
  100. size_t size;
  101. {
  102.     void *result;
  103.  
  104.     if ( ptr )
  105.         result = realloc(ptr, size);
  106.     else
  107.         result = malloc(size);
  108.     if ( size && !result )
  109.         fatal("memory exhausted", 0);
  110.     return ( result );
  111. }
  112.  
  113. /* Compiled search pattern. */
  114. kwset_t kwset;
  115.  
  116. /* Flags controlling how pattern matching is performed. */
  117. static int match_fold;        /* Fold all letters to one case. */
  118. static int match_words;        /* Match only whole words. */
  119. static int match_lines;        /* Match only whole lines. */
  120.  
  121. static void
  122. compile(pattern, size)
  123. const char *pattern;
  124. size_t size;
  125. {
  126.     const char *beg;
  127.     const char *lim;
  128.     const char *err;
  129.     static char trans[NCHAR];
  130.     int i;
  131.  
  132.     if ( match_fold )
  133.         for ( i = 0; i < NCHAR; i++ )
  134.             trans[i] = tolower(i);
  135.  
  136.     if ( (kwset = kwsalloc(match_fold ? trans : NULL)) == (kwset_t *)0 )
  137.         fatal("memory exhausted", 0);
  138.  
  139.     beg = pattern;
  140.     do
  141.     {
  142.         for ( lim = beg; (lim < (pattern + size)) && (*lim != '\n');
  143.                                     lim++ )
  144.             ;
  145.         if ( (err = kwsincr(kwset, beg, lim - beg)) != NULL )
  146.             fatal(err, 0);
  147.         if ( lim < (pattern + size) )
  148.             lim++;
  149.         beg = lim;
  150.     }
  151.     while ( beg < (pattern + size) )
  152.         ;
  153.     if ( (err = kwsprep(kwset)) != NULL )
  154.         fatal(err, 0);
  155. }
  156.  
  157. static char *
  158. execute(buf, size)
  159. char *buf;
  160. size_t size;
  161. {
  162.     char *beg;
  163.     char *try;
  164.     size_t len;
  165.     struct kwsmatch kwsmatch;
  166.  
  167.     for ( beg = buf; beg <= (buf + size); beg++ )
  168.     {
  169.         if ( (beg = kwsexec(kwset, beg, buf + size - beg, &kwsmatch))
  170.                                 == NULL )
  171.             return ( NULL );
  172.         len = kwsmatch.size[0];
  173.         if ( match_lines )
  174.         {
  175.             /* Check that the match starts at the beginning of a line */
  176.             if ( (beg > buf) && (beg[-1] != '\n') )
  177.                 continue;
  178.  
  179. #ifndef __MSDOS__
  180.             /* Check that the match ends at the end of a line */
  181.             if ( ((beg + len) < (buf + size)) &&
  182.                  (*(beg + len) != '\n') )
  183.                 continue;
  184. #else
  185.             /* Check that the match ends at the end of a line */
  186.             /* A line can end with either '\n' or '\r\n' */
  187.             {
  188.             char *p = beg + len;
  189.             char *q = buf + size;
  190.  
  191.             if ( p < q )
  192.                 switch ( *p )
  193.                 {
  194.                 case '\n':
  195.                     break;
  196.                 case '\r':
  197.                     if ( ((p + 1) < q) &&
  198.                          (*(p + 1) != '\n') )
  199.                         continue;
  200.                     break;
  201.                 default:
  202.                     continue;
  203.                 }
  204.             }
  205. #endif
  206.  
  207.             return ( beg );
  208.         }
  209.         else if ( match_words )
  210.             for ( try = beg; len && try; )
  211.             {
  212.                 if ( (try > buf) &&
  213.                      (isalnum(try[-1]) || !isalnum(*try)) )
  214.                     goto retry;
  215.                 if ( ((try + len) < (buf + size)) &&
  216.                      (isalnum(try[len]) || !isalnum(try[len - 1])) )
  217.                     goto retry;
  218.                 return ( try );
  219. retry:
  220.                 if ( --len )
  221.                     try = kwsexec(kwset, beg, len, &kwsmatch);
  222.                 else
  223.                     break;
  224.                 len = kwsmatch.size[0];
  225.             }
  226.         else
  227.             return ( beg );
  228.     }
  229.  
  230.     return ( NULL );
  231. }
  232.  
  233. /* Hairy buffering mechanism to efficiently support all the options. */
  234. static char *bufbeg;        /* Beginning of user-visible portion. */
  235. static char *buflim;        /* Limit of user-visible portion. */
  236. static char *buf;        /* Pointer to base of buffer. */
  237. static size_t bufalloc;        /* Allocated size of buffer. */
  238. static size_t bufcc;        /* Count of characters in buffer. */
  239. static unsigned long int buftotalcc;
  240.                 /* Total character count since reset. */
  241. static char *buflast;        /* Pointer after last character printed. */
  242. static int bufgap;        /* Weird flag indicating buflast is a lie. */
  243. static unsigned long int buftotalnl;
  244.                 /* Count of newlines before last character. */
  245. static int bufpending;        /* Lines of pending output at buflast. */
  246. static int bufdesc;        /* File descriptor to read from. */
  247. static int bufeof;        /* Flag indicating EOF reached. */
  248. static const char *buffile;    /* File name for messages. */
  249.  
  250. /* Scan and count the newlines prior to LIM in the buffer. */
  251. static void
  252. nlscan(lim)
  253. char *lim;
  254. {
  255.     char *p;
  256.  
  257.     for ( p = buflast; p < lim; p++ )
  258.         if ( *p == '\n' )
  259.             buftotalnl++;
  260.     buflast = lim;
  261. }
  262.  
  263. /* Print the line beginning at BEG, using SEP to separate optional label
  264.    fields from the text of the line.  Return the size of the line. */
  265. static size_t
  266. prline(beg, sep)
  267. register char *beg;
  268. register char sep;
  269. {
  270.   register size_t cc;
  271.   register char c;
  272.   static int err;
  273.   
  274.   cc = 0;
  275.  
  276.   if (out_silent || err)
  277.     while (beg < buflim)
  278.       {
  279.     ++cc;
  280.     if (*beg++ == '\n')
  281.       break;
  282.       }
  283.   else
  284.     {
  285.       if (out_file)
  286.     printf("%s%c", buffile, sep);
  287.       if (out_line)
  288.     {
  289.       nlscan(beg);
  290.       printf("%lu%c", buftotalnl + 1, sep);
  291.     }
  292.       if (out_byte)
  293.     printf("%lu%c", buftotalcc + (beg - buf), sep);
  294.  
  295.       while (beg < buflim)
  296.     {
  297.       ++cc;
  298.       c = *beg++;
  299. #ifndef __MSDOS__
  300.       putchar(c);
  301. #else
  302.       /* Convert any CR-LF sequences to \n */
  303.       if (c != '\r' || (beg < buflim && *beg != '\n'))
  304.         putchar(c);
  305. #endif
  306.       if (c == '\n')
  307.         break;
  308.     }
  309.       if (ferror(stdout))
  310.     {
  311.       error("output error", errno);
  312.       err = 1;
  313.     }
  314.     }
  315.  
  316.   if (out_line)
  317.     nlscan(beg);
  318.   else
  319.     buflast = beg;
  320.   bufgap = 0;
  321.  
  322.   return cc;
  323. }
  324.  
  325. /* Print pending bytes of last trailing context prior to LIM. */
  326. static void
  327. prpending(lim)
  328. register char *lim;
  329. {
  330.   while (buflast < lim && bufpending)
  331.     {
  332.       --bufpending;
  333.       prline(buflast, '-');
  334.     }
  335. }
  336.  
  337. /* Print the lines between BEG and LIM.  Deal with context crap.
  338.    Return the count of lines between BEG and LIM. */
  339. static unsigned long
  340. prtext(beg, lim)
  341. char *beg;
  342. char *lim;
  343. {
  344.     static int used;
  345.     char *p;
  346.     int i;
  347.     unsigned long n;
  348.  
  349.     prpending(beg);
  350.  
  351.     p = beg;
  352.     for ( i = 0; i < out_before; i++ )
  353.         if ( p > buflast )
  354.             do
  355.                 p--;
  356.             while ( (p > buflast) && (p[-1] != '\n') );
  357.  
  358.     if ( (out_before || out_after) && used && ((p > buflast) || bufgap) )
  359.         puts("--");
  360.  
  361.     while ( p < beg )
  362.         p += prline(p, '-');
  363.  
  364.     n = 0;
  365.     while ( p < lim )
  366.     {
  367.         n++;
  368.         p += prline(p, ':');
  369.     }
  370.  
  371.     bufpending = out_after;
  372.     used = 1;
  373.  
  374.     return ( n );
  375. }
  376.  
  377. /* Fill the user-visible portion of the buffer, returning a byte count. */
  378. static int
  379. fillbuf()
  380. {
  381.   register char *b, *d, *l;
  382.   int i, cc;
  383.   size_t discard, save;
  384.  
  385.   prpending(buflim);
  386.  
  387.   b = buflim;
  388.   for (i = 0; i < out_before; ++i)
  389.     if (b > buflast)
  390.       do
  391.     --b;
  392.       while (b > buflast && b[-1] != '\n');
  393.  
  394.   if (buflast < b)
  395.     bufgap = 1;
  396.   if (out_line)
  397.     nlscan(b);
  398.  
  399.   discard = b - buf;
  400.   save = buflim - b;
  401.  
  402.   if (b > buf)
  403.     {
  404.       d = buf;
  405.       l = buf + bufcc;
  406.       while (b < l)
  407.     *d++ = *b++;
  408.     }
  409.  
  410.   bufcc -= discard;
  411.   buftotalcc += discard;
  412.  
  413.   do
  414.     {
  415.       if (!bufeof)
  416.     {
  417.       if (bufcc > bufalloc / 2)
  418.         buf = xrealloc(buf, bufalloc *= 2);
  419.       cc = read(bufdesc, buf + bufcc, bufalloc - bufcc);
  420.       if (cc < 0)
  421.         {
  422.           error(buffile, errno);
  423.           bufeof = 1;
  424.         }
  425.       else
  426.         {
  427.           bufeof = !cc;
  428.           bufcc += cc;
  429.         }
  430.     }
  431.       bufbeg = buf + save;
  432.       for (l = buf + bufcc; l > bufbeg && l[-1] != '\n'; --l)
  433.     ;
  434.       buflim = l;
  435.       buflast = buf;
  436.     }
  437.   while (!bufeof && bufbeg == buflim);
  438.  
  439.   if (bufeof)
  440.     buflim = buf + bufcc;
  441.  
  442.   return buflim - bufbeg;
  443. }
  444.  
  445. /* One-time initialization. */
  446. static void
  447. initbuf()
  448. {
  449.   bufalloc = 8192;
  450.   buf = xmalloc(bufalloc);
  451. }
  452.  
  453. /* Reset the buffer for a new file. */
  454. static void
  455. resetbuf(desc, file)
  456. int desc;
  457. const char *file;
  458. {
  459.   bufbeg = buf;
  460.   buflim = buf;
  461.   bufcc = 0;
  462.   buftotalcc = 0;
  463.   buflast = buf;
  464.   bufgap = 0;
  465.   buftotalnl = 0;
  466.   bufpending = 0;
  467.   bufdesc = desc;
  468.   bufeof = 0;
  469.   buffile = file;
  470. }
  471.  
  472. /* Scan the user-visible portion of the buffer, calling prtext() for
  473.    matching lines (or between matching lines if OUT_INVERT is true).
  474.    Return a count of lines printed. */
  475. static unsigned long
  476. grepbuf()
  477. {
  478.     unsigned long total;
  479.     char *p;
  480.     char *b;
  481.     char *l;
  482.  
  483.     total = 0;
  484.     p = bufbeg;
  485.     while ( (b = execute(p, buflim - p)) != NULL )
  486.     {
  487.         if ( (b == buflim) &&
  488.              ((b > bufbeg) && (b[-1] == '\n') || (b == bufbeg)) )
  489.             break;
  490.         while ( (b > bufbeg) && (b[-1] != '\n') )
  491.             b--;
  492.         l = b + 1;
  493.         while ( (l < buflim) && (l[-1] != '\n') )
  494.             l++;
  495.         if ( !out_invert )
  496.             total += prtext(b, l);
  497.         else if ( p < b )
  498.             total += prtext(p, b);
  499.         p = l;
  500.     }
  501.     if ( out_invert && (p < buflim) )
  502.         total += prtext(p, buflim);
  503.     return ( total );
  504. }
  505.  
  506. /* Scan the given file, returning a count of lines printed. */
  507. static unsigned long
  508. grep(desc, file)
  509. int desc;
  510. const char *file;
  511. {
  512.     unsigned long total;
  513.  
  514.     total = 0;
  515.  
  516.     resetbuf(desc, file);
  517.  
  518.     while ( fillbuf() )
  519.         total += grepbuf();
  520.  
  521.     return ( total );
  522. }
  523.  
  524. static void
  525. usage()
  526. {
  527.     fprintf(stderr,
  528. "Usage: fgrep [-[AB]<num>] [-[CVchilnsvwx]] [-[ef]] <expr> [<files...>]\n");
  529.     exit(2);
  530. }
  531.  
  532. int
  533. main(argc, argv)
  534. int argc;
  535. char *argv[];
  536. {
  537.     char *keys;
  538.     size_t keycc;
  539.     size_t keyalloc;
  540.     int count_matches;
  541.     int no_filenames;
  542.     int list_files;
  543.     int opt;
  544.     int cc;
  545.     int desc;
  546.     int status;
  547.     unsigned long count;
  548.     FILE *fp;
  549.  
  550.     setmode(fileno(stdin), O_BINARY);
  551.     setmode(fileno(stdout), O_TEXT);
  552.  
  553.     keys = NULL;
  554.     count_matches = 0;
  555.     no_filenames = 0;
  556.     list_files = 0;
  557.  
  558.     while ( (opt = getopt(argc, argv, "0123456789A:B:CVbce:f:hilnsvwxy"))
  559.                                 != EOF )
  560.         switch (opt)
  561.         {
  562.         case '0':
  563.         case '1':
  564.         case '2':
  565.         case '3':
  566.         case '4':
  567.         case '5':
  568.         case '6':
  569.         case '7':
  570.         case '8':
  571.         case '9':
  572.             out_before = (10 * out_before) + (opt - '0');
  573.             out_after = (10 * out_after) + (opt - '0');
  574.             break;
  575.         case 'A':
  576.             out_after = atoi(optarg);
  577.             if ( out_after < 0 )
  578.                 usage();
  579.             break;
  580.         case 'B':
  581.             out_before = atoi(optarg);
  582.             if ( out_before < 0 )
  583.                 usage();
  584.             break;
  585.         case 'C':
  586.             out_before = out_after = 2;
  587.             break;
  588.         case 'V':
  589.             fprintf(stderr, "GNU fgrep, version 1.1dos\n");
  590.             break;
  591.         case 'b':
  592.             out_byte = 1;
  593.             break;
  594.         case 'c':
  595.             out_silent = 1;
  596.             count_matches = 1;
  597.             break;
  598.         case 'e':
  599.             if ( keys )
  600.                 usage();
  601.             keys = optarg;
  602.             keycc = strlen(keys);
  603.             break;
  604.         case 'f':
  605.             if ( keys )
  606.                 usage();
  607.             fp = strcmp(optarg, "-") ? fopen(optarg, "r") : stdin;
  608.             if (!fp)
  609.                 fatal(optarg, errno);
  610.             /* This file has to be in text mode */
  611.             setmode(fileno(fp), O_TEXT);
  612.             keyalloc = 1024;
  613.             keys = xmalloc(keyalloc);
  614.             keycc = 0;
  615.             while ( !feof(fp) &&
  616.                 ((cc = fread(keys + keycc, 1, keyalloc - keycc,
  617.                                 fp)) > 0) )
  618.             {
  619.                 keycc += cc;
  620.                 if ( keycc == keyalloc )
  621.                     keys = xrealloc(keys, keyalloc *= 2);
  622.             }
  623.             if ( fp != stdin )
  624.                 fclose(fp);
  625.             else
  626.                 setmode(fileno(stdin), O_BINARY);
  627.             break;
  628.         case 'h':
  629.             no_filenames = 1;
  630.             break;
  631.         case 'i':
  632.         case 'y':            /* For old-timers . . . */
  633.             match_fold = 1;
  634.             break;
  635.         case 'l':
  636.             out_silent = 1;
  637.             list_files = 1;
  638.             break;
  639.         case 'n':
  640.             out_line = 1;
  641.             break;
  642.         case 's':
  643.             out_silent = 1;
  644.             break;
  645.         case 'v':
  646.             out_invert = 1;
  647.             break;
  648.         case 'w':
  649.             match_words = 1;
  650.             break;
  651.         case 'x':
  652.             match_lines = 1;
  653.             break;
  654.         default:
  655.             usage();
  656.             break;
  657.         }
  658.  
  659.     if ( !keys )
  660.         if ( optind < argc )
  661.         {
  662.             keys = argv[optind++];
  663.             keycc = strlen(keys);
  664.         }
  665.         else
  666.             usage();
  667.  
  668.     compile(keys, keycc);
  669.  
  670.     if ( ((argc - optind) > 1) && !no_filenames )
  671.         out_file = 1;
  672.  
  673.     status = 1;
  674.     initbuf();
  675.  
  676.     if ( optind < argc )
  677.     {
  678.         while ( optind < argc )
  679.         {
  680.             desc = strcmp(argv[optind], "-") ?
  681.                         open(argv[optind], 0) : 0;
  682.             if ( desc < 0 )
  683.                 error(argv[optind], errno);
  684.             else
  685.             {
  686.                 count = grep(desc, argv[optind]);
  687.                 if ( count_matches )
  688.                 {
  689.                     if ( out_file )
  690.                         printf("%s:", argv[optind]);
  691.                     printf("%lu\n", count);
  692.                 }
  693.                 if ( count )
  694.                 {
  695.                     status = 0;
  696.                     if ( list_files )
  697.                         printf("%s\n", argv[optind]);
  698.                 }
  699.             }
  700.  
  701.             if ( desc )
  702.                 close(desc);
  703.             optind++;
  704.         }
  705.     }
  706.     else
  707.     {
  708.         count = grep(0, "<stdin>");
  709.         if ( count_matches )
  710.             printf("%lu\n", count);
  711.         if ( count )
  712.         {
  713.             status = 0;
  714.             if ( list_files )
  715.                 printf("%s\n", argv[optind]);
  716.         }
  717.     }
  718.  
  719.     exit(error_seen ? 2 : status);
  720. }
  721.