home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / c_spec / sources / grep.c < prev    next >
C/C++ Source or Header  |  1986-02-20  |  10KB  |  473 lines

  1. #include <stdio.h>
  2.  
  3. /*----------------------------------------------------------------
  4.  *
  5.  *            Copyright (c) 1985 Allen Holub
  6.  *            All rights reserved.
  7.  *
  8.  *    Permission to copy this program or any part of this program
  9.  *    is granted in the case of personal, non-commercial use only.
  10.  *    Any use for profit or other commercial gain without written
  11.  *    permission of the author is prohibited.
  12.  *
  13.  *    If you've been give this program by a friend, and you find
  14.  *    it worthwhile, I'd appreciate your sending $35 to me for the
  15.  *    program.
  16.  *
  17.  * Software Engineering Consultants, P.O. Box 5679, Berkeley CA, 94705
  18.  *
  19.  *----------------------------------------------------------------
  20.  *
  21.  *    GREP
  22.  *
  23.  *    Search a file for a pattern.
  24.  *
  25.  *    The algorithm used here is essentially the algorithm in
  26.  *    Software Tools in Pascal (pp 145f.). Though the routines have
  27.  *    been changed somewhat to put them into good 'C'. See tools1.c
  28.  *    for details. 
  29.  *
  30.  *    This program is a healthy subset of the UNIX program of the same
  31.  *    name. The differences are as follows:
  32.  *
  33.  *        - the -s, -x and -b options are not supported.
  34.  *        - the meta-characters ()+? are not supported.
  35.  *        - the -y option cause case to be ignored.
  36.  *
  37.  *    usage is:
  38.  *        grep [-vclnhyef] [expression] files ...
  39.  *
  40.  *    For more details see grep.doc and revision.doc
  41.  */
  42.  
  43. #define OR_SYM    '|'        /*    Used to get the OR of several
  44.                  *    regular expressions.
  45.                  */
  46.  
  47. #define MAXLINE     128        /*    Maximum size of an input line
  48.                  */
  49.  
  50. #define MAX_EXPR 64        /*    The maximum number of regular
  51.                  *    expressions separated by
  52.                  *    newlines or | allowed.
  53.                  */
  54.  
  55. typedef char    *TOKEN;        /* Dummy definition, use like FILE */
  56. extern    char    *matchs        ();
  57. extern    TOKEN    *makepat    ();
  58. extern    char    *stoupper    ();
  59.  
  60. extern    char        *strchr();
  61. #define index(s,c)    strchr(s,c)
  62.  
  63.  
  64. /*    The following global flags are true if a switch was set
  65.  *    in the command line, false otherwise.
  66.  */
  67.  
  68. int    vflag, yflag, cflag, lflag, nflag, hflag, fflag;
  69.  
  70. /*----------------------------------------------------------------------*/
  71.  
  72. main(argc, argv)
  73. int    argc;
  74. char    **argv;
  75. {
  76.     int     i, j, linenum, count;
  77.  
  78.     int    line[MAXLINE];
  79.     int    numfiles;
  80.     FILE    *stream;
  81.     int    exprc;
  82.     TOKEN    *exprv[MAX_EXPR];
  83.  
  84.     fprintf(stderr, "GREP - Copyright (C) 1984, Allen I. Holub,");
  85.     fprintf(stderr, " all rights reserved\n");
  86.  
  87.     ctlc();
  88.     reargv(&argc, &argv);
  89.  
  90.     i = 1;
  91.     
  92.     if (argc < 2)
  93.         pr_usage(1);
  94.  
  95.     if ( *argv[i] == '-')
  96.     {
  97.         /*
  98.          *    Expand the switches on the command line
  99.          */
  100.  
  101.         expand_sw( argv[i++] );
  102.         
  103.         if ( i == argc )
  104.             pr_usage(1);
  105.     }
  106.  
  107.     /*    Get the pattern string.
  108.      */
  109.  
  110.     if ( (exprc = get_expr( exprv, MAX_EXPR, &argv[i++])) == 0 )
  111.         pr_usage(2);
  112.  
  113.     numfiles = argc - i;        /*  Get number of files left to
  114.                      *  process on the command line    
  115.                      */
  116.     do
  117.     {
  118.             if ( numfiles)
  119.         {
  120.             stream = fopen( argv[i], "r");
  121.             if (stream == NULL)
  122.             {
  123.                 fprintf(stderr, "Can't open %s\n", argv[i]);
  124.                 continue;
  125.             }
  126.         }
  127.         else
  128.         {
  129.             stream = stdin;
  130.         }
  131.  
  132.         count = 0;
  133.         linenum = 1;
  134.  
  135.         while ( fgets(line, MAXLINE, stream) )
  136.         {
  137. #ifdef CPM
  138.             if (!fflag  ||  yflag )
  139.                 stoupper(line);
  140. #else
  141.             if ( yflag )
  142.                 stoupper(line);
  143. #endif
  144.  
  145.                 for( j = exprc ; --j >= 0 ; )
  146.             {
  147.                 if ( matchs(line , exprv[j]) )
  148.                 {
  149.                     count++;
  150.                     pr_match(linenum, line, argv[i], 1,
  151.                                 numfiles);
  152.                 }
  153.                 else
  154.                 {
  155.                     pr_match(linenum, line, argv[i], 0,
  156.                                 numfiles);
  157.                 }
  158.  
  159.                 linenum++;
  160.                 cntrl_c();
  161.                 }
  162.             if( lflag && count )
  163.                 break;
  164.         }
  165.         pr_count( numfiles, argv[i], count );
  166.         fclose (stream);
  167.  
  168.     } while (++i < argc);
  169.  
  170.     exit( 0 );
  171. }
  172.  
  173. /*----------------------------------------------------------------------*/
  174.  
  175.  
  176. pr_count( fcount, fname, count)
  177. int    fcount, count;
  178. char    *fname;
  179. {
  180.     /*    Process the -c flag by printing out a count and,
  181.      *    if more than one file was listed on the command line,
  182.      *    the file name too.
  183.      */
  184.  
  185.  
  186.     if (!cflag)
  187.         return;
  188.  
  189.     if (fcount > 1)
  190.          printf("%-12s: ", fname );
  191.  
  192.     printf( "%d\n", count );
  193. }
  194.  
  195. /*----------------------------------------------------------------------*/
  196.  
  197.  
  198. pr_match(linenum, line, fname, match, numfiles)
  199. int    linenum, match;
  200. char    *line, *fname;
  201. {
  202.     /*    If a match is found print the correct thing 
  203.      *    as specified by the command line switches.
  204.      */
  205.  
  206.     char    buf[80];
  207.  
  208.     if (cflag)
  209.         return;
  210.  
  211.     if ( (vflag && !match) ||  (!vflag && match) )
  212.     {
  213.         if (!hflag && ( (numfiles > 1) || lflag) )
  214.             printf("%s%s", fname, lflag ? "\n" : ":" );
  215.  
  216.         if (nflag)
  217.             printf("%03d:", linenum );
  218.  
  219.         if (!lflag)
  220.             printf("%s", line );
  221.     }
  222. }                
  223.  
  224. /*----------------------------------------------------------------------*/
  225.  
  226.  
  227. expand_sw( str )
  228. char    *str;
  229. {
  230.     /*    Set global flags corresponding to specific switches
  231.      *    if those switches are set
  232.      */
  233.  
  234.     vflag = 0;
  235.     cflag = 0;
  236.     lflag = 0;
  237.     nflag = 0;
  238.     hflag = 0;
  239.     fflag = 0;
  240.     yflag = 0;
  241.  
  242.     while (*str)
  243.     {
  244.         switch ( toupper(*str))
  245.         {
  246.         case '-':
  247.         case 'E':
  248.             break;
  249.     
  250.         case 'C':
  251.             cflag = 1;
  252.             break;
  253.  
  254.         case 'F':
  255.             fflag = 1;
  256.             break;
  257.  
  258.         case 'H':
  259.             hflag = 1;
  260.             break;
  261.  
  262.         case 'L':
  263.             lflag = 1;
  264.             break;
  265.  
  266.         case 'N':
  267.             nflag = 1;
  268.             break;
  269.  
  270.         case 'V':
  271.             vflag = 1;
  272.             break;
  273.  
  274.         case 'Y':
  275.             yflag = 1;
  276.             break;
  277.  
  278.         default:
  279.             pr_usage(3);
  280.             break;
  281.         }
  282.  
  283.         str++;
  284.     }
  285. }
  286.  
  287.  
  288. /*----------------------------------------------------------------------*/
  289.  
  290. int do_or( lp, expr, maxexpr )
  291. char    *lp;
  292. TOKEN    **expr;
  293. int    maxexpr;
  294. {
  295.     int    found;
  296.     TOKEN    *pat;
  297.     char    *op;
  298.  
  299.     found = 0;
  300.  
  301.     /*
  302.      *    Extract regular expressions separated by OR_SYMs from
  303.      *    lp and put them into expr. Extract only up to
  304.      *    maxexpr expressions. If yflag is true map string to upper
  305.      *    case first.
  306.      */
  307.  
  308.     if( yflag )
  309.         stoupper( lp );
  310.  
  311.     while ( op = index(lp, OR_SYM) )
  312.     {
  313.         if(found <= maxexpr && (pat = makepat(lp, OR_SYM)) )
  314.         {
  315.             *expr++ = pat;
  316.             found++;
  317.         }
  318.         lp = ++op;
  319.     
  320.         if ( pat == 0 )
  321.             goto fatal_err;
  322.     }
  323.  
  324.     if (found <= maxexpr  &&  (pat = makepat( lp, OR_SYM))  )
  325.     {
  326.         found++;
  327.         *expr = pat;
  328.     }
  329.  
  330.  
  331.     if ( pat == 0 )
  332.     {
  333. fatal_err:
  334.         printf("Illegal expression: %s\n", lp);
  335.         exit();
  336.     }
  337.  
  338.     return (found);
  339. }
  340.  
  341. /*----------------------------------------------------------------------*/
  342.  
  343.  
  344. get_expr( expr, maxexpr, defexpr )
  345. TOKEN    *expr[];
  346. int    maxexpr;
  347. char    **defexpr;
  348. {
  349.     FILE    *stream;
  350.     int    count;
  351.     char    line[MAXLINE];
  352.  
  353. #ifdef DEBUG
  354.     int    i;
  355. #endif
  356.  
  357.     /*    Get regular expressions separated by | or newlines
  358.      *    either out of a file or off the command line depending
  359.      *    on whether the -f flag is set. The expressions are
  360.      *    converted into pattern templates (see tools.c) and
  361.      *    pointers to the templates are put into the array expr[]
  362.      *    (which works similar to argv). 
  363.      *
  364.      *    Return the number of expressions found (which can be used
  365.      *    in a similar fashion to argc).
  366.      */
  367.  
  368.     count = 0;
  369.  
  370.     if ( fflag )
  371.     {
  372.         /*
  373.          *    Then *defexpr is the file name and expressions should
  374.          *    be taken from that file.
  375.          */
  376.  
  377.             if ( (stream = fopen(*defexpr, "r")) == NULL )
  378.         {
  379.             fprintf(stderr, "Can't open %s\n", *defexpr);
  380.             exit( 1 );
  381.         }
  382.  
  383.         while ( (maxexpr - count)  &&  fgets(line, MAXLINE, stream) )
  384.         {
  385.             count += do_or(line, &expr[count], maxexpr - count );
  386.         }
  387.  
  388.         fclose (stream);
  389.     }
  390.     else
  391.     {
  392.         /*
  393.          *    *defexpr is the expression itself.
  394.          */
  395.  
  396.         if ( count += do_or( *defexpr, &expr[count], maxexpr - count))
  397.             *defexpr = " ";
  398.     }
  399.  
  400.  
  401. #ifdef DEBUG
  402.  
  403.     /*    Print out all the regular expressions after they have been
  404.      *    converted into pattern templates (see tools.c).
  405.      */
  406.  
  407.     for (i = count; --i >= 0 ; )
  408.     {
  409.         pr_tok( expr[i] );
  410.         printf("-------------------------------------------------\n");
  411.     }
  412.  
  413. #endif
  414.     return(count);
  415. }
  416.  
  417.  
  418. /*----------------------------------------------------------------------*/
  419.  
  420. cntrl_c()
  421. {
  422.  
  423. #ifdef CPM
  424.  
  425.     /*    If any character was hit, and that character is a
  426.      *    ^C, then abort.
  427.      */
  428.  
  429.     if ( bdos(11) && ((bdos(1,0) & 0x7f) == 0x03) )
  430.         exit(1);
  431. #endif
  432.  
  433. }
  434.  
  435. /*----------------------------------------------------------------------*/
  436.  
  437. #define E(x)    fprintf(stderr, "%s\n", x);
  438.  
  439.  
  440. pr_usage(num)
  441. int    num;
  442. {
  443. #    ifdef DEBUG
  444.     fprintf(stderr,"ERROR NUMBER %d ", num);
  445. #    endif
  446.  
  447.     E("Useage:  grep [-switchs] <pattern> [files ...]");
  448.     E("Prints all matches of <pattern> found in the indicated files");
  449.     E("or in standard input if no files are listed");
  450.     E("<pattern> can be any regular expression composed of:");
  451.     E("     .   = matches any character");
  452.     E("     $ = end of line.  ^ = start of line.");
  453.     E("     *   = previous character 0 or more times");
  454.     E("     +   = previous character 1 or more times");
  455.     E("     []  matches any of the characters enclosed in []. If a ^ is");
  456.     E("         the first character after the [, any character except");
  457.     E("         those in the class will be matched.");
  458.     E("     \\b=backspace \\f=FF   \\n=LF \\r=CR \\t=HT (tab)");
  459.     E("     \\s=space     \\e=ESC  \\xDD=char composed of hex digits DD");
  460.     E("     any other character = that character.");
  461.     E("Optional command line switches are:");
  462.     E("   -c        only a count of matching lines is printed");
  463.     E("   -e <expr> Take expression from next argument.");
  464.     E("   -f <file> Take expressions from file.");
  465.     E("   -h        file name headers aren't printed");
  466.     E("   -l        only names of files that contain match are printed");
  467.     E("   -n        each output line preceeded by line number in file");
  468.     E("   -v        print all lines except matching ones");
  469.     E("   -y        Ignore case");
  470.  
  471.     exit(1);
  472. }
  473.