home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / mavcl130.zip / MAVREGEX.CPP < prev    next >
Text File  |  1995-09-02  |  11KB  |  420 lines

  1. /*  File: MAVREGEX.CPP      Updated: Tue Aug 15 15:54:39 1995
  2. Copyright (c) Fabrizio Aversa
  3. ===========================================================*/
  4. #include <os2def.h>
  5.  
  6. #include <ctype.h>
  7. #include <iostream.h>
  8. #include <istring.hpp>
  9. #include <strstrea.h>
  10.  
  11. #include <mavregex.hpp>
  12.  
  13. /*
  14. Regex searches files for lines matching a regular expression.
  15.  
  16. Flags:
  17. -c only print count of matching lines
  18. -i ignore case in comparisons (GREP is case-sensitive by default)
  19. -n each line is preceded by its line number
  20. -v only print non-matching lines
  21.  
  22. The regular expression should be quoted if it contains blanks.
  23. Characters are matched by the following rules:
  24.  
  25. x any ordinary character not mentioned below
  26. '\'escapes (quotes) special characters seen below
  27. '^'beginning-of-line
  28. '$'end-of-line
  29. '.'any character except end-of-line
  30. ':a'alphabetic character
  31. ':d'digit
  32. ':n'alphanumeric
  33. ': 'whitespace or other non-printable characters
  34. '*'zero or more occurrences of previous character
  35. '+'one or more occurrences of previous character
  36. '-'optionally match previous character (say what?)
  37. '[]'character classes:
  38. --->match any character in the enumerated set
  39. (the example above matches a run of vowels followed by
  40. whitespace).  Ranges are allowed, as in [a-z], which
  41. would match a lower-case letter.  '^' as the first
  42. character in a class means match anything but what
  43. is in the class, so [^aeiou] would match a consonant.
  44.  
  45. */
  46. /* From DECUS C Tools package on DEC systems - for non-commercial
  47. use only.
  48. Modifications by Chuck Allison, April 1985:
  49.  
  50. - handles quoted args (for embedded spaces)
  51. - expands wildcards against indicated directory
  52. - distinguishes case (ignore with -i)
  53.  
  54. Modifications by Fabrizio Aversa, Feb 1994:
  55.  
  56. - C++ porting, stream handling, incapsulation in a class
  57. */
  58.  
  59. #define REGEXLMAX 512
  60. #define REGEXPMAX 256
  61. #define CHARAC    1
  62. #define BOL     2
  63. #define EOL     3
  64. #define ANY     4
  65. #define CLASS   5
  66. #define NCLASS  6
  67. #define STAR    7
  68. #define PLUS    8
  69. #define MINUS   9
  70. #define ALPHA   10
  71. #define DIGIT   11
  72. #define NALPHA  12
  73. #define WHITE   13
  74. #define RANGE   14
  75. #define ENDPAT  15
  76.  
  77. RegEx::RegEx(istream & is1, ostream & os1, IString & str1):
  78. isAct(is1), osAct(os1), strFlags(str1)
  79. {
  80.    parseFlags();
  81. }
  82.  
  83. void RegEx::parseFlags()
  84. {
  85.    cflag = strFlags.includes("c");
  86.    nflag = strFlags.includes("n");
  87.    vflag = strFlags.includes("v");
  88.    iflag = strFlags.includes("i");
  89. }
  90.  
  91. int RegEx::compile( char * source)
  92. /* ..Compile the pattern into global pbuf[].. */
  93. {
  94.    register char  *s;         /* Source string pointer     */
  95.    register char  *lp;        /* Last pattern pointer      */
  96.    register int   c;          /* Current character         */
  97.    int            o;          /* Temp                      */
  98.    char           *spp;       /* Save beginning of pattern */
  99.    
  100.    s = source;
  101.    pp = pbuf;
  102.    
  103.    while (c = *s++)
  104.    {
  105.       /* ..STAR, PLUS and MINUS are special.. */
  106.       if (c == '*' || c == '+' || c == '-')
  107.       {
  108.          if ( pp == pbuf ||
  109.          (o=pp[-1]) == BOL ||
  110.          o == EOL ||
  111.          o == STAR ||
  112.          o == PLUS ||
  113.          o == MINUS
  114.          ) {
  115.             badpat("Illegal occurrance op.", source, s);
  116.             return 0;
  117.          }
  118.          if(!store(ENDPAT)) return 0;
  119.          if(!store(ENDPAT)) return 0;
  120.          spp = pp;               /* Save pattern end     */
  121.          while (--pp > lp)       /* Move pattern down    */
  122.          *pp = pp[-1];        /* one byte             */
  123.          *pp =   (c == '*') ? STAR :
  124.          (c == '-') ? MINUS : PLUS;
  125.          pp = spp;               /* Restore pattern end  */
  126.          continue;
  127.       }
  128.       
  129.       /* ..All the rest.. */
  130.       lp = pp;         /* ..Remember start.. */
  131.       switch(c)
  132.       {
  133.          case '^':
  134.          if(!store(BOL)) return 0;
  135.          break;
  136.          case '$':
  137.          if(!store(EOL)) return 0;
  138.          break;
  139.          case '.':
  140.          if(!store(ANY)) return 0;
  141.          break;
  142.          case '[':
  143.          if(!cclass(source, &s)) return 0;
  144.          break;
  145.          case ':':
  146.          if (*s)
  147.          {
  148.             c = *s++;
  149.             switch(tolower(c))
  150.             {
  151.                case 'a':
  152.                if(!store(ALPHA)) return 0;
  153.                break;
  154.                case 'd':
  155.                if(!store(DIGIT)) return 0;
  156.                break;
  157.                case 'n':
  158.                if(!store(NALPHA)) return 0;
  159.                break;
  160.                case ' ':
  161.                if(!store(WHITE)) return 0;
  162.                break;
  163.                default:
  164.                badpat("Unknown : type", source, s);
  165.                return 0;
  166.             }
  167.             break;
  168.          }
  169.          else {
  170.             badpat("No : type", source, s);
  171.             return 0;
  172.          }
  173.          case '\\':
  174.          if (*s)
  175.          c = *s++;
  176.          default:
  177.          if(!store(CHARAC)) return 0;
  178.          if(!store(iflag ? tolower(c) : c)) return 0;
  179.       }
  180.    }
  181.    if(!store(ENDPAT)) return 0;
  182.    if(!store('\0')) return 0;
  183.    return 1;
  184. }
  185.  
  186.  
  187. int RegEx::cclass(char * source, char **src)
  188. /* char *source; ..Pattern start.. */
  189. /* char *src;    ..Class start.. */
  190. /* ..Compile a class (within []).. */
  191. {
  192.    register char   *s;        /* Source pointer    */
  193.    register char   *cp;       /* Pattern start     */
  194.    register int    c;         /* Current character */
  195.    int             o;         /* ..Temp.. */
  196.    
  197.    s = *src;
  198.    o = CLASS;
  199.    if (*s == '^')
  200.    {
  201.       ++s;
  202.       o = NCLASS;
  203.    }
  204.    if(!store(o)) return 0;
  205.    cp = pp;
  206.    if(!store(0)) return 0;                         /* ..Byte count.. */
  207.    
  208.    while ((c = *s++) && c!=']')
  209.    {
  210.       if (c == '\\')
  211.       {  /* ..Store quoted char.. */
  212.          if ((c = *s++) == '\0')  {
  213.             /* ..Gotta get something.. */
  214.             badpat("Class terminates badly", source, s);
  215.             return 0;
  216.          }  else
  217.          if(!store(iflag ? tolower(c) : c)) return 0;
  218.       }
  219.       else if (c == '-' && (pp - cp) > 1 && *s != ']' && *s != '\0')
  220.       {
  221.          c = pp[-1];             /* Range start     */
  222.          pp[-1] = RANGE;         /* Range signal    */
  223.          if(!store(c)) return 0;               /* Re-store start  */
  224.          c = *s++;               /* Get end char and*/
  225.          if(!store(iflag ? tolower(c) : c)) return 0;
  226.       }
  227.       else
  228.       if(!store(iflag ? tolower(c) : c)) return 0;
  229.    }
  230.    
  231.    if (c != ']') {
  232.       badpat("Unterminated class", source, s);
  233.       return 0;
  234.    }
  235.    if ((c = (pp - cp)) >= 256) {
  236.       badpat("Class too large", source, s);
  237.       return 0;
  238.    }
  239.    if (c == 0) {
  240.       badpat("Empty class", source, s);
  241.       return 0;
  242.    }
  243.    
  244.    *cp = c;
  245.    
  246.    *src= s;
  247.    return 1;
  248. }
  249.  
  250. int RegEx::store(int op)
  251. {
  252.    if (pp >= &pbuf[REGEXPMAX]) return 0;
  253.    *pp++ = op;
  254.    return 1;
  255. }
  256.  
  257. void RegEx::badpat(char * message, char * source, char * s)
  258. /*  char  *message,       ..Error message.. */
  259. /* *source;        ..Pattern start.. */
  260. {
  261.    cerr << "-GREP-E-%s, pattern is " << message
  262.    << ' ' << source << ' ' << s;
  263. }
  264.  
  265. void RegEx::process(IString & strPattern)
  266. /* ..Scan the file for the pattern in pbuf[].. */
  267. {
  268.    register int lno, count, m;
  269.    
  270.    lno = 0;
  271.    count = 0;
  272.    char c;
  273.    int i;
  274.    
  275.    if(!compile((PSZ)strPattern)) {
  276.       cerr << "can't compile " << endl;
  277.    }
  278.    
  279.    while (!isAct.eof())
  280.    {
  281.       
  282.       for(i=0;i<REGEXLMAX;i++) {
  283.          isAct >> c;
  284.          if (c==10 || isAct.eof()) break;
  285.          *(lbuf+i)= c;
  286.       }
  287.       *(lbuf+i)=0;
  288.       
  289.       ++lno;
  290.       m = match();
  291.       if ((m && !vflag) || (!m && vflag))
  292.       {
  293.          ++count;
  294.          if (!cflag)
  295.          {
  296.             if (nflag)
  297.             osAct << lno << '\t';
  298.             osAct << lbuf << endl;
  299.          }
  300.       }
  301.    }
  302. }
  303.  
  304. int RegEx::match()
  305. /* ..Match the current line (in lbuf[]), return 1 if it does.. */
  306. {
  307.    register char *l;        /* ..Line pointer.. */
  308.    
  309.    for (l = lbuf; *l; l++)
  310.    if (pmatch(l, pbuf))
  311.    return(1);
  312.    
  313.    return(0);
  314. }
  315.  
  316. char * RegEx::pmatch(char * line, char *pattern)
  317. /* char *line,     ..(partial) line to match.. */
  318. /* *pattern;   ..(partial) pattern to match.. */
  319. {
  320.    register char   *l;        /* ..Current line pointer.. */
  321.    register char   *p;        /* ..Current pattern pointer.. */
  322.    register char   c;         /* ..Current character.. */
  323.    register char   d;         /* ..Temporary character.. */
  324.    char            *e;        /* ..End for STAR and PLUS match.. */
  325.    int             op;        /* ..Pattern operation.. */
  326.    int             n;         /* ..Class counter.. */
  327.    char            *are;      /* ..Start of STAR match.. */
  328.    
  329.    l = line;
  330.    p = pattern;
  331.    while ((op = *p++) != ENDPAT)
  332.    {
  333.       switch(op)
  334.       {
  335.          case CHARAC:
  336.          c = iflag ? tolower(*l++) : *l++;
  337.          d = iflag ? tolower(*p++) : *p++;
  338.          if (c != d)
  339.          return(0);
  340.          break;
  341.          case BOL:
  342.          if (l != lbuf)
  343.          return(0);
  344.          break;
  345.          case EOL:
  346.          if (*l != '\0' && *l != '\n')
  347.          return(0);
  348.          break;
  349.          case ANY:
  350.          if (*l++ == '\0')
  351.          return(0);
  352.          break;
  353.          case DIGIT:
  354.          if (!isdigit(c = *l++))
  355.          return(0);
  356.          break;
  357.          case ALPHA:
  358.          if (!isalpha(c = *l++))
  359.          return(0);
  360.          break;
  361.          case NALPHA:
  362.          if (!isalnum(c = *l++))
  363.          return(0);
  364.          break;
  365.          case WHITE:
  366.          if (!isspace(c = *l++))
  367.          return(0);
  368.          break;
  369.          case CLASS:
  370.          case NCLASS:
  371.          c = iflag ? tolower(*l++) : *l++;
  372.          n = *p++ & 0x00ff;
  373.          do
  374.          {
  375.             if (*p == RANGE)
  376.             {
  377.                p += 3;
  378.                n -= 2;
  379.                if (c >= p[-2] && c <= p[-1])
  380.                break;
  381.             }
  382.             else if (c == *p++)
  383.             break;
  384.          } while (--n > 1);
  385.          if ((op == CLASS) == (n <= 1))
  386.          return(0);
  387.          if (op == CLASS)
  388.          p += n - 2;
  389.          break;
  390.          case MINUS:
  391.          e = pmatch(l, p);       /* ..Look for a match.. */
  392.          while (*p++ != ENDPAT); /* ..Skip over pattern.. */
  393.          if (e)                  /* ..Got a match?.. */
  394.          l = e;               /* ..Yes, update string.. */
  395.          break;                  /* ..Always succeeds.. */
  396.          case PLUS:                 /* ..One or more.. */
  397.          if ((l = pmatch(l, p)) == 0)
  398.          return(0);           /* ..Gotta have a match.. */
  399.          case STAR:                 /* ..Zero or more.. */
  400.          are = l;                /* ..Remember line start.. */
  401.          while (*l && (e = pmatch(l, p)))
  402.          l = e;               /* ..Get longest match.. */
  403.          while (*p++ != ENDPAT); /* ..Skip over pattern.. */
  404.          while (l >= are)
  405.          {  /* ..Try to match rest.. */
  406.             e = pmatch(l, p);
  407.             if (e)
  408.             return(e);
  409.             --l;                 /* ..Nope, try earlier.. */
  410.          }
  411.          return(0);              /* ..Nothing else worked.. */
  412.          default:
  413.          cerr << "Bad op code " << op << endl;
  414.          cerr << "Cannot happen -- match" << endl;
  415.       }
  416.    }
  417.    return(l);
  418. }
  419.  
  420.