Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

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