diff options
author | Damien Miller <djm@mindrot.org> | 2010-10-07 21:39:17 +1100 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2010-10-07 21:39:17 +1100 |
commit | a6e121aaa0ab61965db2dcfe8e2ba5d719fbe1e6 (patch) | |
tree | 4583d2a94482493f21537611187bd119f496807c /openbsd-compat/glob.c | |
parent | aa18063baf35e303832d9ec58204ffaab221de85 (diff) |
- djm@cvs.openbsd.org 2010/09/25 09:30:16
[sftp.c configure.ac openbsd-compat/glob.c openbsd-compat/glob.h]
make use of new glob(3) GLOB_KEEPSTAT extension to save extra server
rountrips to fetch per-file stat(2) information.
NB. update openbsd-compat/ glob(3) implementation from OpenBSD libc to
match.
Diffstat (limited to 'openbsd-compat/glob.c')
-rw-r--r-- | openbsd-compat/glob.c | 232 |
1 files changed, 170 insertions, 62 deletions
diff --git a/openbsd-compat/glob.c b/openbsd-compat/glob.c index 74b50640..7bbe6c71 100644 --- a/openbsd-compat/glob.c +++ b/openbsd-compat/glob.c @@ -1,4 +1,4 @@ -/* $OpenBSD: glob.c,v 1.26 2005/11/28 17:50:12 deraadt Exp $ */ +/* $OpenBSD: glob.c,v 1.33 2010/09/26 22:15:39 djm Exp $ */ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. @@ -33,6 +33,31 @@ /* OPENBSD ORIGINAL: lib/libc/gen/glob.c */ +/* + * glob(3) -- a superset of the one defined in POSIX 1003.2. + * + * The [!...] convention to negate a range is supported (SysV, Posix, ksh). + * + * Optional extra services, controlled by flags not defined by POSIX: + * + * GLOB_QUOTE: + * Escaping convention: \ inhibits any special meaning the following + * character might have (except \ at end of string is retained). + * GLOB_MAGCHAR: + * Set in gl_flags if pattern contained a globbing character. + * GLOB_NOMAGIC: + * Same as GLOB_NOCHECK, but it will only append pattern if it did + * not contain any magic characters. [Used in csh style globbing] + * GLOB_ALTDIRFUNC: + * Use alternately specified directory access functions. + * GLOB_TILDE: + * expand ~user/foo to the /home/dir/of/user/foo + * GLOB_BRACE: + * expand {1,2}{a,b} to 1a 1b 2a 2b + * gl_matchc: + * Number of matches in the current invocation of glob. + */ + #include "includes.h" #include <sys/types.h> @@ -47,7 +72,7 @@ #include <unistd.h> #if !defined(HAVE_GLOB) || !defined(GLOB_HAS_ALTDIRFUNC) || \ - !defined(GLOB_HAS_GL_MATCHC) || \ + !defined(GLOB_HAS_GL_MATCHC) || !defined(GLOB_HAS_GL_STATV) || \ !defined(HAVE_DECL_GLOB_NOMATCH) || HAVE_DECL_GLOB_NOMATCH == 0 || \ defined(BROKEN_GLOB) @@ -63,31 +88,7 @@ get_arg_max(void) #endif } -/* - * glob(3) -- a superset of the one defined in POSIX 1003.2. - * - * The [!...] convention to negate a range is supported (SysV, Posix, ksh). - * - * Optional extra services, controlled by flags not defined by POSIX: - * - * GLOB_QUOTE: - * Escaping convention: \ inhibits any special meaning the following - * character might have (except \ at end of string is retained). - * GLOB_MAGCHAR: - * Set in gl_flags if pattern contained a globbing character. - * GLOB_NOMAGIC: - * Same as GLOB_NOCHECK, but it will only append pattern if it did - * not contain any magic characters. [Used in csh style globbing] - * GLOB_ALTDIRFUNC: - * Use alternately specified directory access functions. - * GLOB_TILDE: - * expand ~user/foo to the /home/dir/of/user/foo - * GLOB_BRACE: - * expand {1,2}{a,b} to 1a 1b 2a 2b - * gl_matchc: - * Number of matches in the current invocation of glob. - */ - +#include "charclass.h" #define DOLLAR '$' #define DOT '.' @@ -100,7 +101,6 @@ get_arg_max(void) #define RBRACKET ']' #define SEP '/' #define STAR '*' -#undef TILDE /* Some platforms may already define it */ #define TILDE '~' #define UNDERSCORE '_' #define LBRACE '{' @@ -137,6 +137,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) @@ -144,7 +145,8 @@ static int compare(const void *, const void *); static int g_Ctoc(const Char *, char *, u_int); static int g_lstat(Char *, struct stat *, glob_t *); static DIR *g_opendir(Char *, glob_t *); -static Char *g_strchr(Char *, int); +static Char *g_strchr(const Char *, int); +static int g_strncmp(const Char *, const char *, size_t); static int g_stat(Char *, struct stat *, glob_t *); static int glob0(const Char *, glob_t *); static int glob1(Char *, Char *, glob_t *, size_t *); @@ -152,11 +154,11 @@ static int glob2(Char *, Char *, Char *, Char *, Char *, Char *, glob_t *, size_t *); static int glob3(Char *, Char *, Char *, Char *, Char *, Char *, Char *, glob_t *, size_t *); -static int globextend(const Char *, glob_t *, size_t *); +static int globextend(const Char *, glob_t *, size_t *, struct stat *); static const Char * globtilde(const Char *, Char *, size_t, glob_t *); static int globexp1(const Char *, glob_t *); -static int globexp2(const Char *, const Char *, glob_t *, int *); +static int globexp2(const Char *, const Char *, glob_t *); static int match(Char *, Char *, Char *); #ifdef DEBUG static void qprintf(const char *, Char *); @@ -174,6 +176,7 @@ glob(const char *pattern, int flags, int (*errfunc)(const char *, int), if (!(flags & GLOB_APPEND)) { pglob->gl_pathc = 0; pglob->gl_pathv = NULL; + pglob->gl_statv = NULL; if (!(flags & GLOB_DOOFFS)) pglob->gl_offs = 0; } @@ -215,15 +218,13 @@ static int globexp1(const Char *pattern, glob_t *pglob) { const Char* ptr = pattern; - int rv; /* Protect a single {}, for find(1), like csh */ if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS) return glob0(pattern, pglob); - while ((ptr = (const Char *) g_strchr((Char *) ptr, LBRACE)) != NULL) - if (!globexp2(ptr, pattern, pglob, &rv)) - return rv; + if ((ptr = (const Char *) g_strchr(ptr, LBRACE)) != NULL) + return globexp2(ptr, pattern, pglob); return glob0(pattern, pglob); } @@ -235,9 +236,9 @@ globexp1(const Char *pattern, glob_t *pglob) * If it fails then it tries to glob the rest of the pattern and returns. */ static int -globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, int *rv) +globexp2(const Char *ptr, const Char *pattern, glob_t *pglob) { - int i; + int i, rv; Char *lm, *ls; const Char *pe, *pm, *pl; Char patbuf[MAXPATHLEN]; @@ -270,10 +271,8 @@ globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, int *rv) } /* Non matching braces; just glob the pattern */ - if (i != 0 || *pe == EOS) { - *rv = glob0(patbuf, pglob); - return 0; - } + if (i != 0 || *pe == EOS) + return glob0(patbuf, pglob); for (i = 0, pl = pm = ptr; pm <= pe; pm++) { switch (*pm) { @@ -319,7 +318,9 @@ globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, int *rv) #ifdef DEBUG qprintf("globexp2:", patbuf); #endif - *rv = globexp1(patbuf, pglob); + rv = globexp1(patbuf, pglob); + if (rv && rv != GLOB_NOMATCH) + return rv; /* move after the comma, to the next string */ pl = pm + 1; @@ -330,7 +331,6 @@ globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, int *rv) break; } } - *rv = 0; return 0; } @@ -399,6 +399,47 @@ globtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t *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 @@ -427,7 +468,7 @@ glob0(const Char *pattern, glob_t *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; @@ -438,6 +479,20 @@ glob0(const Char *pattern, glob_t *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) { @@ -484,7 +539,7 @@ glob0(const Char *pattern, glob_t *pglob) if ((pglob->gl_flags & GLOB_NOCHECK) || ((pglob->gl_flags & GLOB_NOMAGIC) && !(pglob->gl_flags & GLOB_MAGCHAR))) - return(globextend(pattern, pglob, &limit)); + return(globextend(pattern, pglob, &limit, NULL)); else return(GLOB_NOMATCH); } @@ -547,7 +602,7 @@ glob2(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last, *pathend = EOS; } ++pglob->gl_matchc; - return(globextend(pathbuf, pglob, limitp)); + return(globextend(pathbuf, pglob, limitp, &sb)); } /* Find end of next segment, copy tentatively to pathend. */ @@ -670,25 +725,40 @@ glob3(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last, * gl_pathv points to (gl_offs + gl_pathc + 1) items. */ static int -globextend(const Char *path, glob_t *pglob, size_t *limitp) +globextend(const Char *path, glob_t *pglob, size_t *limitp, struct stat *sb) { char **pathv; - int i; - u_int newsize, len; - char *copy; + ssize_t i; + size_t newn, len; + char *copy = NULL; const Char *p; - - newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs); - pathv = pglob->gl_pathv ? realloc((char *)pglob->gl_pathv, newsize) : - malloc(newsize); - if (pathv == NULL) { + struct stat **statv; + + newn = 2 + pglob->gl_pathc + pglob->gl_offs; + if (SIZE_MAX / sizeof(*pathv) <= newn || + SIZE_MAX / sizeof(*statv) <= newn) { + nospace: + for (i = pglob->gl_offs; i < newn - 2; i++) { + if (pglob->gl_pathv && pglob->gl_pathv[i]) + free(pglob->gl_pathv[i]); + if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0 && + pglob->gl_pathv && pglob->gl_pathv[i]) + free(pglob->gl_statv[i]); + } if (pglob->gl_pathv) { free(pglob->gl_pathv); pglob->gl_pathv = NULL; } + if (pglob->gl_statv) { + free(pglob->gl_statv); + pglob->gl_statv = NULL; + } return(GLOB_NOSPACE); } + pathv = realloc(pglob->gl_pathv, newn * sizeof(*pathv)); + if (pathv == NULL) + goto nospace; if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) { /* first time around -- clear initial gl_offs items */ pathv += pglob->gl_offs; @@ -697,6 +767,29 @@ globextend(const Char *path, glob_t *pglob, size_t *limitp) } pglob->gl_pathv = pathv; + if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0) { + statv = realloc(pglob->gl_statv, newn * sizeof(*statv)); + if (statv == NULL) + goto nospace; + if (pglob->gl_statv == NULL && pglob->gl_offs > 0) { + /* first time around -- clear initial gl_offs items */ + statv += pglob->gl_offs; + for (i = pglob->gl_offs; --i >= 0; ) + *--statv = NULL; + } + pglob->gl_statv = statv; + if (sb == NULL) + statv[pglob->gl_offs + pglob->gl_pathc] = NULL; + else { + if ((statv[pglob->gl_offs + pglob->gl_pathc] = + malloc(sizeof(**statv))) == NULL) + goto copy_error; + memcpy(statv[pglob->gl_offs + pglob->gl_pathc], sb, + sizeof(*sb)); + } + statv[pglob->gl_offs + pglob->gl_pathc + 1] = NULL; + } + for (p = path; *p++;) ; len = (size_t)(p - path); @@ -711,11 +804,11 @@ globextend(const Char *path, glob_t *pglob, size_t *limitp) pathv[pglob->gl_offs + pglob->gl_pathc] = NULL; if ((pglob->gl_flags & GLOB_LIMIT) && - newsize + *limitp >= (u_int) get_arg_max()) { + (newn * sizeof(*pathv)) + *limitp >= ARG_MAX) { errno = 0; return(GLOB_NOSPACE); } - + copy_error: return(copy == NULL ? GLOB_NOSPACE : 0); } @@ -751,13 +844,21 @@ match(Char *name, Char *pat, Char *patend) return(0); if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS) ++pat; - while (((c = *pat++) & M_MASK) != M_END) + 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; pat += 2; } else if (c == k) ok = 1; + } if (ok == negate_range) return(0); break; @@ -785,6 +886,14 @@ globfree(glob_t *pglob) free(pglob->gl_pathv); pglob->gl_pathv = NULL; } + if (pglob->gl_statv != NULL) { + for (i = 0; i < pglob->gl_pathc; i++) { + if (pglob->gl_statv[i] != NULL) + free(pglob->gl_statv[i]); + } + free(pglob->gl_statv); + pglob->gl_statv = NULL; + } } static DIR * @@ -830,11 +939,11 @@ g_stat(Char *fn, struct stat *sb, glob_t *pglob) } static Char * -g_strchr(Char *str, int ch) +g_strchr(const Char *str, int ch) { do { if (*str == ch) - return (str); + return ((Char *)str); } while (*str++); return (NULL); } @@ -870,5 +979,4 @@ qprintf(const char *str, Char *s) #endif #endif /* !defined(HAVE_GLOB) || !defined(GLOB_HAS_ALTDIRFUNC) || - !defined(GLOB_HAS_GL_MATCHC) */ - + !defined(GLOB_HAS_GL_MATCHC) || !defined(GLOB_HAS_GL_STATV) */ |