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