Rev 40262 | Blame | Last modification | View Log | Download | RSS feed
/** Copyright (c) 1989, 1993* The Regents of the University of California. All rights reserved.** This code is derived from software contributed to Berkeley by* Guido van Rossum.** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions* are met:* 1. Redistributions of source code must retain the above copyright* notice, this list of conditions and the following disclaimer.* 2. Redistributions in binary form must reproduce the above copyright* notice, this list of conditions and the following disclaimer in the* documentation and/or other materials provided with the distribution.* 3. Neither the name of the University nor the names of its contributors* may be used to endorse or promote products derived from this software* without specific prior written permission.** THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF* SUCH DAMAGE.*/#if defined(LIBC_SCCS) && !defined(lint)static char sccsid[] = "@(#)glob.c 8.3 (Berkeley) 10/13/93";/* most changes between the version above and the one below have been ported:static char sscsid[]= "$OpenBSD: glob.c,v 1.8.10.1 2001/04/10 jason Exp $";*/#endif /* LIBC_SCCS and not lint *//** 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.* GLOB_ALPHASORT:* sort alphabetically like csh (case doesn't matter) instead of in ASCII* order*/#include "dos_glob.h"#define MAXPATHLEN 255#define DOSISH#define ARG_MAX 14500#define BG_DOLLAR '$'#define BG_DOT '.'#define BG_EOS '\0'#define BG_LBRACKET '['#define BG_NOT '!'#define BG_QUESTION '?'#define BG_QUOTE '\\'#define BG_RANGE '-'#define BG_RBRACKET ']'#define BG_SEP '/'#ifdef DOSISH#define BG_SEP2 '\\'#endif#define BG_STAR '*'#define BG_TILDE '~'#define BG_UNDERSCORE '_'#define BG_LBRACE '{'#define BG_RBRACE '}'#define BG_SLASH '/'#define BG_COMMA ','#define M_QUOTE 0x80#define M_PROTECT 0x40#define M_MASK 0xff#define M_ASCII 0x7f#include <stdlib.h>#include <dirent.h>#include <sys/types.h>#include <sys/stat.h>#include <ctype.h>#include <string.h>typedef unsigned char Char;typedef unsigned char U8;typedef size_t STRLEN;typedef struct stat Stat_t;typedef struct dirent Direntry_t;#define CHAR(c) ((Char)((c)&M_ASCII))#define META(c) ((Char)((c)|M_QUOTE))#define M_ALL META('*')#define M_END META(']')#define M_NOT META('!')#define M_ONE META('?')#define M_RNG META('-')#define M_SET META('[')#define ismeta(c) (((c)&M_QUOTE) != 0)static int compare(const void *, const void *);static int ci_compare(const void *, const void *);static int g_Ctoc(const Char *, char *, STRLEN);static int g_lstat(Char *, Stat_t *, glob_t *);static DIR *g_opendir(Char *, glob_t *);static const Char *g_strchr(const Char *, int);static int glob0(const Char *, glob_t *);static int glob1(Char *, Char *, glob_t *, size_t *);static int glob2(Char *, Char *, Char *, Char *, Char *, Char *,glob_t *, size_t *);static int glob3(Char *, Char *, Char *, Char *, Char *, Char *,Char *, Char *, glob_t *, size_t *);static int globextend(const Char *, glob_t *, size_t *);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 match(Char *, Char *, Char *, int);#ifdef GLOB_DEBUGstatic void qprintf(const char *, Char *);#endif /* GLOB_DEBUG */intdos_glob(const char *pattern, int flags,int (*errfunc)(const char *, int), glob_t *pglob){const U8 *patnext;int c;Char *bufnext, *bufend, patbuf[MAXPATHLEN];patnext = (U8 *) pattern;#if 1if (!(flags & GLOB_APPEND)) {pglob->gl_pathc = 0;pglob->gl_pathv = NULL;if (!(flags & GLOB_DOOFFS))pglob->gl_offs = 0;}#elsepglob->gl_pathc = 0;pglob->gl_pathv = NULL;pglob->gl_offs = 0;#endifpglob->gl_flags = flags & ~GLOB_MAGCHAR;pglob->gl_errfunc = errfunc;pglob->gl_matchc = 0;bufnext = patbuf;bufend = bufnext + MAXPATHLEN - 1;#ifdef DOSISH/* Nasty hack to treat patterns like "C:*" correctly. In this* case, the * should match any file in the current directory* on the C: drive. However, the glob code does not treat the* colon specially, so it looks for files beginning "C:" in* the current directory. To fix this, change the pattern to* add an explicit "./" at the start (just after the drive* letter and colon - ie change to "C:./").*/if (isalpha(pattern[0]) && pattern[1] == ':' &&pattern[2] != BG_SEP && pattern[2] != BG_SEP2 &&bufend - bufnext > 4) {*bufnext++ = pattern[0];*bufnext++ = ':';*bufnext++ = '.';*bufnext++ = BG_SEP;patnext += 2;}#endifif (flags & GLOB_QUOTE) {/* Protect the quoted characters. */while (bufnext < bufend && (c = *patnext++) != BG_EOS)if (c == BG_QUOTE) {#ifdef DOSISH/* To avoid backslashitis on Win32,* we only treat \ as a quoting character* if it precedes one of the* metacharacters []-{}~\*/if ((c = *patnext++) != '[' && c != ']' &&c != '-' && c != '{' && c != '}' &&c != '~' && c != '\\') {#elseif ((c = *patnext++) == BG_EOS) {#endifc = BG_QUOTE;--patnext;}*bufnext++ = c | M_PROTECT;} else*bufnext++ = c;} elsewhile (bufnext < bufend && (c = *patnext++) != BG_EOS)*bufnext++ = c;*bufnext = BG_EOS;if (flags & GLOB_BRACE)return globexp1(patbuf, pglob);elsereturn glob0(patbuf, pglob);}/** Expand recursively a glob {} pattern. When there is no more expansion* invoke the standard globbing routine to glob the rest of the magic* characters*/static intglobexp1(const Char *pattern, glob_t *pglob){const Char* ptr = pattern;int rv;/* Protect a single {}, for find(1), like csh */if (pattern[0] == BG_LBRACE && pattern[1] == BG_RBRACE && pattern[2] == BG_EOS)return glob0(pattern, pglob);while ((ptr = (const Char *) g_strchr(ptr, BG_LBRACE)) != NULL)if (!globexp2(ptr, pattern, pglob, &rv))return rv;return glob0(pattern, pglob);}/** Recursive brace globbing helper. Tries to expand a single brace.* If it succeeds then it invokes globexp1 with the new pattern.* If it fails then it tries to glob the rest of the pattern and returns.*/static intglobexp2(const Char *ptr, const Char *pattern,glob_t *pglob, int *rv){int i;Char *lm, *ls;const Char *pe, *pm, *pl;Char patbuf[MAXPATHLEN];/* copy part up to the brace */for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++);*lm = BG_EOS;ls = lm;/* Find the balanced brace */for (i = 0, pe = ++ptr; *pe; pe++)if (*pe == BG_LBRACKET) {/* Ignore everything between [] */for (pm = pe++; *pe != BG_RBRACKET && *pe != BG_EOS; pe++);if (*pe == BG_EOS) {/** We could not find a matching BG_RBRACKET.* Ignore and just look for BG_RBRACE*/pe = pm;}} else if (*pe == BG_LBRACE)i++;else if (*pe == BG_RBRACE) {if (i == 0)break;i--;}/* Non matching braces; just glob the pattern */if (i != 0 || *pe == BG_EOS) {*rv = glob0(patbuf, pglob);return 0;}for (i = 0, pl = pm = ptr; pm <= pe; pm++) {switch (*pm) {case BG_LBRACKET:/* Ignore everything between [] */for (pl = pm++; *pm != BG_RBRACKET && *pm != BG_EOS; pm++);if (*pm == BG_EOS) {/** We could not find a matching BG_RBRACKET.* Ignore and just look for BG_RBRACE*/pm = pl;}break;case BG_LBRACE:i++;break;case BG_RBRACE:if (i) {i--;break;}/* FALLTHROUGH */case BG_COMMA:if (i && *pm == BG_COMMA)break;else {/* Append the current string */for (lm = ls; (pl < pm); *lm++ = *pl++);/** Append the rest of the pattern after the* closing brace*/for (pl = pe + 1; (*lm++ = *pl++) != BG_EOS; );/* Expand the current pattern */#ifdef GLOB_DEBUGqprintf("globexp2:", patbuf);#endif /* GLOB_DEBUG */*rv = globexp1(patbuf, pglob);/* move after the comma, to the next string */pl = pm + 1;}break;default:break;}}*rv = 0;return 0;}/** expand tilde from the passwd file: not supported.*/static const Char *globtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t *pglob){char *h;const Char *p;Char *b, *eb;if (*pattern != BG_TILDE || !(pglob->gl_flags & GLOB_TILDE))return pattern;/* Copy up to the end of the string or / */eb = &patbuf[patbuf_len - 1];for (p = pattern + 1, h = (char *) patbuf;h < (char*)eb && *p && *p != BG_SLASH; *h++ = (char)*p++);*h = BG_EOS;if (((char *) patbuf)[0] == BG_EOS) {/** handle a plain ~ or ~/ by expanding $HOME* first and then trying the password file*/if ((h = getenv("R_USER")) == NULL) {return pattern;}} else {/** Expand a ~user*/return pattern;}/* Copy the home directory */for (b = patbuf; b < eb && *h; *b++ = *h++);/* Append the rest of the pattern */while (b < eb && (*b++ = *p++) != BG_EOS);*b = BG_EOS;return patbuf;}/** The main glob() routine: compiles the pattern (optionally processing* quotes), calls glob1() to do the real pattern matching, and finally* sorts the list (unless unsorted operation is requested). Returns 0* if things went well, nonzero if errors occurred. It is not an error* to find no matches.*/static intglob0(const Char *pattern, glob_t *pglob){const Char *qpat, *qpatnext;int c, err, oldflags, oldpathc;Char *bufnext, patbuf[MAXPATHLEN];size_t limit = 0;qpat = globtilde(pattern, patbuf, MAXPATHLEN, pglob);qpatnext = qpat;oldflags = pglob->gl_flags;oldpathc = pglob->gl_pathc;bufnext = patbuf;/* We don't need to check for buffer overflow any more. */while ((c = *qpatnext++) != BG_EOS) {switch (c) {case BG_LBRACKET:c = *qpatnext;if (c == BG_NOT)++qpatnext;if (*qpatnext == BG_EOS ||g_strchr((Char *) qpatnext+1, BG_RBRACKET) == NULL) {*bufnext++ = BG_LBRACKET;if (c == BG_NOT)--qpatnext;break;}*bufnext++ = M_SET;if (c == BG_NOT)*bufnext++ = M_NOT;c = *qpatnext++;do {*bufnext++ = CHAR(c);if (*qpatnext == BG_RANGE &&(c = qpatnext[1]) != BG_RBRACKET) {*bufnext++ = M_RNG;*bufnext++ = CHAR(c);qpatnext += 2;}} while ((c = *qpatnext++) != BG_RBRACKET);pglob->gl_flags |= GLOB_MAGCHAR;*bufnext++ = M_END;break;case BG_QUESTION:pglob->gl_flags |= GLOB_MAGCHAR;*bufnext++ = M_ONE;break;case BG_STAR:pglob->gl_flags |= GLOB_MAGCHAR;/* collapse adjacent stars to one,* to avoid exponential behavior*/if (bufnext == patbuf || bufnext[-1] != M_ALL)*bufnext++ = M_ALL;break;default:*bufnext++ = CHAR(c);break;}}*bufnext = BG_EOS;#ifdef GLOB_DEBUGqprintf("glob0:", patbuf);#endif /* GLOB_DEBUG */if ((err = glob1(patbuf, patbuf+MAXPATHLEN-1, pglob, &limit)) != 0) {pglob->gl_flags = oldflags;return(err);}/** If there was no match we are going to append the pattern* if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified* and the pattern did not contain any magic characters* GLOB_NOMAGIC is there just for compatibility with csh.*/if (pglob->gl_pathc == oldpathc &&((pglob->gl_flags & GLOB_NOCHECK) ||((pglob->gl_flags & GLOB_NOMAGIC) &&!(pglob->gl_flags & GLOB_MAGCHAR)))){#ifdef GLOB_DEBUGprintf("calling globextend from glob0\n");#endif /* GLOB_DEBUG */pglob->gl_flags = oldflags;return(globextend(qpat, pglob, &limit));}else if (!(pglob->gl_flags & GLOB_NOSORT))qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,pglob->gl_pathc - oldpathc, sizeof(char *),(pglob->gl_flags & (GLOB_ALPHASORT|GLOB_NOCASE))? ci_compare : compare);pglob->gl_flags = oldflags;return(0);}static intci_compare(const void *p, const void *q){const char *pp = *(const char **)p;const char *qq = *(const char **)q;int ci;while (*pp && *qq) {if (tolower(*pp) != tolower(*qq))break;++pp;++qq;}ci = tolower(*pp) - tolower(*qq);if (ci == 0)return compare(p, q);return ci;}static intcompare(const void *p, const void *q){return(strcmp(*(char **)p, *(char **)q));}static intglob1(Char *pattern, Char *pattern_last, glob_t *pglob, size_t *limitp){Char pathbuf[MAXPATHLEN];/* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */if (*pattern == BG_EOS) return(0);return(glob2(pathbuf, pathbuf+MAXPATHLEN-1,pathbuf, pathbuf+MAXPATHLEN-1,pattern, pattern_last, pglob, limitp));}/** The functions glob2 and glob3 are mutually recursive; there is one level* of recursion for each segment in the pattern that contains one or more* meta characters.*/static intglob2(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last,Char *pattern, Char *pattern_last, glob_t *pglob, size_t *limitp){Stat_t sb;Char *p, *q;int anymeta;/** Loop over pattern segments until end of pattern or until* segment with meta character found.*/for (anymeta = 0;;) {if (*pattern == BG_EOS) { /* End of pattern? */*pathend = BG_EOS;if (g_lstat(pathbuf, &sb, pglob)) return(0);if (((pglob->gl_flags & GLOB_MARK) &&pathend[-1] != BG_SEP#ifdef DOSISH&& pathend[-1] != BG_SEP2#endif) && S_ISDIR(sb.st_mode) ) {if (pathend+1 > pathend_last)return (1);*pathend++ = BG_SEP;*pathend = BG_EOS;}++pglob->gl_matchc;#ifdef GLOB_DEBUGprintf("calling globextend from glob2\n");#endif /* GLOB_DEBUG */return(globextend(pathbuf, pglob, limitp));}/* Find end of next segment, copy tentatively to pathend. */q = pathend;p = pattern;while (*p != BG_EOS && *p != BG_SEP#ifdef DOSISH&& *p != BG_SEP2#endif) {if (ismeta(*p)) anymeta = 1;if (q+1 > pathend_last) return (1);*q++ = *p++;}if (!anymeta) { /* No expansion, do next segment. */pathend = q;pattern = p;while (*pattern == BG_SEP#ifdef DOSISH|| *pattern == BG_SEP2#endif) {if (pathend+1 > pathend_last) return (1);*pathend++ = *pattern++;}} else/* Need expansion, recurse. */return(glob3(pathbuf, pathbuf_last, pathend,pathend_last, pattern, pattern_last,p, pattern_last, pglob, limitp));}/* NOTREACHED */}static intglob3(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last,Char *pattern, Char *pattern_last,Char *restpattern, Char *restpattern_last, glob_t *pglob, size_t *limitp){register Direntry_t *dp;DIR *dirp;int err;int nocase;char buf[MAXPATHLEN];if (pathend > pathend_last)return (1);*pathend = BG_EOS;errno = 0;#ifdef VMS{Char *q = pathend;if (q - pathbuf > 5) {q -= 5;if (q[0] == '.' &&tolower(q[1]) == 'd' && tolower(q[2]) == 'i' &&tolower(q[3]) == 'r' && q[4] == '/'){q[0] = '/';q[1] = BG_EOS;pathend = q+1;}}}#endifif ((dirp = g_opendir(pathbuf, pglob)) == NULL) {/* TODO: don't call for ENOENT or ENOTDIR? */if (pglob->gl_errfunc) {if (g_Ctoc(pathbuf, buf, sizeof(buf)))return (GLOB_ABEND);if (pglob->gl_errfunc(buf, errno) ||(pglob->gl_flags & GLOB_ERR))return (GLOB_ABEND);}return(0);}err = 0;nocase = ((pglob->gl_flags & GLOB_NOCASE) != 0);/* Search directory for matching names. */while ((dp = readdir(dirp))) {register U8 *sc;register Char *dc;/* Initial BG_DOT must be matched literally. */if (dp->d_name[0] == BG_DOT && *pattern != BG_DOT)continue;dc = pathend;sc = (U8 *) dp->d_name;while (dc < pathend_last && (*dc++ = *sc++) != BG_EOS);if (dc >= pathend_last) {*dc = BG_EOS;err = 1;break;}if (!match(pathend, pattern, restpattern, nocase)) {*pathend = BG_EOS;continue;}err = glob2(pathbuf, pathbuf_last, --dc, pathend_last,restpattern, restpattern_last, pglob, limitp);if (err)break;}closedir(dirp);return(err);}extern void *R_chk_calloc(size_t, size_t);extern void *R_chk_realloc(void *, size_t);extern void R_chk_free(void *);#define Calloc(n, t) (t *) R_chk_calloc( (size_t) (n), sizeof(t) )#define Realloc(p,n,t) (t *) R_chk_realloc( (void *)(p), (size_t)((n) * sizeof(t)) )#define Free(p) (R_chk_free( (void *)(p) ), (p) = NULL)/** Extend the gl_pathv member of a glob_t structure to accomodate a new item,* add the new item, and update gl_pathc.** This assumes the BSD realloc, which only copies the block when its size* crosses a power-of-two boundary; for v7 realloc, this would cause quadratic* behavior.** Return 0 if new item added, error code if memory couldn't be allocated.** Invariant of the glob_t structure:* Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and* gl_pathv points to (gl_offs + gl_pathc + 1) items.*/static intglobextend(const Char *path, glob_t *pglob, size_t *limitp){register char **pathv;register int i;STRLEN newsize, len;char *copy;const Char *p;#ifdef GLOB_DEBUGprintf("Adding ");for (p = path; *p; p++)(void)printf("%c", CHAR(*p));printf("\n");#endif /* GLOB_DEBUG */newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);if (pglob->gl_pathv)pathv = Realloc(pglob->gl_pathv, newsize, char*);elsepathv = Calloc(newsize,char*);if (pathv == NULL) {if (pglob->gl_pathv) {Free(pglob->gl_pathv);pglob->gl_pathv = NULL;}return(GLOB_NOSPACE);}if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {/* first time around -- clear initial gl_offs items */pathv += pglob->gl_offs;for (i = pglob->gl_offs; --i >= 0; )*--pathv = NULL;}pglob->gl_pathv = pathv;for (p = path; *p++;);len = (STRLEN)(p - path);*limitp += len;copy = Calloc(p-path, char);if (copy != NULL) {if (g_Ctoc(path, copy, len)) {Free(copy);return(GLOB_NOSPACE);}pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;}pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;if ((pglob->gl_flags & GLOB_LIMIT) &&newsize + *limitp >= ARG_MAX) {errno = 0;return(GLOB_NOSPACE);}return(copy == NULL ? GLOB_NOSPACE : 0);}/** pattern matching function for filenames. Each occurrence of the ** pattern causes a recursion level.*/static intmatch(register Char *name, register Char *pat, register Char *patend, int nocase){int ok, negate_range;Char c, k;while (pat < patend) {c = *pat++;switch (c & M_MASK) {case M_ALL:if (pat == patend)return(1);doif (match(name, pat, patend, nocase))return(1);while (*name++ != BG_EOS);return(0);case M_ONE:if (*name++ == BG_EOS)return(0);break;case M_SET:ok = 0;if ((k = *name++) == BG_EOS)return(0);if ((negate_range = ((*pat & M_MASK) == M_NOT)) != BG_EOS)++pat;while (((c = *pat++) & M_MASK) != M_END)if ((*pat & M_MASK) == M_RNG) {if (nocase) {if (tolower(c) <= tolower(k) && tolower(k) <= tolower(pat[1]))ok = 1;} else {if (c <= k && k <= pat[1])ok = 1;}pat += 2;} else if (nocase ? (tolower(c) == tolower(k)) : (c == k))ok = 1;if (ok == negate_range)return(0);break;default:k = *name++;if (nocase ? (tolower(k) != tolower(c)) : (k != c))return(0);break;}}return(*name == BG_EOS);}/* Free allocated data belonging to a glob_t structure. */voiddos_globfree(glob_t *pglob){register int i;register char **pp;if (pglob->gl_pathv != NULL) {pp = pglob->gl_pathv + pglob->gl_offs;for (i = pglob->gl_pathc; i--; ++pp)if (*pp)Free(*pp);Free(pglob->gl_pathv);pglob->gl_pathv = NULL;}}static DIR *g_opendir(register Char *str, glob_t *pglob){char buf[MAXPATHLEN];if (!*str) {strcpy(buf, ".");} else {if (g_Ctoc(str, buf, sizeof(buf)))return(NULL);}return(opendir(buf));}static intg_lstat(register Char *fn, Stat_t *sb, glob_t *pglob){char buf[MAXPATHLEN];if (g_Ctoc(fn, buf, sizeof(buf)))return(-1);return(stat(buf, sb));}static const Char *g_strchr(const Char *str, int ch){do {if (*str == ch)return (str);} while (*str++);return (NULL);}static intg_Ctoc(register const Char *str, char *buf, STRLEN len){while (len--) {if ((*buf++ = (char)*str++) == BG_EOS)return (0);}return (1);}#ifdef GLOB_DEBUGstatic voidqprintf(const char *str, register Char *s){register Char *p;(void)printf("%s:\n", str);for (p = s; *p; p++)(void)printf("%c", CHAR(*p));(void)printf("\n");for (p = s; *p; p++)(void)printf("%c", *p & M_PROTECT ? '"' : ' ');(void)printf("\n");for (p = s; *p; p++)(void)printf("%c", ismeta(*p) ? '_' : ' ');(void)printf("\n");}#endif /* GLOB_DEBUG */