/* wild.c - wildcard routines * * Written by T. Alexander Popiel, 24 June 1993 * * Last modified by T. Alexander Popiel, 15 October 1993 * * * * Thanks go to Andrew Molitor for debugging * * Thanks also go to Rich $alz for code to benchmark against * * * * Copyright (c) 1993 by T. Alexander Popiel * * This code is hereby placed under GNU copyleft, * * see copyrite.h for details. */ #include "config.h" #include <ctype.h> #ifdef I_STRING #include <string.h> #else #include <strings.h> #endif #ifdef I_STDLIB #include <stdlib.h> #endif #include "copyrite.h" #include "conf.h" #include "intrface.h" #include "globals.h" #include "externs.h" #include "mymalloc.h" #include "regexp.h" #include "confmagic.h" #define FIXCASE(a) (DOWNCASE(a)) #define EQUAL(a,b) ((a == b) || (FIXCASE(a) == FIXCASE(b))) #define NOTEQUAL(a,b) ((a != b) && (FIXCASE(a) != FIXCASE(b))) #define NUMARGS (10) static char wspace[3 * BUFFER_LEN + NUMARGS]; /* argument return buffer */ /* big to match tprintf */ int quick_wild _((const char *tstr, const char *dstr)); static int wild1 _((const char *tstr, const char *dstr, int arg, char *wbuf)); int wild _((const char *s, const char *d, int p, int os)); int wild_match _((const char *s, const char *d)); int regexp_match _((const char *s, const char *d)); int local_wild _((char *s, char *d, int p, int os)); int local_wild_match _((const char *s, const char *d)); int wildcard _((const char *)); /* --------------------------------------------------------------------------- * quick_wild: do a wildcard match, without remembering the wild data. * * This routine will cause crashes if fed NULLs instead of strings. */ int quick_wild(tstr, dstr) const char *tstr; const char *dstr; { while (*tstr != '*') { switch (*tstr) { case '?': /* Single character match. Return false if at * end of data. */ if (!*dstr) return 0; break; case '\\': /* Escape character. Move up, and force literal * match of next character. */ tstr++; /* FALL THROUGH */ default: /* Literal character. Check for a match. * If matching end of data, return true. */ if (NOTEQUAL(*dstr, *tstr)) return 0; if (!*dstr) return 1; } tstr++; dstr++; } /* Skip over '*'. */ tstr++; /* Return true on trailing '*'. */ if (!*tstr) return 1; /* Skip over wildcards. */ while ((*tstr == '?') || (*tstr == '*')) { if (*tstr == '?') { if (!*dstr) return 0; dstr++; } tstr++; } /* Skip over a backslash in the pattern string if it is there. */ if (*tstr == '\\') tstr++; /* Return true on trailing '*'. */ if (!*tstr) return 1; /* Scan for possible matches. */ while (*dstr) { if (EQUAL(*dstr, *tstr) && quick_wild(tstr + 1, dstr + 1)) return 1; dstr++; } return 0; } /* --------------------------------------------------------------------------- * wild1: INTERNAL: do a wildcard match, remembering the wild data. * * DO NOT CALL THIS FUNCTION DIRECTLY - DOING SO MAY RESULT IN * SERVER CRASHES AND IMPROPER ARGUMENT RETURN. * * Side Effect: this routine modifies the 'wnxt' global variable, * and what it points to. */ static int wild1(tstr, dstr, arg, wbuf) const char *tstr; const char *dstr; int arg; char *wbuf; { const char *datapos; char *wnext; int argpos, numextra; while (*tstr != '*') { switch (*tstr) { case '?': /* Single character match. Return false if at * end of data. */ if (!*dstr) return 0; wnxt[arg++] = wbuf; *wbuf++ = *dstr; *wbuf++ = '\0'; /* Jump to the fast routine if we can. */ if (arg >= NUMARGS) return quick_wild(tstr + 1, dstr + 1); break; case '\\': /* Escape character. Move up, and force literal * match of next character. */ tstr++; /* FALL THROUGH */ default: /* Literal character. Check for a match. * If matching end of data, return true. */ if (NOTEQUAL(*dstr, *tstr)) return 0; if (!*dstr) return 1; } tstr++; dstr++; } /* If at end of pattern, slurp the rest, and leave. */ if (!tstr[1]) { wnxt[arg] = wbuf; strcpy(wbuf, dstr); return 1; } /* Remember current position for filling in the '*' return. */ datapos = dstr; argpos = arg; /* Scan forward until we find a non-wildcard. */ do { if (argpos < arg) { /* Fill in arguments if someone put another '*' * before a fixed string. */ wnxt[argpos++] = wbuf; *wbuf++ = '\0'; /* Jump to the fast routine if we can. */ if (argpos >= NUMARGS) return quick_wild(tstr, dstr); /* Fill in any intervening '?'s */ while (argpos < arg) { wnxt[argpos++] = wbuf; *wbuf++ = *datapos++; *wbuf++ = '\0'; /* Jump to the fast routine if we can. */ if (argpos >= NUMARGS) return quick_wild(tstr, dstr); } } /* Skip over the '*' for now... */ tstr++; arg++; /* Skip over '?'s for now... */ numextra = 0; while (*tstr == '?') { if (!*dstr) return 0; tstr++; dstr++; arg++; numextra++; } } while (*tstr == '*'); /* Skip over a backslash in the pattern string if it is there. */ if (*tstr == '\\') tstr++; /* Check for possible matches. This loop terminates either at * end of data (resulting in failure), or at a successful match. */ if (!*tstr) while (*dstr) dstr++; else { wnext = wbuf; wnext++; while (1) { if (EQUAL(*dstr, *tstr) && ((arg < NUMARGS) ? wild1(tstr, dstr, arg, wnext) : quick_wild(tstr, dstr))) break; if (!*dstr) return 0; dstr++; wnext++; } } /* Found a match! Fill in all remaining arguments. * First do the '*'... */ wnxt[argpos++] = wbuf; strncpy(wbuf, datapos, (dstr - datapos) - numextra); wbuf += (dstr - datapos) - numextra; *wbuf++ = '\0'; datapos = dstr - numextra; /* Fill in any trailing '?'s that are left. */ while (numextra) { if (argpos >= NUMARGS) return 1; wnxt[argpos++] = wbuf; *wbuf++ = *datapos++; *wbuf++ = '\0'; numextra--; } /* It's done! */ return 1; } /* --------------------------------------------------------------------------- * wild: do a wildcard match, remembering the wild data. * * This routine will cause crashes if fed NULLs instead of strings. * * This function may crash if malloc() fails. * * Side Effect: this routine modifies the 'wnxt' global variable. */ int wild(s, d, p, os) const char *s; const char *d; int p; int os; { /* Do fast match. */ while ((*s != '*') && (*s != '?')) { if (*s == '\\') s++; if (NOTEQUAL(*d, *s)) return 0; if (!*d) return 1; s++; d++; } /* Do the match. */ return wild1(s, d, p, wspace); } /* --------------------------------------------------------------------------- * wild_match: do a wildcard * match, remembering the wild data, if wildcard match is done. * * This routine will cause crashes if fed NULLs instead of strings. */ int wild_match(s, d) const char *s; const char *d; { int j; /* Clear %0-%9 and r(0) - r(9) */ for (j = 0; j < NUMARGS; j++) wnxt[j] = (char *) NULL; for (j = 0; j < 10; j++) rnxt[j] = (char *) NULL; return wild(s, d, 0, 0); } /* --------------------------------------------------------------------------- * regexp_match: do a regexp match, remembering the matched subexpressions * * Based on TinyMUSH 2.2.4 */ int regexp_match(s, d) const char *s; const char *d; { int j; regexp *re; int i, len; static char wtmp[10][BUFFER_LEN]; if ((re = regcomp((char *) s)) == NULL) { /* * This is a matching error. We have an error message in * regexp_errbuf that we can ignore, since we're doing * command-matching. */ return 0; } /* * Now we try to match the pattern. The relevant fields will * automatically be filled in by this. */ if (!regexec(re, (char *) d)) { mush_free(re, "regexp"); return 0; } /* * Now we fill in our args vector. Note that in regexp matching, * 0 is the entire string matched, and the parenthesized strings * go from 1 to 9. We DO PRESERVE THIS PARADIGM, for consistency * with other languages. */ /* Clear %0-%9 and r(0) - r(9) */ for (j = 0; j < 10; j++) { wtmp[j][0] = '\0'; wnxt[j] = (char *) NULL; rnxt[j] = (char *) NULL; } for (i = 0; (i < NSUBEXP) && (re->startp[i]) && (re->endp[i]); i++) { len = re->endp[i] - re->startp[i]; if (len > BUFFER_LEN - 1) len = BUFFER_LEN - 1; strncpy(wtmp[i], re->startp[i], len); wtmp[i][len] = '\0'; /* strncpy() does not null-terminate */ wnxt[i] = wtmp[i]; } mush_free(re, "regexp"); return 1; } /* --------------------------------------------------------------------------- * local_wild: do a wildcard match, without remembering the wild data. * * This routine will cause crashes if fed NULLs instead of strings. */ int local_wild(s, d, p, os) char *s; char *d; int p; int os; { return quick_wild(s, d); } /* --------------------------------------------------------------------------- * local_wild_match: do either an order comparison or a wildcard match. * Ignore match correspondences. * * This routine will cause crashes if fed NULLs instead of strings. */ int local_wild_match(s, d) const char *s; const char *d; { switch (*s) { case '>': s++; if ((isascii(*s) && isdigit(*s)) || (*s == '-')) return (atoi(s) < atoi(d)); else return (strcmp(s, d) < 0); case '<': s++; if ((isascii(*s) && isdigit(*s)) || (*s == '-')) return (atoi(s) > atoi(d)); else return (strcmp(s, d) > 0); } return quick_wild(s, d); } /* --------------------------------------------------------------------------- * wildcard - return 1 if the string has a wildcard character (* or ?) * in it, and 0 otherwise. Not used by the wild matching routines, but * suitable for outside use. */ int wildcard(s) const char *s; { if (strchr(s, '*') || strchr(s, '?')) return 1; return 0; }