home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / MISC / lgrep_v2.lzh / lgrep.c next >
C/C++ Source or Header  |  1994-01-29  |  15KB  |  562 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. /* Readapted to work on a single string.  Renamed to lgrep - BAS */
  23. /* Debug flag added. 
  24.    Corrected to compile under OSK. 
  25.    Checked to be SCULPTOR compatible. 
  26.    Sticky bit set in makefile.
  27.    exit(2) changed to exit(0) after help, usage and error
  28.       displays to avoid OSK error translations               MCG */
  29.  
  30. #include <stdio.h>
  31.  
  32. char    *documentation[] = {
  33. "lgrep searches a string for a given pattern.  Execute by",
  34. "   lgrep [flags] regular_expression string",
  35. "",
  36. "Flags are single characters preceeded by '-':",
  37. "   -v      Reverses the truth of the result",
  38. " lgrep returns 1 if the result is TRUE, returns 0 is FALSE",
  39. " and anything else if there is an error",
  40. 0 };
  41.  
  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. "       occurrances of that expression: \"fo*\" matches \"f\", \"fo\"",
  59. "       \"foo\", etc.",
  60. "'+'    An expression followed by a plus sign matches one or more",
  61. "       occurrances 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 ENDOFLINE     3        /* changed by MCG; OSK stdio.h defines EOL */
  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     vflag;
  95.  
  96. int     debug   =       0;       
  97.  
  98. char    *pp;
  99.  
  100. char    lbuf[LMAX];
  101. char    pbuf[PMAX];
  102.  
  103. /*******************************************************/
  104.  
  105. int main(argc, argv)
  106. int argc;
  107. char *argv[];
  108. {
  109.    register char   *p;
  110.    register int    c, i;
  111.    int             gotpattern;
  112.    int             gotcha;
  113.    int pi;
  114.    int r;
  115.  
  116.    int grep();
  117.  
  118.    if (argc <= 1)
  119.       usage("No arguments");
  120.  
  121.    gotpattern = 0;
  122.    for (i=1; i < argc; ++i) {
  123.       p = argv[i];
  124.       if (*p == '-') {
  125.          ++p;
  126.          while (c = *p++) {
  127.             switch(tolower(c)) {
  128.  
  129.             case '?':
  130.                help(documentation);
  131.                help(patdoc);
  132.                exit(0);
  133.                break;
  134.  
  135.             case 'd':
  136.             case 'D':
  137.                ++debug;
  138.                break;
  139.  
  140.             case 'v':
  141.             case 'V':
  142.                ++vflag;
  143.                break;
  144.  
  145.             default:
  146.                usage("Unknown flag");
  147.             }
  148.          }
  149.       } else if (!gotpattern) {
  150.          pi=i;
  151.          compile(argv[pi]);
  152.          ++gotpattern;
  153.          break;
  154.       }
  155.    }
  156.  
  157.    if ((argc - 1) > (i + 1))
  158.       usage("Too many arguments");
  159.    if ((i + 1) == argc)
  160.       usage("Wrong number of arguments");
  161.  
  162.    r = grep(argv[i+1]);
  163.    if (vflag)
  164.       r = 1 - r;
  165.    if (debug)
  166.       printf("\nReply = %d\n",r);
  167.       
  168.    exit(r);    
  169. }
  170.  
  171. /*******************************************************/
  172.  
  173. help(hp)
  174. char **hp;  /* dns added extra '*'  */
  175. /*
  176.  * Give good help
  177.  */
  178. {
  179.    register char   **dp;
  180.  
  181.    for (dp = hp; *dp; dp++)
  182.       printf("%s\n", *dp);
  183. }
  184.  
  185.  
  186. /*******************************************************/
  187.  
  188. usage(s)
  189. char    *s;
  190. {
  191.    fprintf(stderr, "LGREP-E-%s\n", s);
  192.    fprintf(stderr,
  193.       "Usage: lgrep [-v?] pattern string  lgrep -? for help\n");
  194.    exit(0);
  195. }
  196.  
  197.  
  198.  
  199. /*******************************************************/
  200.  
  201.  
  202. compile(source)
  203. char       *source;   /* Pattern to compile         */
  204. /*
  205.  * Compile the pattern into global pbuf[]
  206.  */
  207. {
  208.    register char  *s;         /* Source string pointer     */
  209.    register char  *lp;        /* Last pattern pointer      */
  210.    register int   c;          /* Current character         */
  211.    int            o;          /* Temp                      */
  212.    char           *spp;       /* Save beginning of pattern */
  213.    char           *cclass();  /* Compile class routine     */
  214.  
  215.    s = source;
  216.    if (debug)
  217.       printf("Pattern = \"%s\"\n", s);
  218.    pp = pbuf;
  219.    while (c = *s++) {
  220.       /*
  221.        * STAR, PLUS and MINUS are special.
  222.        */
  223.       if (c == '*' || c == '+' || c == '-') {
  224.          if (pp == pbuf ||
  225.               (o=pp[-1]) == BOL ||
  226.               o == ENDOFLINE ||
  227.               o == STAR ||
  228.               o == PLUS ||
  229.               o == MINUS)
  230.             badpat("Illegal occurrance op.", source, s);
  231.          store(ENDPAT);
  232.          store(ENDPAT);
  233.          spp = pp;               /* Save pattern end     */
  234.          while (--pp > lp)       /* Move pattern down    */
  235.             *pp = pp[-1];        /* one byte             */
  236.          *pp =   (c == '*') ? STAR :
  237.             (c == '-') ? MINUS : PLUS;
  238.          pp = spp;               /* Restore pattern end  */
  239.          continue;
  240.       }
  241.       /*
  242.        * All the rest.
  243.        */
  244.       lp = pp;         /* Remember start       */
  245.       switch(c) {
  246.  
  247.       case '^':
  248.          store(BOL);
  249.          break;
  250.  
  251.       case '$':
  252.          store(ENDOFLINE);
  253.          break;
  254.  
  255.       case '.':
  256.          store(ANY);
  257.          break;
  258.  
  259.       case '[':
  260.          s = cclass(source, s);
  261.          break;
  262.  
  263.       case ':':
  264.          if (*s) {
  265.             c = *s++;
  266.             switch(tolower(c)) {
  267.  
  268.             case 'a':
  269.             case 'A':
  270.                store(ALPHA);
  271.                break;
  272.  
  273.             case 'd':
  274.             case 'D':
  275.                store(DIGIT);
  276.                break;
  277.  
  278.             case 'n':
  279.             case 'N':
  280.                store(NALPHA);
  281.                break;
  282.  
  283.             case ' ':
  284.                store(PUNCT);
  285.                break;
  286.  
  287.             default:
  288.                badpat("Unknown : type", source, s);
  289.  
  290.             }
  291.             break;
  292.          }
  293.          else    badpat("No : type", source, s);
  294.  
  295.       case '\\':
  296.          if (*s)
  297.             c = *s++;
  298.  
  299.       default:
  300.          store(CHAR);
  301.          store(tolower(c));
  302.       }
  303.    }
  304.    store(ENDPAT);
  305.    store(0);                /* Terminate string     */
  306.    if (debug) {
  307.       for (lp = pbuf; lp < pp;) {
  308.          if ((c = (*lp++ & 0377)) < ' ')
  309.             printf("\\%o ", c);
  310.          else    printf("%c ", c);
  311.         }
  312. /*        printf("\n");*/
  313.    }
  314. }
  315.  
  316. /*******************************************************/
  317.  
  318. char *
  319. cclass(source, src)
  320. char       *source;   /* Pattern start -- for error msg.      */
  321. char       *src;      /* Class start           */
  322. /*
  323.  * Compile a class (within [])
  324.  */
  325. {
  326.    register char   *s;        /* Source pointer    */
  327.    register char   *cp;       /* Pattern start     */
  328.    register int    c;         /* Current character */
  329.    int             o;         /* Temp              */
  330.  
  331.    s = src;
  332.    o = CLASS;
  333.    if (*s == '^') {
  334.       ++s;
  335.       o = NCLASS;
  336.    }
  337.    store(o);
  338.    cp = pp;
  339.    store(0);                          /* Byte count      */
  340.    while ((c = *s++) && c!=']') {
  341.       if (c == '\\') {                /* Store quoted char    */
  342.          if ((c = *s++) == '\0')      /* Gotta get something  */
  343.             badpat("Class terminates badly", source, s);
  344.          else    store(tolower(c));
  345.       }
  346.       else if (c == '-' &&
  347.             (pp - cp) > 1 && *s != ']' && *s != '\0') {
  348.          c = pp[-1];             /* Range start     */
  349.          pp[-1] = RANGE;         /* Range signal    */
  350.          store(c);               /* Re-store start  */
  351.          c = *s++;               /* Get end char and*/
  352.          store(tolower(c));      /* Store it        */
  353.       }
  354.       else {
  355.          store(tolower(c));      /* Store normal char */
  356.       }
  357.    }
  358.    if (c != ']')
  359.       badpat("Unterminated class", source, s);
  360.    if ((c = (pp - cp)) >= 256)
  361.       badpat("Class too large", source, s);
  362.    if (c == 0)
  363.       badpat("Empty class", source, s);
  364.    *cp = c;
  365.    return(s);
  366. }
  367.  
  368. /*******************************************************/
  369.  
  370. store(op)
  371. {
  372.    if (pp >= &pbuf[PMAX])
  373.       error("Pattern too complex\n");
  374.    *pp++ = op;
  375. }
  376.  
  377.  
  378. /*******************************************************/
  379.  
  380. badpat(message, source, stop)
  381. char  *message;       /* Error message */
  382. char  *source;        /* Pattern start */
  383. char  *stop;          /* Pattern end   */
  384. {
  385.    register int    c;
  386.  
  387.    fprintf(stderr, "-GREP-E-%s, pattern is\"%s\"\n", message, source);
  388.    fprintf(stderr, "-GREP-E-Stopped at byte %d, '%c'\n",
  389.          stop-source, stop[-1]);
  390.    error("?GREP-E-Bad pattern\n");
  391. }
  392.  
  393.  
  394.  
  395. /*******************************************************/
  396.  
  397. int grep(sp)
  398. char *sp;
  399. /*
  400.  * Scan the string for the pattern in pbuf[]
  401.  */
  402. {
  403.    register int lno, count;
  404.    int m;
  405.    
  406.    int match();
  407.  
  408.    lno = 0;
  409.    count = 0;
  410.    strcpy(lbuf,sp);
  411.    m = match();
  412.    return(m);
  413. }
  414.  
  415.  
  416. /*******************************************************/
  417.  
  418. int match()
  419. /*
  420.  * Match the current line (in lbuf[]), return 1 if it does.
  421.  */
  422. {
  423.    register char   *l;        /* Line pointer       */
  424.    char *pmatch();
  425.  
  426.    for (l = lbuf; *l; l++) {
  427.       if (pmatch(l, pbuf))
  428.          return(1);
  429.    }
  430.    return(0);
  431. }
  432.  
  433. /*******************************************************/
  434.  
  435. char *
  436. pmatch(line, pattern)
  437. char               *line;     /* (partial) line to match      */
  438. char               *pattern;  /* (partial) pattern to match   */
  439. {
  440.    register char   *l;        /* Current line pointer         */
  441.    register char   *p;        /* Current pattern pointer      */
  442.    register char   c;         /* Current character            */
  443.    char            *e;        /* End for STAR and PLUS match  */
  444.    int             op;        /* Pattern operation            */
  445.    int             n;         /* Class counter                */
  446.    char            *are;      /* Start of STAR match          */
  447.  
  448.    l = line;
  449.    if (debug > 1)
  450.       printf("pmatch(\"%s\")\n", line);
  451.    p = pattern;
  452.    while ((op = *p++) != ENDPAT) {
  453.       if (debug > 1)
  454.          printf("byte[%d] = 0%o, '%c', op = 0%o\n",
  455.                l-line, *l, *l, op);
  456.       switch(op) {
  457.  
  458.       case CHAR:
  459.          if (tolower(*l++) != *p++)
  460.             return(0);
  461.          break;
  462.  
  463.       case BOL:
  464.          if (l != lbuf)
  465.             return(0);
  466.          break;
  467.  
  468.       case ENDOFLINE:
  469.          if ((*l != '\0') && (*l != '\n'))
  470.             return(0);
  471.          break;
  472.  
  473.       case ANY:
  474.          if (*l++ == '\0')
  475.             return(0);
  476.          break;
  477.  
  478.       case DIGIT:
  479.          if ((c = *l++) < '0' || (c > '9'))
  480.             return(0);
  481.          break;
  482.  
  483.       case ALPHA:
  484.          c = tolower(*l++);
  485.          if (c < 'a' || c > 'z')
  486.             return(0);
  487.          break;
  488.  
  489.       case NALPHA:
  490.          c = tolower(*l++);
  491.          if (c >= 'a' && c <= 'z')
  492.             break;
  493.          else if (c < '0' || c > '9')
  494.             return(0);
  495.          break;
  496.  
  497.       case PUNCT:
  498.          c = *l++;
  499.          if (c == 0 || c > ' ')
  500.             return(0);
  501.          break;
  502.  
  503.       case CLASS:
  504.       case NCLASS:
  505.          c = tolower(*l++);
  506.          n = *p++ & 0377;
  507.          do {
  508.             if (*p == RANGE) {
  509.                p += 3;
  510.                n -= 2;
  511.                if (c >= p[-2] && c <= p[-1])
  512.                   break;
  513.             }
  514.             else if (c == *p++)
  515.                break;
  516.          } while (--n > 1);
  517.          if ((op == CLASS) == (n <= 1))
  518.             return(0);
  519.          if (op == CLASS)
  520.             p += n - 2;
  521.          break;
  522.  
  523.       case MINUS:
  524.          e = pmatch(l, p);       /* Look for a match    */
  525.          while (*p++ != ENDPAT); /* Skip over pattern   */
  526.          if (e)                  /* Got a match?        */
  527.             l = e;               /* Yes, update string  */
  528.          break;                  /* Always succeeds     */
  529.  
  530.       case PLUS:                 /* One or more ...     */
  531.          if ((l = pmatch(l, p)) == 0)
  532.             return(0);           /* Gotta have a match  */
  533.       case STAR:                 /* Zero or more ...    */
  534.          are = l;                /* Remember line start */
  535.          while (*l && (e = pmatch(l, p)))
  536.             l = e;               /* Get longest match   */
  537.          while (*p++ != ENDPAT); /* Skip over pattern   */
  538.          while (l >= are) {      /* Try to match rest   */
  539.             if (e = pmatch(l, p))
  540.                return(e);
  541.             --l;                 /* Nope, try earlier   */
  542.          }
  543.          return(0);              /* Nothing else worked */
  544.  
  545.       default:
  546.          printf("Bad op code %d\n", op);
  547.          error("Cannot happen -- match\n");
  548.       }
  549.    }
  550.    return(l);
  551. }
  552.  
  553. /*******************************************************/
  554.  
  555. error(s)
  556. char *s;
  557. {
  558.    fprintf(stderr, "%s", s);
  559.    exit(0);
  560. }
  561.  
  562.