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

  1. /* +++Date last modified: 05-Jul-1997 */
  2.  
  3. /*
  4.  * The  information  in  this  document  is  subject  to  change
  5.  * without  notice  and  should not be construed as a commitment
  6.  * by Digital Equipment Corporation or by DECUS.
  7.  *
  8.  * Neither Digital Equipment Corporation, DECUS, nor the authors
  9.  * assume any responsibility for the use or reliability of  this
  10.  * document or the described software.
  11.  *
  12.  *      Copyright (C) 1980, DECUS
  13.  *
  14.  * General permission to copy or modify, but not for profit,  is
  15.  * hereby  granted,  provided that the above copyright notice is
  16.  * included and reference made to  the  fact  that  reproduction
  17.  * privileges were granted by DECUS.
  18.  */
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <ctype.h>
  22.  
  23. /*
  24.  * grep
  25.  *
  26.  * Runs on the Decus compiler or on vms, On vms, define as:
  27.  *      grep :== "$disk:[account]grep"      (native)
  28.  *      grep :== "$disk:[account]grep grep" (Decus)
  29.  * See below for more information.
  30.  */
  31.  
  32. char    *documentation[] = {
  33. "grep searches a file for a given pattern.  Execute by",
  34. "   grep [flags] regular_expression file_list\n",
  35. "Flags are single characters preceeded by '-':",
  36. "   -c      Only a count of matching lines is printed",
  37. "   -f      Print file name for matching lines switch, see below",
  38. "   -n      Each line is preceeded by its line number",
  39. "   -v      Only print non-matching lines\n",
  40. "The file_list is a list of files (wildcards are acceptable on RSX modes).",
  41. "\nThe file name is normally printed if there is a file given.",
  42. "The -f flag reverses this action (print name no file, not if more).\n",
  43. 0 };
  44.  
  45. char    *patdoc[] = {
  46. "The regular_expression defines the pattern to search for.  Upper- and",
  47. "lower-case are always ignored.  Blank lines never match.  The expression",
  48. "should be quoted to prevent file-name translation.",
  49. "x      An ordinary character (not mentioned below) matches that character.",
  50. "'\\'    The backslash quotes any character.  \"\\$\" matches a dollar-sign.",
  51. "'^'    A circumflex at the beginning of an expression matches the",
  52. "       beginning of a line.",
  53. "'$'    A dollar-sign at the end of an expression matches the end of a line.",
  54. "'.'    A period matches any character except \"new-line\".",
  55. "':a'   A colon matches a class of characters described by the following",
  56. "':d'     character.  \":a\" matches any alphabetic, \":d\" matches digits,",
  57. "':n'     \":n\" matches alphanumerics, \": \" matches spaces, tabs, and",
  58. "': '     other control characters, such as new-line.",
  59. "'*'    An expression followed by an asterisk matches zero or more",
  60. "       occurrances of that expression: \"fo*\" matches \"f\", \"fo\"",
  61. "       \"foo\", etc.",
  62. "'+'    An expression followed by a plus sign matches one or more",
  63. "       occurrances of that expression: \"fo+\" matches \"fo\", etc.",
  64. "'-'    An expression followed by a minus sign optionally matches",
  65. "       the expression.",
  66. "'[]'   A string enclosed in square brackets matches any character in",
  67. "       that string, but no others.  If the first character in the",
  68. "       string is a circumflex, the expression matches any character",
  69. "       except \"new-line\" and the characters in the string.  For",
  70. "       example, \"[xyz]\" matches \"xx\" and \"zyx\", while \"[^xyz]\"",
  71. "       matches \"abc\" but not \"axb\".  A range of characters may be",
  72. "       specified by two characters separated by \"-\".  Note that,",
  73. "       [a-z] matches alphabetics, while [z-a] never matches.",
  74. "The concatenation of regular expressions is a regular expression.",
  75. 0};
  76.  
  77. #define LMAX    512
  78. #define PMAX    256
  79.  
  80. #define CHAR    1
  81. #define BOL     2
  82. #define EOL     3
  83. #define ANY     4
  84. #define CLASS   5
  85. #define NCLASS  6
  86. #define STAR    7
  87. #define PLUS    8
  88. #define MINUS   9
  89. #define ALPHA   10
  90. #define DIGIT   11
  91. #define NALPHA  12
  92. #define PUNCT   13
  93. #define RANGE   14
  94. #define ENDPAT  15
  95.  
  96. int cflag=0, fflag=0, nflag=0, vflag=0, nfile=0, debug=0;
  97.  
  98. char *pp, lbuf[LMAX], pbuf[PMAX];
  99.  
  100. extern char *cclass(), *pmatch();
  101.  
  102.  
  103. /*** Main program - parse arguments & grep *************/
  104. main(argc, argv)
  105. int argc;
  106. char *argv[];
  107. {
  108.    register char   *p;
  109.    register int    c, i;
  110.    int             gotpattern;
  111.  
  112.    FILE            *f;
  113.  
  114.    if (argc <= 1)
  115.       usage("No arguments");
  116.    if (argc == 2 && argv[1][0] == '?' && argv[1][1] == 0) {
  117.       help(documentation);
  118.       help(patdoc);
  119.       return 1;
  120.       }
  121.    nfile = argc-1;
  122.    gotpattern = 0;
  123.    for (i=1; i < argc; ++i) {
  124.       p = argv[i];
  125.       if (*p == '-') {
  126.          ++p;
  127.          while (c = *p++) {
  128.             switch(tolower(c)) {
  129.  
  130.             case '?':
  131.                help(documentation);
  132.                break;
  133.  
  134.             case 'C':
  135.             case 'c':
  136.                ++cflag;
  137.                break;
  138.  
  139.             case 'D':
  140.             case 'd':
  141.                ++debug;
  142.                break;
  143.  
  144.             case 'F':
  145.             case 'f':
  146.                ++fflag;
  147.                break;
  148.  
  149.             case 'n':
  150.             case 'N':
  151.                ++nflag;
  152.                break;
  153.  
  154.             case 'v':
  155.             case 'V':
  156.                ++vflag;
  157.                break;
  158.  
  159.             default:
  160.                usage("Unknown flag");
  161.             }
  162.          }
  163.          argv[i] = 0;
  164.          --nfile;
  165.       } else if (!gotpattern) {
  166.          compile(p);
  167.          argv[i] = 0;
  168.          ++gotpattern;
  169.          --nfile;
  170.       }
  171.    }
  172.    if (!gotpattern)
  173.       usage("No pattern");
  174.    if (nfile == 0)
  175.       grep(stdin, 0);
  176.    else {
  177.       fflag = fflag ^ (nfile > 0);
  178.       for (i=1; i < argc; ++i) {
  179.          if (p = argv[i]) {
  180.             if ((f=fopen(p, "r")) == NULL)
  181.                cant(p);
  182.             else {
  183.                grep(f, p);
  184.                fclose(f);
  185.             }
  186.          }
  187.       }
  188.    }
  189.    return EXIT_SUCCESS;
  190. }
  191.  
  192. /*** Display a file name *******************************/
  193. file(s)
  194. char *s;
  195. {
  196.    printf("File %s:\n", s);
  197. }
  198.  
  199. /*** Report unopenable file ****************************/
  200. cant(s)
  201. char *s;
  202. {
  203.    fprintf(stderr, "%s: cannot open\n", s);
  204. }
  205.  
  206. /*** Give good help ************************************/
  207. help(hp)
  208. char **hp;
  209. {
  210.    register char   **dp;
  211.  
  212.    for (dp = hp; *dp; ++dp)
  213.       printf("%s\n", *dp);
  214. }
  215.  
  216. /*** Display usage summary *****************************/
  217. usage(s)
  218. char    *s;
  219. {
  220.    fprintf(stderr, "?GREP-E-%s\n", s);
  221.    fprintf(stderr,
  222.       "Usage: grep [-cfnv] pattern [file ...].  grep ? for help\n");
  223.    exit(EXIT_FAILURE);
  224. }
  225.  
  226. /*** Compile the pattern into global pbuf[] ************/
  227. compile(source)
  228. char       *source;   /* Pattern to compile */
  229. {
  230.    register char  *s;         /* Source string pointer     */
  231.    register char  *lp;        /* Last pattern pointer      */
  232.    register int   c;          /* Current character         */
  233.    int            o;          /* Temp                      */
  234.    char           *spp;       /* Save beginning of pattern */
  235.  
  236.    s = source;
  237.    if (debug)
  238.       printf("Pattern = \"%s\"\n", s);
  239.    pp = pbuf;
  240.    while (c = *s++) {
  241.       /*
  242.        * STAR, PLUS and MINUS are special.
  243.        */
  244.       if (c == '*' || c == '+' || c == '-') {
  245.          if (pp == pbuf ||
  246.               (o=pp[-1]) == BOL ||
  247.               o == EOL ||
  248.               o == STAR ||
  249.               o == PLUS ||
  250.               o == MINUS)
  251.             badpat("Illegal occurrance op.", source, s);
  252.          store(ENDPAT);
  253.          store(ENDPAT);
  254.          spp = pp;               /* Save pattern end     */
  255.          while (--pp > lp)       /* Move pattern down    */
  256.             *pp = pp[-1];        /* one byte             */
  257.          *pp =   (c == '*') ? STAR :
  258.             (c == '-') ? MINUS : PLUS;
  259.          pp = spp;               /* Restore pattern end  */
  260.          continue;
  261.       }
  262.       /*
  263.        * All the rest.
  264.        */
  265.       lp = pp;         /* Remember start       */
  266.       switch(c) {
  267.  
  268.       case '^':
  269.          store(BOL);
  270.          break;
  271.  
  272.       case '$':
  273.          store(EOL);
  274.          break;
  275.  
  276.       case '.':
  277.          store(ANY);
  278.          break;
  279.  
  280.       case '[':
  281.          s = cclass(source, s);
  282.          break;
  283.  
  284.       case ':':
  285.          if (*s) {
  286.             switch(tolower(c = *s++)) {
  287.  
  288.             case 'a':
  289.             case 'A':
  290.                store(ALPHA);
  291.                break;
  292.  
  293.             case 'd':
  294.             case 'D':
  295.                store(DIGIT);
  296.                break;
  297.  
  298.             case 'n':
  299.             case 'N':
  300.                store(NALPHA);
  301.                break;
  302.  
  303.             case ' ':
  304.                store(PUNCT);
  305.                break;
  306.  
  307.             default:
  308.                badpat("Unknown : type", source, s);
  309.  
  310.             }
  311.             break;
  312.          }
  313.          else    badpat("No : type", source, s);
  314.  
  315.       case '\\':
  316.          if (*s)
  317.             c = *s++;
  318.  
  319.       default:
  320.          store(CHAR);
  321.          store(tolower(c));
  322.       }
  323.    }
  324.    store(ENDPAT);
  325.    store(0);                /* Terminate string     */
  326.    if (debug) {
  327.       for (lp = pbuf; lp < pp;) {
  328.          if ((c = (*lp++ & 0377)) < ' ')
  329.             printf("\\%o ", c);
  330.          else    printf("%c ", c);
  331.         }
  332.         printf("\n");
  333.    }
  334. }
  335.  
  336. /*** Compile a class (within []) ***********************/
  337. char *cclass(source, src)
  338. char       *source;   /* Pattern start -- for error msg. */
  339. char       *src;      /* Class start */
  340. {
  341.    register char   *s;        /* Source pointer    */
  342.    register char   *cp;       /* Pattern start     */
  343.    register int    c;         /* Current character */
  344.    int             o;         /* Temp              */
  345.  
  346.    s = src;
  347.    o = CLASS;
  348.    if (*s == '^') {
  349.       ++s;
  350.       o = NCLASS;
  351.    }
  352.    store(o);
  353.    cp = pp;
  354.    store(0);                          /* Byte count      */
  355.    while ((c = *s++) && c!=']') {
  356.       if (c == '\\') {                /* Store quoted char    */
  357.          if ((c = *s++) == '\0')      /* Gotta get something  */
  358.             badpat("Class terminates badly", source, s);
  359.          else    store(tolower(c));
  360.       }
  361.       else if (c == '-' &&
  362.             (pp - cp) > 1 && *s != ']' && *s != '\0') {
  363.          c = pp[-1];             /* Range start     */
  364.          pp[-1] = RANGE;         /* Range signal    */
  365.          store(c);               /* Re-store start  */
  366.          c = *s++;               /* Get end char and*/
  367.          store(tolower(c));      /* Store it        */
  368.       }
  369.       else {
  370.          store(tolower(c));      /* Store normal char */
  371.       }
  372.    }
  373.    if (c != ']')
  374.       badpat("Unterminated class", source, s);
  375.    if ((c = (pp - cp)) >= 256)
  376.       badpat("Class too large", source, s);
  377.    if (c == 0)
  378.       badpat("Empty class", source, s);
  379.    *cp = c;
  380.    return(s);
  381. }
  382.  
  383. /*** Store an entry in the pattern buffer **************/
  384. store(op)
  385.    int op;
  386. {
  387.    if (pp >= &pbuf[PMAX])
  388.       error("Pattern too complex\n");
  389.    *pp++ = op;
  390. }
  391.  
  392. /*** Report a bad pattern specification ****************/
  393. badpat(message, source, stop)
  394. char  *message;       /* Error message */
  395. char  *source;        /* Pattern start */
  396. char  *stop;          /* Pattern end   */
  397. {
  398.    fprintf(stderr, "-GREP-E-%s, pattern is\"%s\"\n", message, source);
  399.    fprintf(stderr, "-GREP-E-Stopped at byte %d, '%c'\n",
  400.          stop-source, stop[-1]);
  401.    error("?GREP-E-Bad pattern\n");
  402. }
  403.  
  404. /*** Scan the file for the pattern in pbuf[] ***********/
  405. grep(fp, fn)
  406. FILE       *fp;       /* File to process            */
  407. char       *fn;       /* File name (for -f option)  */
  408. {
  409.    register int lno, count, m;
  410.  
  411.    lno = 0;
  412.    count = 0;
  413.    while (fgets(lbuf, LMAX, fp)) {
  414.       ++lno;
  415.       m = match();
  416.       if ((m && !vflag) || (!m && vflag)) {
  417.          ++count;
  418.          if (!cflag) {
  419.             if (fflag && fn) {
  420.                file(fn);
  421.                fn = 0;
  422.             }
  423.             if (nflag)
  424.                printf("%d\t", lno);
  425.             printf("%s\n", lbuf);
  426.          }
  427.       }
  428.    }
  429.    if (cflag) {
  430.       if (fflag && fn)
  431.          file(fn);
  432.       printf("%d\n", count);
  433.    }
  434. }
  435.  
  436. /*** Match line (lbuf) with pattern (pbuf) return 1 if match ***/
  437. match()
  438. {
  439.    register char   *l;        /* Line pointer       */
  440.  
  441.    for (l = lbuf; *l; ++l) {
  442.       if (pmatch(l, pbuf))
  443.          return(1);
  444.    }
  445.    return(0);
  446. }
  447.  
  448. /*** Match partial line with pattern *******************/
  449. char *pmatch(line, pattern)
  450. char               *line;     /* (partial) line to match      */
  451. char               *pattern;  /* (partial) pattern to match   */
  452. {
  453.    register char   *l;        /* Current line pointer         */
  454.    register char   *p;        /* Current pattern pointer      */
  455.    register char   c;         /* Current character            */
  456.    char            *e;        /* End for STAR and PLUS match  */
  457.    int             op;        /* Pattern operation            */
  458.    int             n;         /* Class counter                */
  459.    char            *are;      /* Start of STAR match          */
  460.  
  461.    l = line;
  462.    if (debug > 1)
  463.       printf("pmatch(\"%s\")\n", line);
  464.    p = pattern;
  465.    while ((op = *p++) != ENDPAT) {
  466.       if (debug > 1)
  467.          printf("byte[%d] = 0%o, '%c', op = 0%o\n",
  468.                l-line, *l, *l, op);
  469.       switch(op) {
  470.  
  471.       case CHAR:
  472.          if (tolower(*l++) != *p++)
  473.             return(0);
  474.          break;
  475.  
  476.       case BOL:
  477.          if (l != lbuf)
  478.             return(0);
  479.          break;
  480.  
  481.       case EOL:
  482.          if (*l != '\0')
  483.             return(0);
  484.          break;
  485.  
  486.       case ANY:
  487.          if (*l++ == '\0')
  488.             return(0);
  489.          break;
  490.  
  491.       case DIGIT:
  492.          if ((c = *l++) < '0' || (c > '9'))
  493.             return(0);
  494.          break;
  495.  
  496.       case ALPHA:
  497.          c = tolower(*l++);
  498.          if (c < 'a' || c > 'z')
  499.             return(0);
  500.          break;
  501.  
  502.       case NALPHA:
  503.          c = tolower(*l++);
  504.          if (c >= 'a' && c <= 'z')
  505.             break;
  506.          else if (c < '0' || c > '9')
  507.             return(0);
  508.          break;
  509.  
  510.       case PUNCT:
  511.          c = *l++;
  512.          if (c == 0 || c > ' ')
  513.             return(0);
  514.          break;
  515.  
  516.       case CLASS:
  517.       case NCLASS:
  518.          c = tolower(*l++);
  519.          n = *p++ & 0377;
  520.          do {
  521.             if (*p == RANGE) {
  522.                p += 3;
  523.                n -= 2;
  524.                if (c >= p[-2] && c <= p[-1])
  525.                   break;
  526.             }
  527.             else if (c == *p++)
  528.                break;
  529.          } while (--n > 1);
  530.          if ((op == CLASS) == (n <= 1))
  531.             return(0);
  532.          if (op == CLASS)
  533.             p += n - 2;
  534.          break;
  535.  
  536.       case MINUS:
  537.          e = pmatch(l, p);       /* Look for a match    */
  538.          while (*p++ != ENDPAT); /* Skip over pattern   */
  539.          if (e)                  /* Got a match?        */
  540.             l = e;               /* Yes, update string  */
  541.          break;                  /* Always succeeds     */
  542.  
  543.       case PLUS:                 /* One or more ...     */
  544.          if ((l = pmatch(l, p)) == 0)
  545.             return(0);           /* Gotta have a match  */
  546.       case STAR:                 /* Zero or more ...    */
  547.          are = l;                /* Remember line start */
  548.          while (*l && (e = pmatch(l, p)))
  549.             l = e;               /* Get longest match   */
  550.          while (*p++ != ENDPAT); /* Skip over pattern   */
  551.          while (l >= are) {      /* Try to match rest   */
  552.             if (e = pmatch(l, p))
  553.                return(e);
  554.             --l;                 /* Nope, try earlier   */
  555.          }
  556.          return(0);              /* Nothing else worked */
  557.  
  558.       default:
  559.          printf("Bad op code %d\n", op);
  560.          error("Cannot happen -- match\n");
  561.       }
  562.    }
  563.    return(l);
  564. }
  565.  
  566. /*** Report an error ***********************************/
  567. error(s)
  568. char *s;
  569. {
  570.    fprintf(stderr, "%s", s);
  571.    exit(EXIT_FAILURE);
  572. }
  573.