From: Todd C. Miller Date: Fri, 26 Sep 2008 21:13:29 +0000 (+0000) Subject: Add character class support to included glob(3) and fnmatch(3). X-Git-Tag: SUDO_1_7_0~84 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0372d7dc2bda6e61b5018c5a25c89698a9175433;p=sudo Add character class support to included glob(3) and fnmatch(3). --- diff --git a/emul/charclass.h b/emul/charclass.h new file mode 100644 index 000000000..13fc18fa2 --- /dev/null +++ b/emul/charclass.h @@ -0,0 +1,29 @@ +/* + * Public domain, 2008, Todd C. Miller + * + * $Sudo$ + */ + +/* + * POSIX character class support for fnmatch() and glob(). + */ +static struct cclass { + const char *name; + int (*isctype)(int); +} cclasses[] = { + { "alnum", isalnum }, + { "alpha", isalpha }, + { "blank", isblank }, + { "cntrl", iscntrl }, + { "digit", isdigit }, + { "graph", isgraph }, + { "lower", islower }, + { "print", isprint }, + { "punct", ispunct }, + { "space", isspace }, + { "upper", isupper }, + { "xdigit", isxdigit }, + { NULL, NULL } +}; + +#define NCCLASSES (sizeof(cclasses) / sizeof(cclasses[0]) - 1) diff --git a/fnmatch.c b/fnmatch.c index 148435808..31c895212 100644 --- a/fnmatch.c +++ b/fnmatch.c @@ -49,6 +49,7 @@ #include #include "emul/fnmatch.h" +#include "emul/charclass.h" #undef EOS #define EOS '\0' @@ -61,7 +62,8 @@ __unused static const char rcsid[] = "$OpenBSD: fnmatch.c,v 1.6 1998/03/19 00:29:59 millert Exp $"; #endif /* LIBC_SCCS and not lint */ -static int rangematch __P((const char *, char, int, char **)); +static int rangematch __P((const char *, int, int, char **)); +static int classmatch __P((const char *, int, int, const char **)); int fnmatch(pattern, string, flags) @@ -167,16 +169,16 @@ fnmatch(pattern, string, flags) static int #ifdef __STDC__ -rangematch(const char *pattern, char test, int flags, char **newp) +rangematch(const char *pattern, int test, int flags, char **newp) #else rangematch(pattern, test, flags, newp) const char *pattern; - char test; + int test; int flags; char **newp; #endif { - int negate, ok; + int negate, ok, rv; char c, c2; /* @@ -190,7 +192,7 @@ rangematch(pattern, test, flags, newp) ++pattern; if (ISSET(flags, FNM_CASEFOLD)) - test = tolower((unsigned char)test); + test = tolower(test); /* * A right bracket shall lose its special meaning and represent @@ -200,6 +202,17 @@ rangematch(pattern, test, flags, newp) ok = 0; c = *pattern++; do { + if (c == '[' && *pattern == ':') { + do { + rv = classmatch(pattern + 1, test, + (flags & FNM_CASEFOLD), &pattern); + if (rv == RANGE_MATCH) + ok = 1; + c = *pattern++; + } while (rv != RANGE_ERROR && c == '[' && *pattern == ':'); + if (c == ']') + break; + } if (c == '\\' && !ISSET(flags, FNM_NOESCAPE)) c = *pattern++; if (c == EOS) @@ -226,3 +239,43 @@ rangematch(pattern, test, flags, newp) *newp = (char *)pattern; return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH); } + +static int +#ifdef __STDC__ +classmatch(const char *pattern, int test, int foldcase, const char **ep) +#else +classmatch(pattern, test, foldcase, ep) + const char *pattern; + int test; + int foldcase; + const char **ep; +#endif +{ + struct cclass *cc; + const char *colon; + size_t len; + int rval = RANGE_NOMATCH; + + if ((colon = strchr(pattern, ':')) == NULL || colon[1] != ']') { + *ep = pattern - 2; + return(RANGE_ERROR); + } + *ep = colon + 2; + len = (size_t)(colon - pattern); + + if (foldcase && strncmp(pattern, "upper:]", 7) == 0) + pattern = "lower:]"; + for (cc = cclasses; cc->name != NULL; cc++) { + if (!strncmp(pattern, cc->name, len) && cc->name[len] == '\0') { + if (cc->isctype(test)) + rval = RANGE_MATCH; + break; + } + } + if (cc->name == NULL) { + /* invalid character class, return EOS */ + *ep = colon + strlen(colon); + rval = RANGE_ERROR; + } + return(rval); +} diff --git a/glob.c b/glob.c index 6d4f1a253..ef6f67b9d 100644 --- a/glob.c +++ b/glob.c @@ -99,6 +99,7 @@ #include #include "emul/glob.h" +#include "emul/charclass.h" #define DOLLAR '$' #define DOT '.' @@ -147,6 +148,7 @@ typedef char Char; #define M_ONE META('?') #define M_RNG META('-') #define M_SET META('[') +#define M_CLASS META(':') #define ismeta(c) (((c)&M_QUOTE) != 0) @@ -154,7 +156,8 @@ static int compare __P((const void *, const void *)); static int g_Ctoc __P((const Char *, char *, unsigned int)); static int g_lstat __P((Char *, struct stat *, glob_t *)); static DIR *g_opendir __P((Char *, glob_t *)); -static Char *g_strchr __P((Char *, int)); +static Char *g_strchr __P((const Char *, int)); +static int g_strncmp __P((const Char *, const char *, size_t)); static int g_stat __P((Char *, struct stat *, glob_t *)); static int glob0 __P((const Char *, glob_t *)); static int glob1 __P((Char *, Char *, glob_t *)); @@ -238,7 +241,7 @@ globexp1(pattern, pglob) if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS) return glob0(pattern, pglob); - while ((ptr = (const Char *) g_strchr((Char *) ptr, LBRACE)) != NULL) + while ((ptr = (const Char *) g_strchr(ptr, LBRACE)) != NULL) if (!globexp2(ptr, pattern, pglob, &rv)) return rv; @@ -415,6 +418,47 @@ globtilde(pattern, patbuf, patbuf_len, pglob) return patbuf; } +static int +g_strncmp(const Char *s1, const char *s2, size_t n) +{ + int rv = 0; + + while (n--) { + rv = *(Char *)s1 - *(const unsigned char *)s2++; + if (rv) + break; + if (*s1++ == '\0') + break; + } + return rv; +} + +static int +g_charclass(const Char **patternp, Char **bufnextp) +{ + const Char *pattern = *patternp + 1; + Char *bufnext = *bufnextp; + const Char *colon; + struct cclass *cc; + size_t len; + + if ((colon = g_strchr(pattern, ':')) == NULL || colon[1] != ']') + return 1; /* not a character class */ + + len = (size_t)(colon - pattern); + for (cc = cclasses; cc->name != NULL; cc++) { + if (!g_strncmp(pattern, cc->name, len) && cc->name[len] == '\0') + break; + } + if (cc->name == NULL) + return -1; /* invalid character class */ + *bufnext++ = M_CLASS; + *bufnext++ = (Char)(cc - &cclasses[0]); + *bufnextp = bufnext; + *patternp += len + 3; + + return 0; +} /* * The main glob() routine: compiles the pattern (optionally processing @@ -444,7 +488,7 @@ glob0(pattern, pglob) if (c == NOT) ++qpatnext; if (*qpatnext == EOS || - g_strchr((Char *) qpatnext+1, RBRACKET) == NULL) { + g_strchr(qpatnext+1, RBRACKET) == NULL) { *bufnext++ = LBRACKET; if (c == NOT) --qpatnext; @@ -455,6 +499,20 @@ glob0(pattern, pglob) *bufnext++ = M_NOT; c = *qpatnext++; do { + if (c == LBRACKET && *qpatnext == ':') { + do { + err = g_charclass(&qpatnext, + &bufnext); + if (err) + break; + c = *qpatnext++; + } while (c == LBRACKET && *qpatnext == ':'); + if (err == -1 && + !(pglob->gl_flags & GLOB_NOCHECK)) + return GLOB_NOMATCH; + if (c == RBRACKET) + break; + } *bufnext++ = CHAR(c); if (*qpatnext == RANGE && (c = qpatnext[1]) != RBRACKET) { @@ -753,6 +811,13 @@ match(name, pat, patend) if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS) ++pat; while (((c = *pat++) & M_MASK) != M_END) + if ((c & M_MASK) == M_CLASS) { + int idx = *pat & M_MASK; + if (idx < NCCLASSES && + cclasses[idx].isctype(k)) + ok = 1; + ++pat; + } if ((*pat & M_MASK) == M_RNG) { if (c <= k && k <= pat[1]) ok = 1; @@ -834,12 +899,12 @@ g_stat(fn, sb, pglob) static Char * g_strchr(str, ch) - Char *str; + const Char *str; int ch; { do { if (*str == ch) - return (str); + return ((Char *)str); } while (*str++); return (NULL); }