home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume3 / decus_grep < prev    next >
Internet Message Format  |  1986-11-30  |  15KB

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