home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / c / croutes.zip / GREP.C86 < prev    next >
Text File  |  1984-07-29  |  15KB  |  642 lines

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