home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / enterprs / c128 / util / grep.sfx / grep.c next >
Encoding:
C/C++ Source or Header  |  1990-02-02  |  16.2 KB  |  645 lines

  1. /*  grep.c
  2.     =============================================================
  3.     UNIX grep utility
  4.     =============================================================
  5.     Note:   Compiles with Zortech C++ v 2.0  (__ZTC__ defined)
  6.             Guidelines 2.0 + Manx CG65       (MPU6502 defined)
  7. */
  8.  
  9. #include <stdio.h>
  10. #include <ctype.h>
  11. #include <string.h>
  12. #include <dos.h>
  13.  
  14. int     main    (int argc, char ** argv);
  15. void    file    (char * s);
  16. void    cant    (char * s);
  17. void    help    (char ** hp);
  18. void    Usage   (void);
  19. void    compile (char * source);
  20. char *  cclass  (char * source,char * src);
  21. void    store   (int op);
  22. void    badpat  (char * message, char * source, char * stop);
  23. void    grep    (FILE * fp,char * fn);
  24. int     match   (void);
  25. char *  pmatch  (char * line, char * pattern);
  26. void    error   (char * s);
  27. char    ToLower (char c);
  28.  
  29. #define LMAX     512    // Size of input line buffer
  30. #define PMAX     512    // Maximum compiled pattern length
  31.  
  32. enum {  CHAR=1,         // Any character
  33.         BOL,            // Start of line
  34.         EOL,            // End of line
  35.         ANY,            // Wildcard character
  36.         CLASS,          // Class of characters
  37.         NOTCLASS,       // Not Class
  38.         ZERO_OR_MORE,
  39.         ONE_OR_MORE,
  40.         OR,
  41.         ALPHA,
  42.         DIGIT,
  43.         HEXDIGIT,
  44.         QUOTE,
  45.         PUNCT,          // Punctuation character ,.; etc.
  46.         NALPHA,         // Alphanumeric
  47.         WHITESPACE,     // Whitespace
  48.         RANGE,          // Range of characters
  49.         ENDPAT
  50. };
  51.  
  52. int cflag    = 0;       // Default. Don't just count lines
  53. int fflag    = 1;       // Default. Show filenames
  54. int nflag    = 0;       // Default. Don't show line numbers
  55. int vflag    = 0;       // Default. Matching lines
  56. int caseflag = 1;       // Default. Case sensitive
  57.  
  58. int nfile;
  59.  
  60. char    *pp;
  61. char    lbuf[LMAX+2];
  62. char    pbuf[PMAX+2];
  63.  
  64. #ifdef MONITOR
  65. #include <prof.hpp>
  66. Profiler GrepProf("Grep");
  67. #endif
  68.  
  69.  
  70. int main(int argc, char **argv)
  71. {
  72.     register char * p, *cp;
  73.     register int c, i;
  74.     int gotpattern;
  75.     FILE * f;
  76.     struct FIND * finfo;
  77.     char path[64];
  78.  
  79.  
  80.     if (argc <= 2)
  81.         Usage();
  82.  
  83.     nfile = argc-1;
  84.     gotpattern = 0;
  85.  
  86.     for (i=1; i < argc; ++i) {
  87.  
  88.         p = argv[i];
  89.  
  90.         if (*p == '-') {
  91.             ++p;
  92.             while ( (c = *p++) != 0) {
  93.  
  94.                 switch(tolower(c)) {
  95.  
  96.                     case 'c':
  97.  
  98.                         ++cflag;
  99.                         break;
  100.  
  101.                     case 'f':
  102.  
  103.                         ++fflag;
  104.                         break;
  105.  
  106.                     case 'n':
  107.  
  108.                        ++nflag;
  109.                        break;
  110.  
  111.                     case 'v':
  112.  
  113.                         ++vflag;
  114.                         break;
  115.  
  116.                     case 'y':               // -y -> make case insensitive
  117.  
  118.                         caseflag = 0;
  119.                         break;
  120.  
  121.                     default:
  122.  
  123.                         printf("Unknown Option : \"-%c\"\n",c);
  124.                         exit(1);
  125.                 }
  126.             }
  127.             argv[i] = 0;
  128.             --nfile;
  129.  
  130.         } else if (!gotpattern) {
  131.             compile(p);
  132.             argv[i] = 0;
  133.             ++gotpattern;
  134.             --nfile;
  135.         }
  136.     }
  137.     if (!gotpattern)
  138.         error("No pattern");
  139.  
  140.     if (nfile != 0) {
  141.  
  142.         for (i=1; i < argc; ++i) {
  143.  
  144.             if ((p = argv[i]) != 0) {
  145.  
  146.                 finfo = findfirst(p, 0);
  147.  
  148.                 strcpy(path,p);
  149. #ifndef MPU6502
  150.                 cp = strrchr(path,'\\');
  151.                 if (cp == 0)
  152. #endif
  153.                 cp = strchr(path,':');
  154.  
  155.                 if (cp == 0)
  156.                     cp = path;
  157.                 else
  158.                     cp++;
  159.  
  160.                 while(finfo != 0) {
  161.  
  162.                     strcpy(cp,finfo->name);         // name = path+name
  163.  
  164.                     if ((f=fopen(path, "r")) == 0)
  165.                         cant(path);
  166.                     else {
  167.                         grep(f, path);
  168.                         fclose(f);
  169.                     }
  170.                     finfo = findnext();
  171.                 }
  172.             }
  173.         }
  174.     }
  175.     exit(0);
  176. }
  177.  
  178.  
  179.  
  180.  
  181. void file(char * s)
  182. {
  183.    printf("File : %-16s ", s);
  184. }
  185.  
  186.  
  187. void cant(char * s)
  188. {
  189.    printf("Can't open \"%s\"\n", s);
  190. }
  191.  
  192.  
  193.  
  194. void Usage()
  195. {
  196.     printf("GREP [-options] reg_expr file(s)\n\n");
  197.     printf(" -c = Show # of matches only.  -n = Show line numbers\n");
  198.     printf(" -f = Don't show filenames     -v = Show only nonmatching lines\n");
  199.     printf(" -y = Ignore case\n\n");
  200.  
  201.     exit(1);
  202. }
  203.  
  204. /*  -----------------------------------------------
  205.     Compile the pattern 'source' into global pbuf[]
  206.     -----------------------------------------------
  207. */
  208.  
  209. void compile(char * source)
  210. {
  211.     register char * s;                  // Source string pointer
  212.     register char * lp;                 // Last pattern pointer
  213.     register int c;                     // Current character
  214.     int o;                              // Temp
  215.     char * spp;                         // Save beginning of pattern
  216.  
  217.     s = source;
  218.  
  219.     pp = pbuf;
  220.     lp = pp;
  221.  
  222.     while ((c = *s++) != 0) {           // ZERO_OR_MORE, ONE_OR_MORE and OR are special
  223.  
  224.         if (c == '*' || c == '+' || c == '-') {
  225.  
  226.             if (pp==pbuf || (o=pp[-1])==BOL || o==EOL || o==ZERO_OR_MORE || o==ONE_OR_MORE || o==OR)
  227.                 badpat("Illegal op ", source, s);
  228.  
  229.             store(ENDPAT);
  230.             store(ENDPAT);
  231.             spp = pp;                   // Save pattern end
  232.  
  233.             while (--pp > lp)           // Move pattern down
  234.                 *pp = pp[-1];           // one byte
  235.  
  236.                 *pp = (c == '*') ? ZERO_OR_MORE : (c == '-') ? OR : ONE_OR_MORE;
  237.  
  238.                 pp = spp;               // Restore pattern end
  239.                 continue;
  240.             }
  241.  
  242.             // All the rest.
  243.  
  244.             lp = pp;                    // Remember start
  245.  
  246.             switch(c) {
  247.  
  248.                 case '^':
  249.  
  250.                     store(BOL);
  251.                     break;
  252.  
  253.                 case '$':
  254.  
  255.                     store(EOL);
  256.                     break;
  257.  
  258.                 case '.':
  259.  
  260.                     store(ANY);
  261.                     break;
  262.  
  263.                 case '[':
  264.  
  265.                     s = cclass(source, s);
  266.                     break;
  267.  
  268.                 case ':':
  269.  
  270.                     if (*s) {
  271.                         c = *s++;
  272.                         switch(tolower(c)) {
  273.                             case 'a':
  274.                                 store(ALPHA);
  275.                                 break;
  276.                             case 'd':
  277.                                 store(DIGIT);
  278.                                 break;
  279.                             case 'n':
  280.                                 store(NALPHA);
  281.                                 break;
  282.                             case 'b':
  283.                                 store(WHITESPACE);
  284.                                 break;
  285.                             case 'x':
  286.                                 store(HEXDIGIT);
  287.                                 break;
  288.                             case 'p':
  289.                                 store(PUNCT);
  290.                                 break;
  291.                             case 'q':
  292.                                 store(QUOTE);
  293.                                 break;
  294.                             default:
  295.                                badpat("Unknown predefined class", source, s);
  296.                         }
  297.                         break;
  298.                     }
  299.                     else
  300.                         badpat("':' needs class", source, s);
  301.  
  302.                 case '\\':
  303.  
  304.                     if (*s)
  305.                         c = *s++;
  306.  
  307.                 default:
  308.  
  309.                     store(CHAR);
  310.                     store(ToLower(c));
  311.             }
  312.     }
  313.     store(ENDPAT);
  314.     store(0);                           // Terminate string
  315.  
  316. }
  317.  
  318.  
  319. /*  ------------------------------------------
  320.     Compile class within []
  321.  
  322.     source = Pattern start - for error message
  323.     src    = Class start
  324.     ------------------------------------------
  325. */
  326.  
  327. char * cclass(char * source, char * src)
  328. {
  329.     register char * s;                  // Source pointer
  330.     register char * cp;                 // Pattern start
  331.     register int c;                     // Current character
  332.     int o;                              // Temp
  333.  
  334.     s = src;
  335.     o = CLASS;
  336.  
  337.     if (*s == '^') {
  338.         ++s;
  339.         o = NOTCLASS;
  340.     }
  341.  
  342.     store(o);
  343.     cp = pp;
  344.     store(0);                           // Byte count
  345.  
  346.     while (((c = *s++) != 0) && c!=']') {
  347.  
  348.         if (c == '\\') {                // Store quoted char
  349.             if ((c = *s++) == '\0')     // Gotta get something
  350.                 badpat("Bad terminator", source, s);
  351.             else
  352.                 store(ToLower(c));
  353.         }
  354.         else if (c == '-' && (pp - cp) > 1 && *s != ']' && *s != '\0') {
  355.             c = pp[-1];                 // Range start
  356.             pp[-1] = RANGE;             // Range signal
  357.             store(c);                   // Re-store start
  358.             c = *s++;                   // Get end char and
  359.             store(ToLower(c));          // Store it
  360.         }
  361.         else {
  362.             store(ToLower(c));          // Store normal char
  363.         }
  364.     }
  365.  
  366.     if (c != ']')
  367.         badpat("Need ']'", source, s);
  368.  
  369.     if ((c = (pp - cp)) >= 256)
  370.         badpat("Class too large", source, s);
  371.  
  372.     if (c == 0)
  373.         badpat("Empty class", source, s);
  374.  
  375.     *cp = c;
  376.     return(s);
  377. }
  378.  
  379.  
  380. void store(int op)
  381. {
  382.     if (pp >= &pbuf[PMAX])
  383.         error("Pattern too complex\n");
  384.     *pp++ = op;
  385. }
  386.  
  387.  
  388. /*  ------------------------------------
  389.     message = error message
  390.     source  = start of offending pattern
  391.     stop    = end of offending pattern
  392.     ------------------------------------
  393. */
  394.  
  395. void badpat(char * message, char * source, char * stop)
  396. {
  397.     printf("\"%s\" - Bad Pattern - %s\n",source,message);
  398.  
  399.     for(int i=0; i<stop-source; i++)
  400.         printf(" ");
  401.     printf("^");
  402.     exit(1);
  403. }
  404.  
  405.  
  406. /*  -------------------------------------
  407.     Scan the file for the pattern in ebuf
  408.     -------------------------------------
  409.     fp = file to process
  410.     fn = File name for -f option
  411. */
  412.  
  413. void grep(FILE * fp, char * fn)
  414. {
  415.     register int lno, count, m;
  416.  
  417.     lno = 0;
  418.     count = 0;
  419.  
  420.     while (fgets(lbuf, LMAX, fp)) {
  421.  
  422.         m = strlen(lbuf) - 1;           // Drop CR-LF from end of line
  423.  
  424.         while( m && lbuf[m] == '\x0a' || lbuf[m] == '\x0d') {
  425.             lbuf[m--] = 0;
  426.         }
  427.  
  428.         ++lno;
  429.         m = match();
  430.         if ((m && !vflag) || (!m && vflag)) {
  431.             ++count;
  432.             if (!cflag) {
  433.                 if (fflag && fn) {
  434.                     file(fn);
  435.                     fn = 0;
  436.                     printf("\n");
  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("%u match(es)\n", count);
  448.    }
  449. }
  450.  
  451.  
  452. /*  -------------------------------------------------------
  453.     Match the current line (in lbuf[]), return 1 if it does
  454.     -------------------------------------------------------
  455. */
  456.  
  457. int match()
  458. {
  459.     register char * l;                      // Line pointer
  460.  
  461.     for (l = lbuf; *l; l++) {
  462.         if (pmatch(l, pbuf))
  463.             return(1);
  464.     }
  465.     return(0);
  466. }
  467.  
  468.  
  469. /*  --------------------------------------------------------------
  470.     Match a (partial) pattern in a (partial) line
  471.     --------------------------------------------------------------
  472.     This routine, match() and fgets() account for the bulk of the
  473.     program's execution time
  474. */
  475.  
  476. char * pmatch(char * line, char * pattern)
  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 ZERO_OR_MORE and ONE_OR_MORE match
  482.     int op;                             // Pattern operation
  483.     int n;                              // Class counter
  484.     char * are;                         // Start of ZERO_OR_MORE match
  485.  
  486.     l = line;
  487.     p = pattern;
  488.  
  489.     while ((op = *p++) != ENDPAT) {
  490.  
  491.         switch(op) {
  492.  
  493.             case CHAR:
  494.  
  495.                 if (ToLower(*l) != *p++)
  496.                     return(0);
  497.                 l++;
  498.                 break;
  499.  
  500.             case BOL:
  501.  
  502.                 if (l != lbuf)
  503.                     return(0);
  504.                 break;
  505.  
  506.             case EOL:
  507.  
  508.                 if (*l != '\0')
  509.                     return(0);
  510.                 break;
  511.  
  512.             case ANY:
  513.  
  514.                 if (*l++ == '\0')
  515.                     return(0);
  516.                 break;
  517.  
  518.             case DIGIT:
  519.  
  520.                 if ((c = *l++) < '0' || (c > '9'))
  521.                     return(0);
  522.                 break;
  523.  
  524.             case HEXDIGIT:
  525.  
  526.                 c = *l++;
  527.                 if (!isxdigit(c))
  528.                     return 0;
  529.                 break;
  530.  
  531.             case PUNCT:
  532.  
  533.                 c = *l++;
  534.                 if (!ispunct(c))
  535.                     return 0;
  536.                 break;
  537.  
  538.             case QUOTE:
  539.  
  540.                 c = *l++;
  541.                 if ( !(c == '\"' || c == '\'') )
  542.                     return 0;
  543.                 break;
  544.  
  545.             case ALPHA:
  546.  
  547.                 c = *l++;
  548.                 if (!isalpha(c))
  549.                     return 0;
  550.                 break;
  551.  
  552.             case NALPHA:
  553.  
  554.                 c=*l++;
  555.                 if (!isalnum(c))
  556.                     return(0);
  557.                 break;
  558.  
  559.             case WHITESPACE:
  560.  
  561.                 c = *l++;
  562.                 if (c == 0 || !isspace(c))
  563.                     return(0);
  564.                 break;
  565.  
  566.             case CLASS:
  567.             case NOTCLASS:
  568.  
  569.                 c = ToLower(*l);
  570.                 l++;
  571.                 n = *p++ & 0xff;
  572.  
  573.                 do {
  574.                     if (*p == RANGE) {
  575.                         p += 3;
  576.                         n -= 2;
  577.                         if (c >= p[-2] && c <= p[-1])
  578.                             break;
  579.                     }
  580.                     else if (c == *p++)
  581.                         break;
  582.                 } while (--n > 1);
  583.  
  584.                 if ((op == CLASS) == (n <= 1))
  585.                     return(0);
  586.  
  587.                 if (op == CLASS)
  588.                     p += n - 2;
  589.                 break;
  590.  
  591.             case OR:
  592.  
  593.                 e = pmatch(l, p);                   // Look for a match
  594.  
  595.                 while (*p++ != ENDPAT)
  596.                     ;                               // Skip over pattern
  597.                 if (e)                              // Got a match?
  598.                     l = e;                          // Yes, update string
  599.                 break;                              // Always succeeds
  600.  
  601.             case ONE_OR_MORE:                       // One or more ...
  602.  
  603.                 if ((l = pmatch(l, p)) == 0)
  604.                     return(0);                      // Gotta have a match
  605.  
  606.             case ZERO_OR_MORE:                      // Zero or more ...
  607.  
  608.                 are = l;                            // Remember line start
  609.  
  610.                 while (*l && ((e = pmatch(l, p)) != 0))
  611.                     l = e;                          // Get longest match
  612.  
  613.                 while (*p++ != ENDPAT)
  614.                     ;                               // Skip over pattern
  615.  
  616.                 while (l >= are) {                  // Try to match rest
  617.                     if ((e = pmatch(l, p)) != 0)
  618.                         return(e);
  619.                     --l;                            // Nope, try earlier
  620.                 }
  621.                 return(0);                          // Nothing else worked
  622.  
  623.             default:
  624.  
  625.                 printf("Bad op\n");
  626.                 exit(1);
  627.         }
  628.     }
  629.     return(l);
  630. }
  631.  
  632.  
  633. void error(char * s)
  634. {
  635.     fprintf(stderr, "%s", s);
  636.     exit(1);
  637. }
  638.  
  639.  
  640. char ToLower(char c)
  641. {
  642.     return caseflag ? c : tolower(c);
  643. }
  644.  
  645.