home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / unix_c / utils / grep.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-03-21  |  14.6 KB  |  588 lines

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