home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / zip / portfoli / grep.lzh / GREP.C next >
Text File  |  1991-08-21  |  17KB  |  660 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. /*
  94. #ifndef stdin
  95. #define stdin STDIN
  96. #define stdout STDOUT
  97. #define stderr STDERR
  98. #define DeSmet 1
  99. #endif
  100. */
  101.  
  102. #define LMAX    512
  103. #define PMAX    256
  104.  
  105. #define CHAR    1
  106. #define BOL     2
  107. #define EOL     3
  108. #define ANY     4
  109. #define CLASS   5
  110. #define NCLASS  6
  111. #define STAR    7
  112. #define PLUS    8
  113. #define MINUS   9
  114. #define ALPHA   10
  115. #define DIGIT   11
  116. #define NALPHA  12
  117. #define PUNCT   13
  118. #define RANGE   14
  119. #define ENDPAT  15
  120.  
  121. int     cflag;
  122. int     fflag;
  123. int     nflag;
  124. int     vflag;
  125. int     nfile;
  126.  
  127. int     debug   =       0;         /* Set for debug code      */
  128.  
  129. char    *pp;
  130.  
  131. #ifndef vms
  132. char    file_name[81];
  133. #endif
  134.  
  135. char    lbuf[LMAX];
  136. char    pbuf[PMAX];
  137.  
  138. /*******************************************************/
  139.  
  140. main(argc, argv)
  141. char *argv[];
  142. {
  143.    register char   *p;
  144.    register int    c, i;
  145.    int             gotpattern;
  146.    int             gotcha;
  147.  
  148.    FILE            *f;
  149.  
  150.    if (argc <= 1)
  151.       usage("No arguments");
  152.    if (argc == 2 && argv[1][0] == '?' && argv[1][1] == 0) {
  153.       help(documentation);
  154.       help(patdoc);
  155.       return;
  156.       }
  157.    nfile = argc-1;
  158.    gotpattern = 0;
  159.    for (i=1; i < argc; ++i) {
  160.       p = argv[i];
  161.       if (*p == '-') {
  162.          ++p;
  163.          while (c = *p++) {
  164.             switch(tolower(c)) {
  165.  
  166.             case '?':
  167.                help(documentation);
  168.                break;
  169.  
  170.             case 'C':
  171.             case 'c':
  172.                ++cflag;
  173.                break;
  174.  
  175.             case 'D':
  176.             case 'd':
  177.                ++debug;
  178.                break;
  179.  
  180.             case 'F':
  181.             case 'f':
  182.                ++fflag;
  183.                break;
  184.  
  185.             case 'n':
  186.             case 'N':
  187.                ++nflag;
  188.                break;
  189.  
  190.             case 'v':
  191.             case 'V':
  192.                ++vflag;
  193.                break;
  194.  
  195.             default:
  196.                usage("Unknown flag");
  197.             }
  198.          }
  199.          argv[i] = 0;
  200.          --nfile;
  201.       } else if (!gotpattern) {
  202.          compile(p);
  203.          argv[i] = 0;
  204.          ++gotpattern;
  205.          --nfile;
  206.       }
  207.    }
  208.    if (!gotpattern)
  209.       usage("No pattern");
  210.    if (nfile == 0)
  211.       grep(stdin, 0);
  212.    else {
  213.       fflag = fflag ^ (nfile > 0);
  214.       for (i=1; i < argc; ++i) {
  215.          if (p = argv[i]) {
  216.             if ((f=fopen(p, "r")) == NULL)
  217.                cant(p);
  218.             else {
  219.                grep(f, p);
  220.                fclose(f);
  221.             }
  222.          }
  223.       }
  224.    }
  225. }
  226.  
  227. /*******************************************************/
  228.  
  229. file(s)
  230. char *s;
  231. {
  232.    printf("File %s:\n", s);
  233. }
  234.  
  235. /*******************************************************/
  236.  
  237. cant(s)
  238. char *s;
  239. {
  240.    fprintf(stderr, "%s: cannot open\n", s);
  241. }
  242.  
  243.  
  244. /*******************************************************/
  245.  
  246. help(hp)
  247. char **hp;  /* dns added extra '*'  */
  248. /*
  249.  * Give good help
  250.  */
  251. {
  252.    register char   **dp;
  253.  
  254.    for (dp = hp; *dp; dp++)
  255.       printf("%s\n", *dp);
  256. }
  257.  
  258.  
  259. /*******************************************************/
  260.  
  261. usage(s)
  262. char    *s;
  263. {
  264.    fprintf(stderr, "?GREP-E-%s\n", s);
  265.    fprintf(stderr,
  266.       "Usage: grep [-cfnv] pattern [file ...].  grep ? for help\n");
  267.    exit(1);
  268. }
  269.  
  270.  
  271.  
  272. /*******************************************************/
  273.  
  274.  
  275. compile(source)
  276. char       *source;   /* Pattern to compile         */
  277. /*
  278.  * Compile the pattern into global pbuf[]
  279.  */
  280. {
  281.    register char  *s;         /* Source string pointer     */
  282.    register char  *lp;        /* Last pattern pointer      */
  283.    register int   c;          /* Current character         */
  284.    int            o;          /* Temp                      */
  285.    char           *spp;       /* Save beginning of pattern */
  286.    char           *cclass();  /* Compile class routine     */
  287.  
  288.    s = source;
  289.    if (debug)
  290.       printf("Pattern = \"%s\"\n", s);
  291.    pp = pbuf;
  292.    while (c = *s++) {
  293.       /*
  294.        * STAR, PLUS and MINUS are special.
  295.        */
  296.       if (c == '*' || c == '+' || c == '-') {
  297.          if (pp == pbuf ||
  298.               (o=pp[-1]) == BOL ||
  299.               o == EOL ||
  300.               o == STAR ||
  301.               o == PLUS ||
  302.               o == MINUS)
  303.             badpat("Illegal occurrance op.", source, s);
  304.          store(ENDPAT);
  305.          store(ENDPAT);
  306.          spp = pp;               /* Save pattern end     */
  307.          while (--pp > lp)       /* Move pattern down    */
  308.             *pp = pp[-1];        /* one byte             */
  309.          *pp =   (c == '*') ? STAR :
  310.             (c == '-') ? MINUS : PLUS;
  311.          pp = spp;               /* Restore pattern end  */
  312.          continue;
  313.       }
  314.       /*
  315.        * All the rest.
  316.        */
  317.       lp = pp;         /* Remember start       */
  318.       switch(c) {
  319.  
  320.       case '^':
  321.          store(BOL);
  322.          break;
  323.  
  324.       case '$':
  325.          store(EOL);
  326.          break;
  327.  
  328.       case '.':
  329.          store(ANY);
  330.          break;
  331.  
  332.       case '[':
  333.          s = cclass(source, s);
  334.          break;
  335.  
  336.       case ':':
  337.          if (*s) {
  338.             c = *s++;
  339.             switch(tolower(c)) {
  340.  
  341.             case 'a':
  342.             case 'A':
  343.                store(ALPHA);
  344.                break;
  345.  
  346.             case 'd':
  347.             case 'D':
  348.                store(DIGIT);
  349.                break;
  350.  
  351.             case 'n':
  352.             case 'N':
  353.                store(NALPHA);
  354.                break;
  355.  
  356.             case ' ':
  357.                store(PUNCT);
  358.                break;
  359.  
  360.             default:
  361.                badpat("Unknown : type", source, s);
  362.  
  363.             }
  364.             break;
  365.          }
  366.          else    badpat("No : type", source, s);
  367.  
  368.       case '\\':
  369.          if (*s)
  370.             c = *s++;
  371.  
  372.       default:
  373.          store(CHAR);
  374.          store(tolower(c));
  375.       }
  376.    }
  377.    store(ENDPAT);
  378.    store(0);                /* Terminate string     */
  379.    if (debug) {
  380.       for (lp = pbuf; lp < pp;) {
  381.          if ((c = (*lp++ & 0377)) < ' ')
  382.             printf("\\%o ", c);
  383.          else    printf("%c ", c);
  384.         }
  385.         printf("\n");
  386.    }
  387. }
  388.  
  389. /*******************************************************/
  390.  
  391. char *
  392. cclass(source, src)
  393. char       *source;   /* Pattern start -- for error msg.      */
  394. char       *src;      /* Class start           */
  395. /*
  396.  * Compile a class (within [])
  397.  */
  398. {
  399.    register char   *s;        /* Source pointer    */
  400.    register char   *cp;       /* Pattern start     */
  401.    register int    c;         /* Current character */
  402.    int             o;         /* Temp              */
  403.  
  404.    s = src;
  405.    o = CLASS;
  406.    if (*s == '^') {
  407.       ++s;
  408.       o = NCLASS;
  409.    }
  410.    store(o);
  411.    cp = pp;
  412.    store(0);                          /* Byte count      */
  413.    while ((c = *s++) && c!=']') {
  414.       if (c == '\\') {                /* Store quoted char    */
  415.          if ((c = *s++) == '\0')      /* Gotta get something  */
  416.             badpat("Class terminates badly", source, s);
  417.          else    store(tolower(c));
  418.       }
  419.       else if (c == '-' &&
  420.             (pp - cp) > 1 && *s != ']' && *s != '\0') {
  421.          c = pp[-1];             /* Range start     */
  422.          pp[-1] = RANGE;         /* Range signal    */
  423.          store(c);               /* Re-store start  */
  424.          c = *s++;               /* Get end char and*/
  425.          store(tolower(c));      /* Store it        */
  426.       }
  427.       else {
  428.          store(tolower(c));      /* Store normal char */
  429.       }
  430.    }
  431.    if (c != ']')
  432.       badpat("Unterminated class", source, s);
  433.    if ((c = (pp - cp)) >= 256)
  434.       badpat("Class too large", source, s);
  435.    if (c == 0)
  436.       badpat("Empty class", source, s);
  437.    *cp = c;
  438.    return(s);
  439. }
  440.  
  441. /*******************************************************/
  442.  
  443. store(op)
  444. {
  445.    if (pp >= &pbuf[PMAX])
  446.       error("Pattern too complex\n");
  447.    *pp++ = op;
  448. }
  449.  
  450.  
  451. /*******************************************************/
  452.  
  453. badpat(message, source, stop)
  454. char  *message;       /* Error message */
  455. char  *source;        /* Pattern start */
  456. char  *stop;          /* Pattern end   */
  457. {
  458.    register int    c;
  459.  
  460.    fprintf(stderr, "-GREP-E-%s, pattern is\"%s\"\n", message, source);
  461.    fprintf(stderr, "-GREP-E-Stopped at byte %d, '%c'\n",
  462.          stop-source, stop[-1]);
  463.    error("?GREP-E-Bad pattern\n");
  464. }
  465.  
  466.  
  467.  
  468. /*******************************************************/
  469.  
  470. grep(fp, fn)
  471. FILE       *fp;       /* File to process            */
  472. char       *fn;       /* File name (for -f option)  */
  473. /*
  474.  * Scan the file for the pattern in pbuf[]
  475.  */
  476. {
  477.    register int lno, count, m;
  478.  
  479.    lno = 0;
  480.    count = 0;
  481.    while (fgets(lbuf, LMAX, fp)) {
  482.       ++lno;
  483.       m = match();
  484.       if ((m && !vflag) || (!m && vflag)) {
  485.          ++count;
  486.          if (!cflag) {
  487.             if (fflag && fn) {
  488.                file(fn);
  489.                fn = 0;
  490.             }
  491.             if (nflag)
  492.                printf("%d\t", lno);
  493.             printf("%s\n", lbuf);
  494.          }
  495.       }
  496.    }
  497.    if (cflag) {
  498.       if (fflag && fn)
  499.          file(fn);
  500.       printf("%d\n", count);
  501.    }
  502. }
  503.  
  504.  
  505. /*******************************************************/
  506.  
  507. match()
  508. /*
  509.  * Match the current line (in lbuf[]), return 1 if it does.
  510.  */
  511. {
  512.    register char   *l;        /* Line pointer       */
  513.    char *pmatch();
  514.  
  515.    for (l = lbuf; *l; l++) {
  516.       if (pmatch(l, pbuf))
  517.          return(1);
  518.    }
  519.    return(0);
  520. }
  521.  
  522. /*******************************************************/
  523.  
  524. char *
  525. pmatch(line, pattern)
  526. char               *line;     /* (partial) line to match      */
  527. char               *pattern;  /* (partial) pattern to match   */
  528. {
  529.    register char   *l;        /* Current line pointer         */
  530.    register char   *p;        /* Current pattern pointer      */
  531.    register char   c;         /* Current character            */
  532.    char            *e;        /* End for STAR and PLUS match  */
  533.    int             op;        /* Pattern operation            */
  534.    int             n;         /* Class counter                */
  535.    char            *are;      /* Start of STAR match          */
  536.  
  537.    l = line;
  538.    if (debug > 1)
  539.       printf("pmatch(\"%s\")\n", line);
  540.    p = pattern;
  541.    while ((op = *p++) != ENDPAT) {
  542.       if (debug > 1)
  543.          printf("byte[%d] = 0%o, '%c', op = 0%o\n",
  544.                l-line, *l, *l, op);
  545.       switch(op) {
  546.  
  547.       case CHAR:
  548.          if (tolower(*l++) != *p++)
  549.             return(0);
  550.          break;
  551.  
  552.       case BOL:
  553.          if (l != lbuf)
  554.             return(0);
  555.          break;
  556.  
  557.       case EOL:
  558.          if (*l != '\0')
  559.             return(0);
  560.          break;
  561.  
  562.       case ANY:
  563.          if (*l++ == '\0')
  564.             return(0);
  565.          break;
  566.  
  567.       case DIGIT:
  568.          if ((c = *l++) < '0' || (c > '9'))
  569.             return(0);
  570.          break;
  571.  
  572.       case ALPHA:
  573.          c = tolower(*l++);
  574.          if (c < 'a' || c > 'z')
  575.             return(0);
  576.          break;
  577.  
  578.       case NALPHA:
  579.          c = tolower(*l++);
  580.          if (c >= 'a' && c <= 'z')
  581.             break;
  582.          else if (c < '0' || c > '9')
  583.             return(0);
  584.          break;
  585.  
  586.       case PUNCT:
  587.          c = *l++;
  588.          if (c == 0 || c > ' ')
  589.             return(0);
  590.          break;
  591.  
  592.       case CLASS:
  593.       case NCLASS:
  594.          c = tolower(*l++);
  595.          n = *p++ & 0377;
  596.          do {
  597.             if (*p == RANGE) {
  598.                p += 3;
  599.                n -= 2;
  600.                if (c >= p[-2] && c <= p[-1])
  601.                   break;
  602.             }
  603.             else if (c == *p++)
  604.                break;
  605.          } while (--n > 1);
  606.          if ((op == CLASS) == (n <= 1))
  607.             return(0);
  608.          if (op == CLASS)
  609.             p += n - 2;
  610.          break;
  611.  
  612.       case MINUS:
  613.          e = pmatch(l, p);       /* Look for a match    */
  614.          while (*p++ != ENDPAT); /* Skip over pattern   */
  615.          if (e)                  /* Got a match?        */
  616.             l = e;               /* Yes, update string  */
  617.          break;                  /* Always succeeds     */
  618.  
  619.       case PLUS:                 /* One or more ...     */
  620.          if ((l = pmatch(l, p)) == 0)
  621.             return(0);           /* Gotta have a match  */
  622.       case STAR:                 /* Zero or more ...    */
  623.          are = l;                /* Remember line start */
  624.          while (*l && (e = pmatch(l, p)))
  625.             l = e;               /* Get longest match   */
  626.          while (*p++ != ENDPAT); /* Skip over pattern   */
  627.          while (l >= are) {      /* Try to match rest   */
  628.             if (e = pmatch(l, p))
  629.                return(e);
  630.             --l;                 /* Nope, try earlier   */
  631.          }
  632.          return(0);              /* Nothing else worked */
  633.  
  634.       default:
  635.          printf("Bad op code %d\n", op);
  636.          error("Cannot happen -- match\n");
  637.       }
  638.    }
  639.    return(l);
  640. }
  641.  
  642. /*******************************************************/
  643.  
  644. error(s)
  645. char *s;
  646. {
  647.    fprintf(stderr, "%s", s);
  648.    exit(1);
  649. }
  650.  
  651. /*******************************************************/
  652.  
  653. #ifdef DeSmet
  654. fclose(file)
  655. FILE file;
  656. {
  657.      return(close(file));
  658. }
  659. #endif
  660. ə