/*static char *SCCSID = "@(#)qmatch.c 13.7 90/08/13";*/ #include #include #include #include #include #define ASCLEN 128 /* Number of ascii characters */ #define BUFLEN 256 /* Temporary buffer length */ #define EOS ('\r') /* End of string character */ #define PATMAX 512 /* Maximum parsed pattern length */ #define BEGLINE 0x08 /* Match at beginning of line */ #define DEBUG 0x20 /* Print debugging output */ #define ENDLINE 0x10 /* Match at end of line */ #define T_END 0 /* End of expression */ #define T_STRING 1 /* String to match */ #define T_SINGLE 2 /* Single character to match */ #define T_CLASS 3 /* Class to match */ #define T_ANY 4 /* Match any character */ #define T_STAR 5 /* *-expr */ typedef struct exprnode { struct exprnode *ex_next; /* Next node in list */ unsigned char *ex_pattern; /* Pointer to pattern to match */ } EXPR; /* Expression node */ static int clists = 1; /* One is first available index */ static int toklen[] = /* Table of token lengths */ { 32767, /* T_END: invalid */ 32767, /* T_STRING: invalid */ 2, /* T_SINGLE */ ASCLEN/8+1, /* T_CLASS */ 1, /* T_ANY */ 32767 /* T_STAR: invalid */ }; int ( __cdecl *ncmp)(const char *,const char *,size_t); /* String comparison pointer */ unsigned char *exprparse(unsigned char *p); extern int casesen; /* Case-sensitivity flag */ extern char *(*find)(); /* Pointer to search function */ extern int flags; /* Flags */ extern int strcnt; /* String count */ extern char transtab[]; /* Translation table */ EXPR *stringlist[ASCLEN]; /* String table */ void addexpr(char *e, int n); /* Add expression */ char *get1stcharset(unsigned char *e, char *bitvec); extern char *alloc(); /* User-defined heap allocator */ unsigned char *simpleprefix();/* Match simple prefix */ char *strnupr(); /* See QGREPSUB.ASM */ unsigned char *simpleprefix(s,pp) register unsigned char *s; /* String pointer */ unsigned char **pp; /* Pointer to pattern pointer */ { register unsigned char *p; /* Simple pattern pointer */ register int c; /* Single character */ p = *pp; /* Initialize */ while(*p != T_END && *p != T_STAR) /* While not at end of pattern */ { switch(*p++) /* Switch on token type */ { case T_STRING: /* String to compare */ if((*ncmp)(s,p + 1,*p) != 0) return(NULL); /* Fail if mismatch found */ s += *p; /* Skip matched portion */ p += *p + 1; /* Skip to next token */ break; case T_SINGLE: /* Single character */ c = *s++; /* Get character */ if(!casesen) c = toupper(c); /* Map to upper case if necessary */ if(c != (int)*p++) return(NULL); /* Fail if mismatch found */ break; case T_CLASS: /* Class of characters */ if(!isascii(*s) || !(p[*s >> 3] & (1 << (*s & 7)))) return(NULL); /* Failure if bit not set */ p += ASCLEN/8; /* Skip bit vector */ ++s; /* Skip character */ break; case T_ANY: /* Any character */ if(*s++ == EOS) return(NULL); /* Match all but end of string */ break; } } *pp = p; /* Update pointer */ return(s); /* Pattern is prefix of s */ } int match(s,p) register unsigned char *s; /* String to match */ unsigned char *p; /* Pattern to match against */ { register unsigned char *q; /* Temporary pointer */ unsigned char *r; /* Temporary pointer */ register int c; /* Character */ if(*p != T_END && *p != T_STAR && (s = simpleprefix(s,&p)) == NULL) return(0); /* Failure if prefix mismatch */ if(*p++ == T_END) return(1); /* Match if end of pattern */ q = r = p; /* Point to repeated token */ r += toklen[*q]; /* Skip repeated token */ switch(*q++) /* Switch on token type */ { case T_ANY: /* Any character */ while(match(s,r) == 0) /* While match not found */ { if(*s++ == EOS) return(0);/* Match all but end of string */ } return(1); /* Success */ case T_SINGLE: /* Single character */ while(match(s,r) == 0) /* While match not found */ { c = *s++; /* Get character */ if(!casesen) c = toupper(c); /* Map to upper case if necessary */ if((unsigned char) c != *q) return(0); /* Fail if mismatch found */ } return(1); /* Success */ case T_CLASS: /* Class of characters */ while(match(s,r) == 0) /* While match not found */ { if(!isascii(*s) || !(q[*s >> 3] & (1 << (*s & 7)))) return(0); /* Fail if bit not set */ ++s; /* Else skip character */ } return(1); /* Success */ } return(0); /* Return failure */ } int exprmatch(s,p) char *s; /* String */ char *p; /* Pattern */ { ncmp = strncmp; /* Assume case-sensitive */ if(!casesen) { ncmp = _strnicmp; } /* Be case-insensitive if flag set */ return(match(s,p)); /* See if pattern matches string */ } void bitset(bitvec,first,last,bitval) char *bitvec; /* Bit vector */ int first; /* First character */ int last; /* Last character */ int bitval; /* Bit value (0 or 1) */ { int bitno; /* Bit number */ bitvec += first >> 3; /* Point at first byte */ bitno = first & 7; /* Calculate first bit number */ while(first <= last) /* Loop to set bits */ { if(bitno == 0 && first + 8 <= last) { /* If we have a whole byte's worth */ *bitvec++ = (char)(bitval? '\xFF': '\0'); /* Set the bits */ first += 8; /* Increment the counter */ continue; /* Next iteration */ } *bitvec=(char)(*bitvec & (unsigned char)(~(1 << bitno))) | (unsigned char)(bitval << bitno); /* Set the appropriate bit */ if(++bitno == 8) /* If we wrap into next byte */ { ++bitvec; /* Increment pointer */ bitno = 0; /* Reset bit index */ } ++first; /* Increment bit index */ } } unsigned char *exprparse(p) register unsigned char *p; /* Raw pattern */ { register char *cp; /* Char pointer */ unsigned char *cp2; /* Char pointer */ int i; /* Counter/index */ int j; /* Counter/index */ int m; /* Counter/index */ int n; /* Counter/index */ int bitval; /* Bit value */ char buffer[PATMAX]; /* Temporary buffer */ if(!casesen) strnupr(p,strlen(p)); /* Force pattern to upper case */ cp = buffer; /* Initialize pointer */ if(*p == '^') *cp++ = *p++; /* Copy leading caret if any */ while(*p != '\0') /* While not end of pattern */ { i = -2; /* Initialize */ for(n = 0;;) /* Loop to delimit ordinary string */ { n += strcspn(p + n,".\\[*");/* Look for a special character */ if(p[n] != '\\') break; /* Break if not backslash */ i = n; /* Remember where backslash is */ if(p[++n] == '\0') return(NULL); /* Cannot be at very end */ ++n; /* Skip escaped character */ } if(p[n] == '*') /* If we found a *-expr. */ { if(n-- == 0) return(NULL); /* Illegal first character */ if(i == n - 1) n = i; /* Escaped single-char. *-expr. */ } if(n > 0) /* If we have string or single */ { if(n == 1 || (n == 2 && *p == '\\')) { /* If single character */ *cp++ = T_SINGLE; /* Set type */ if(*p == '\\') ++p; /* Skip escape if any */ *cp++ = *p++; /* Copy single character */ } else /* Else we have a string */ { *cp++ = T_STRING; /* Set type */ cp2 = cp++; /* Save pointer to length byte */ while(n-- > 0) /* While bytes to copy remain */ { if(*p == '\\') /* If escape found */ { ++p; /* Skip escape */ --n; /* Adjust length */ } *cp++ = *p++; /* Copy character */ } *cp2 = (char)((cp - cp2) - 1); /* Set string length */ } } if(*p == '\0') break; /* Break if end of pattern */ if(*p == '.') /* If matching any */ { if(*++p == '*') /* If star follows any */ { ++p; /* Skip star, too */ *cp++ = T_STAR; /* Insert prefix ahead of token */ } *cp++ = T_ANY; /* Match any character */ continue; /* Next iteration */ } if(*p == '[') /* If character class */ { if(*++p == '\0') return(NULL); /* Skip '[' */ *cp++ = T_CLASS; /* Set type */ memset(cp,'\0',ASCLEN/8); /* Clear the vector */ bitval = 1; /* Assume we're setting bits */ if(*p == '^') /* If inverted class */ { ++p; /* Skip '^' */ memset(cp,'\xFF',ASCLEN/8); /* Set all bits */ bitset(cp,EOS,EOS,0); /* All except end-of-string */ bitset(cp,'\n','\n',0); /* And linefeed! */ bitval = 0; /* Now we're clearing bits */ } while(*p != ']') /* Loop to find ']' */ { if(*p == '\0') return(NULL); /* Check for malformed string */ if(*p == '\\') /* If escape found */ { if(*++p == '\0') return(NULL); /* Skip escape */ } i = *p++; /* Get first character in range */ if(*p == '-' && p[1] != '\0' && p[1] != ']') { /* If range found */ ++p; /* Skip hyphen */ if(*p == '\\' && p[1] != '\0') ++p; /* Skip escape character */ j = *p++; /* Get end of range */ } else j = i; /* Else just one character */ bitset(cp,i,j,bitval); /* Set bits in vector */ if(!casesen) /* If ignoring case */ { m = (i < 'A')? 'A': i; /* m = max(i,'A') */ n = (j > 'Z')? 'Z': j; /* n = min(j,'Z') */ if(m <= n) bitset(cp,tolower(m),tolower(n),bitval); /* Whack corresponding lower case */ m = (i < 'a')? 'a': i; /* m = max(i,'a') */ n = (j > 'z')? 'z': j; /* n = min(j,'z') */ if(m <= n) bitset(cp,toupper(m),toupper(n),bitval); /* Whack corresponding upper case */ } } if(*++p == '*') /* If repeated class */ { memmove(cp,cp - 1,ASCLEN/8 + 1); /* Move vector forward 1 byte */ cp[-1] = T_STAR; /* Insert prefix */ ++cp; /* Skip to start of vector */ ++p; /* Skip star */ } cp += ASCLEN/8; /* Skip over vector */ continue; /* Next iteration */ } *cp++ = T_STAR; /* Repeated single character */ *cp++ = T_SINGLE; if(*p == '\\') ++p; /* Skip escape if any */ *cp++ = *p++; /* Copy the character */ assert(*p == '*'); /* Validate assumption */ ++p; /* Skip the star */ } *cp++ = T_END; /* Mark end of parsed expression */ cp2 = alloc(cp - buffer); /* Allocate buffer */ memmove(cp2,buffer,(size_t)(cp - buffer)); /* Copy expression to buffer */ return(cp2); /* Return buffer pointer */ } int istoken(s,n) unsigned char *s; /* String */ int n; /* Length */ { if(n >= 2 && s[0] == '\\' && s[1] == '<') return(1); /* Token if starts with '\<' */ while(n-- > 0) /* Loop to find end of string */ { if(*s++ == '\\') /* If escape found */ { if(--n == 0 && *s == '>') return(1); /* Token if ends with '\>' */ ++s; /* Skip escaped character */ } } return(0); /* Not a token */ } int isexpr(s,n) unsigned char *s; /* String */ int n; /* Length */ { unsigned char *cp; /* Char pointer */ int status; /* Return status */ char buffer[BUFLEN]; /* Temporary buffer */ if(istoken(s,n)) return(1); /* Tokens are exprs */ memmove(buffer,s,n); /* Copy string to buffer */ buffer[n] = '\0'; /* Null-terminate string */ if((s = exprparse(buffer)) == NULL) return(0); /* Not an expression if parse fails */ status = 1; /* Assume we have an expression */ if(*s != '^' && *s != T_END) /* If no caret and not empty */ { status = 0; /* Assume not an expression */ cp = s; /* Initialize */ do /* Loop to find special tokens */ { switch(*cp++) /* Switch on token type */ { case T_STAR: /* Repeat prefix */ case T_CLASS: /* Character class */ case T_ANY: /* Any character */ ++status; /* This is an expression */ break; case T_SINGLE: /* Single character */ ++cp; /* Skip character */ break; case T_STRING: /* String */ cp += *cp + 1; /* Skip string */ break; } } while(!status && *cp != T_END); /* Do while not at end of expression */ } free(s); /* Free expression */ return(status); /* Return status */ } void exprprint(p,fo) unsigned char *p; /* Pointer to expression */ FILE *fo; /* File pointer */ { int bit; /* Bit value */ int count; /* Count of characters in string */ int first; /* First character in range */ int last; /* Last character in range */ int star; /* Repeat prefix flag */ if(*p == '^') fputc(*p++,fo); /* Print leading caret */ while(*p != T_END) /* While not at end of expression */ { star = 0; /* Assume no prefix */ if(*p == T_STAR) /* If repeat prefix found */ { ++star; /* Set flag */ ++p; /* Skip prefix */ } switch(*p++) /* Switch on token type */ { case T_END: /* End of expression */ case T_STAR: /* Repeat prefix */ fprintf(stderr,"Internal error: exprprint\n"); /* Not valid */ exit(2); /* Die abnormal death */ case T_STRING: /* String */ count = *p++; /* Get string length */ goto common; /* Forgive me, Djikstra! */ case T_SINGLE: /* Single character */ count = 1; /* Only one character */ common: while(count-- > 0) /* While bytes remain */ { if(*p == EOS) /* If end-of-string found */ { ++p; /* Skip character */ fputc('$',fo); /* Emit special marker */ continue; /* Next iteration */ } if(strchr("*.[\\$",*p) != NULL) fputc('\\',fo); /* Emit escape if needed */ fputc(*p++,fo); /* Emit the character */ } break; case T_ANY: /* Match any */ fputc('.',fo); /* Emit dot */ break; case T_CLASS: first = -1; /* Initialize */ fputc('[',fo); /* Open braces */ for(count = ' '; count <= '~'; ++count) { /* Loop through printable characters */ if((bit = p[count >> 3] & (1 << (count & 7))) != 0) { /* If bit is set */ if(first == -1) first = count; /* Set first bit */ last = count; /* Set last bit */ } if((!bit || count == '~') && first != -1) { /* If range to print */ if(strchr("\\]-",first) != NULL) fputc('\\',fo); /* Emit escape if needed */ fputc(first,fo); /* Print first character in range */ if(last != first) /* If we have a range */ { if(last > first + 1) fputc('-',fo); /* Emit hyphen if needed */ if(strchr("\\]-",last) != NULL) fputc('\\',fo); /* Emit escape if needed */ fputc(last,fo); /* Print last character in range */ } first = -1; /* Range printed */ } } fputc(']',fo); /* Close braces */ p += ASCLEN/8; /* Skip bit vector */ break; } if(star) fputc('*',fo); /* Print star if needed */ } fputc('\n',fo); /* Print newline */ } char *get1stcharset(e,bitvec) unsigned char *e; /* Pointer to expression */ char *bitvec; /* Pointer to bit vector */ { unsigned char *cp; /* Char pointer */ int i; /* Index/counter */ int star; /* Repeat prefix flag */ if(*e == '^') ++e; /* Skip leading caret if any */ memset(bitvec,'\0',ASCLEN/8); /* Clear bit vector */ cp = e; /* Initialize */ while(*e != T_END) /* Loop to process leading *-expr.s */ { star = 0; /* Assume no repeat prefix */ if(*e == T_STAR) /* If repeat prefix found */ { ++star; /* Set flag */ ++e; /* Skip repeat prefix */ } switch(*e++) /* Switch on token type */ { case T_END: /* End of expression */ case T_STAR: /* Repeat prefix */ fprintf(stderr,"Internal error: get1stcharset\n"); /* Not valid */ exit(2); /* Die abnormal death */ case T_STRING: /* String */ if(star || *e++ == '\0') /* If repeat prefix or zero count */ { fprintf(stderr,"Internal error: get1stcharset\n"); /* Not valid */ exit(2); /* Die abnormal death */ } /* Drop through */ case T_SINGLE: /* Single character */ bitset(bitvec,*e,*e,1); /* Set the bit */ ++e; /* Skip the character */ break; case T_ANY: /* Match any */ memset(bitvec,'\xFF',ASCLEN/8); /* Set all the bits */ bitset(bitvec,EOS,EOS,0); /* Except end-of-string */ bitset(bitvec,'\n','\n',0); /* And linefeed! */ break; case T_CLASS: for(i = 0; i < ASCLEN/8; ++i) bitvec[i] |= *e++; /* Or in all the bits */ break; } if(!star) break; /* Break if not repeated */ cp = e; /* Update pointer */ } return(cp); /* Point to 1st non-repeated expr. */ } char *findall(buffer,bufend) char *buffer; /* Buffer in which to search */ char *bufend; /* End of buffer */ { return(buffer < bufend? buffer: NULL); /* Fail only on empty buffer */ } void addtoken(e,n) char *e; /* Raw token expression */ int n; /* Length of expression */ { static char achpref[] = "^";/* Prefix */ static char achprefsuf[] = "[^A-Za-z0-9_]"; /* Prefix/suffix */ static char achsuf[] = "$"; /* Suffix */ char buffer[BUFLEN]; /* Temporary buffer */ assert(n >= 2); /* Must have at least two characters */ if(e[0] == '\\' && e[1] == '<') /* If begin token */ { if(!(flags & BEGLINE)) /* If not matching at beginning only */ { memcpy(buffer,achprefsuf,sizeof achprefsuf - 1); /* Copy first prefix */ memcpy(buffer + sizeof achprefsuf - 1,e + 2,n - 2); /* Attach expression */ addexpr(buffer,n + sizeof achprefsuf - 3); /* Add expression */ } memcpy(buffer,achpref,sizeof achpref - 1); /* Copy second prefix */ memcpy(buffer + sizeof achpref - 1,e + 2,n - 2); /* Attach expression */ addexpr(buffer,n + sizeof achpref - 3); /* Add expression */ return; /* Done */ } assert(e[n-2] == '\\' && e[n - 1] == '>'); /* Must be end token */ if(!(flags & ENDLINE)) /* If not matching at end only */ { memcpy(buffer,e,n - 2); /* Copy expression */ memcpy(buffer + n - 2,achprefsuf,sizeof achprefsuf - 1); /* Attach first suffix */ addexpr(buffer,n + sizeof achprefsuf - 3); /* Add expression */ } memcpy(buffer,e,n - 2); /* Copy expression */ memcpy(buffer + n - 2,achsuf,sizeof achsuf - 1); /* Attach second suffix */ addexpr(buffer,n + sizeof achsuf - 3); /* Add expression */ } void addexpr(e,n) char *e; /* Expression to add */ int n; /* Length of expression */ { EXPR *expr; /* Expression node pointer */ int i; /* Index */ int j; /* Index */ int locflags; /* Local copy of flags */ char bitvec[ASCLEN/8]; /* First char. bit vector */ char buffer[BUFLEN]; /* Temporary buffer */ if(find == findall) return; /* Return if matching everything */ if(istoken(e,n)) /* If expr is token */ { addtoken(e,n); /* Convert and add tokens */ return; /* Done */ } locflags = flags; /* Initialize local copy */ if(*e == '^') locflags |= BEGLINE; /* Set flag if match must begin line */ j = -2; /* Assume no escapes in string */ for(i = 0; i < n - 1; ++i) /* Loop to find last escape */ { if(e[i] == '\\') j = i++; /* Save index of last escape */ } if(n > 0 && e[n-1] == '$' && j != n-2) { /* If expr. ends in unescaped '$' */ --n; /* Skip dollar sign */ locflags |= ENDLINE; /* Match must be at end */ } strncpy(buffer,e,n); /* Copy pattern to buffer */ if(locflags & ENDLINE) buffer[n++] = EOS; /* Add end character if needed */ buffer[n] = '\0'; /* Null-terminate string */ if((e = exprparse(buffer)) == NULL) return; /* Return if invalid expression */ ++strcnt; /* Increment string count */ if(!(locflags & BEGLINE)) /* If match needn't be at beginning */ { e = get1stcharset(e,bitvec); /* Remove leading *-expr.s */ } /* * E now points to a buffer containing a preprocessed expression. * We need to find the set of allowable first characters and make * the appropriate entries in the string node table. */ if(*get1stcharset(e,bitvec) == T_END) { /* If expression will match anything */ find = findall; /* Match everything */ return; /* All done */ } for(j = 0; j < ASCLEN; ++j) /* Loop to examine bit vector */ { if(bitvec[j >> 3] & (1 << (j & 7))) { /* If the bit is set */ expr = (EXPR *) alloc(sizeof(EXPR)); /* Allocate record */ expr->ex_pattern = e; /* Point it at pattern */ if((i = transtab[j]) == 0) /* If no existing list */ { if((i = clists++) >= ASCLEN) { /* If too many string lists */ fprintf(stderr,"Too many string lists\n"); /* Error message */ exit(2); /* Die */ } stringlist[i] = NULL; /* Initialize */ transtab[j] = (char) i; /* Set pointer to new list */ if(!casesen && isalpha(j)) transtab[j ^ 0x20] = (char) i; /* Set pointer for other case */ } expr->ex_next = stringlist[i]; /* Link new record into table */ stringlist[i] = expr; } } if(locflags & DEBUG) exprprint(e,stderr); /* Print the expression if debugging */ } char *findexpr(buffer,bufend) unsigned char *buffer; /* Buffer in which to search */ char *bufend; /* End of buffer */ { EXPR *expr; /* Expression list pointer */ unsigned char *pattern; /* Pattern */ int i; /* Index */ while(buffer < bufend) /* Loop to find match */ { if((i = transtab[*buffer++]) == 0) continue; /* Continue if not valid 1st char */ if((expr = (EXPR *) stringlist[i]) == NULL) { /* If null pointer */ fprintf(stderr,"Internal error: findexpr\n"); /* Print error message */ exit(2); /* Die */ } --buffer; /* Back up to first character */ while(expr != NULL) /* Loop to find match */ { pattern = expr->ex_pattern; /* Point to pattern */ expr = expr->ex_next; /* Point to next record */ if(pattern[0] == '^') /* If match begin line */ { ++pattern; /* Skip caret */ if(buffer[-1] != '\n') continue; /* Don't bother if not at beginning */ } if(exprmatch(buffer,pattern)) return(buffer); /* Return pointer if match found */ } ++buffer; /* Skip first character */ } return(NULL); /* No match */ }