sl@0: // glob.c sl@0: sl@0: sl@0: /* sl@0: * Copyright (c) 1989, 1993 sl@0: * The Regents of the University of California. All rights reserved. sl@0: * sl@0: * This code is derived from software contributed to Berkeley by sl@0: * Guido van Rossum. sl@0: * sl@0: * Redistribution and use in source and binary forms, with or without sl@0: * modification, are permitted provided that the following conditions sl@0: * are met: sl@0: * 1. Redistributions of source code must retain the above copyright sl@0: * notice, this list of conditions and the following disclaimer. sl@0: * 2. Redistributions in binary form must reproduce the above copyright sl@0: * notice, this list of conditions and the following disclaimer in the sl@0: * documentation and/or other materials provided with the distribution. sl@0: * 4. Neither the name of the University nor the names of its contributors sl@0: * may be used to endorse or promote products derived from this software sl@0: * without specific prior written permission. sl@0: * sl@0: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND sl@0: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE sl@0: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE sl@0: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE sl@0: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL sl@0: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS sl@0: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) sl@0: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT sl@0: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY sl@0: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF sl@0: * SUCH DAMAGE. sl@0: */ sl@0: sl@0: /* Portions Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).All rights reserved. */ sl@0: sl@0: #if defined(LIBC_SCCS) && !defined(lint) sl@0: static char sccsid[] = "@(#)glob.c 8.3 (Berkeley) 10/13/93"; sl@0: #endif /* LIBC_SCCS and not lint */ sl@0: #include sl@0: __FBSDID("$FreeBSD: src/lib/libc/gen/glob.c,v 1.23 2005/09/14 19:14:32 ache Exp $"); sl@0: sl@0: sl@0: /* sl@0: * glob(3) -- a superset of the one defined in POSIX 1003.2. sl@0: * sl@0: * The [!...] convention to negate a range is supported (SysV, Posix, ksh). sl@0: * sl@0: * Optional extra services, controlled by flags not defined by POSIX: sl@0: * sl@0: * GLOB_QUOTE: sl@0: * Escaping convention: \ inhibits any special meaning the following sl@0: * character might have (except \ at end of string is retained). sl@0: * GLOB_MAGCHAR: sl@0: * Set in gl_flags if pattern contained a globbing character. sl@0: * GLOB_NOMAGIC: sl@0: * Same as GLOB_NOCHECK, but it will only append pattern if it did sl@0: * not contain any magic characters. [Used in csh style globbing] sl@0: * GLOB_ALTDIRFUNC: sl@0: * Use alternately specified directory access functions. sl@0: * GLOB_TILDE: sl@0: * expand ~user/foo to the /home/dir/of/user/foo sl@0: * GLOB_BRACE: sl@0: * expand {1,2}{a,b} to 1a 1b 2a 2b sl@0: * gl_matchc: sl@0: * Number of matches in the current invocation of glob. sl@0: */ sl@0: sl@0: /* sl@0: * Some notes on multibyte character support: sl@0: * 1. Patterns with illegal byte sequences match nothing - even if sl@0: * GLOB_NOCHECK is specified. sl@0: * 2. Illegal byte sequences in filenames are handled by treating them as sl@0: * single-byte characters with a value of the first byte of the sequence sl@0: * cast to wchar_t. sl@0: * 3. State-dependent encodings are not currently supported. sl@0: */ sl@0: sl@0: #include sl@0: #include sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: #include "libc_collate.h" sl@0: sl@0: sl@0: #if (defined(__SYMBIAN32__) && (defined(__WINSCW__) || defined(__WINS__))) sl@0: sl@0: #include "libc_wsd_defs.h" sl@0: #include "reent.h" sl@0: sl@0: #endif //defined(__SYMBIAN32__) && (defined(__WINSCW__) || defined(__WINS__)) sl@0: sl@0: #ifdef __cplusplus sl@0: extern "C" { sl@0: #endif sl@0: #ifdef __SYMBIAN32__ sl@0: extern char LC_COLLATE_LocaleName[30]; sl@0: #ifdef EMULATOR sl@0: char *GET_WSD_VAR_NAME(LC_COLLATE_LocaleName, g)(); sl@0: #define LC_COLLATE_LocaleName (GET_WSD_VAR_NAME(LC_COLLATE_LocaleName, g)()) sl@0: #endif //EMULATOR sl@0: sl@0: //Declared and defined from libc_collate.h and vfscanf.c sl@0: extern int __collate_range_cmp(int , int ); sl@0: #endif //__SYMBIAN32__ sl@0: sl@0: sl@0: sl@0: #define DOLLAR '$' sl@0: #define DOT '.' sl@0: #define EOS '\0' sl@0: #define LBRACKET '[' sl@0: #define NOT '!' sl@0: #define QUESTION '?' sl@0: #define QUOTE '\\' sl@0: #define RANGE '-' sl@0: #define RBRACKET ']' sl@0: sl@0: // For Symbian Environment the path is specified using the '\' sl@0: // unlike in the Linux where the path seprator '/' is used. sl@0: // Since '\\' as path seprator is used the escaping flag sl@0: // 'QUOTE' cannot be used here. sl@0: // For e.g. ..\\test\\hollows\\* sl@0: sl@0: #ifdef __SYMBIAN32__ sl@0: #define SEP '\\' sl@0: #else sl@0: #define SEP '/' sl@0: #endif //__SYMBIAN32__ sl@0: sl@0: sl@0: #define STAR '*' sl@0: #define TILDE '~' sl@0: #define UNDERSCORE '_' sl@0: #define LBRACE '{' sl@0: #define RBRACE '}' sl@0: #define SLASH '/' sl@0: #define COMMA ',' sl@0: sl@0: sl@0: #ifndef __SYMBIAN32__ sl@0: sl@0: #ifndef DEBUG sl@0: sl@0: #define M_QUOTE 0x8000000000ULL sl@0: #define M_PROTECT 0x4000000000ULL sl@0: #define M_MASK 0xffffffffffULL sl@0: #define M_CHAR 0x00ffffffffULL sl@0: sl@0: typedef uint_fast64_t Char; sl@0: sl@0: #else //DEBUG sl@0: sl@0: #define M_QUOTE 0x80 sl@0: #define M_PROTECT 0x40 sl@0: #define M_MASK 0xff sl@0: #define M_CHAR 0x7f sl@0: sl@0: typedef char Char; sl@0: sl@0: #endif //DEBUG sl@0: sl@0: #else //__SYMBIAN32__ sl@0: sl@0: #define M_QUOTE 0x80 sl@0: #define M_PROTECT 0x40 sl@0: #define M_MASK 0xff sl@0: #define M_CHAR 0x7f sl@0: sl@0: typedef unsigned char Char; sl@0: sl@0: #endif //__SYMBIAN32__ sl@0: sl@0: #define CHAR(c) ((Char)((c)&M_CHAR)) sl@0: #define META(c) ((Char)((c)|M_QUOTE)) sl@0: #define M_ALL META('*') sl@0: #define M_END META(']') sl@0: #define M_NOT META('!') sl@0: #define M_ONE META('?') sl@0: #define M_RNG META('-') sl@0: #define M_SET META('[') sl@0: #define ismeta(c) (((c)&M_QUOTE) != 0) sl@0: sl@0: sl@0: static int compare(const void *, const void *); sl@0: static int g_Ctoc(const Char *, char *, u_int); sl@0: static int g_lstat(Char *, struct stat *, glob_t *); sl@0: static DIR *g_opendir(Char *, glob_t *); sl@0: static Char *g_strchr(Char *, wchar_t); sl@0: #ifdef notdef sl@0: static Char *g_strcat(Char *, const Char *); sl@0: #endif sl@0: static int g_stat(Char *, struct stat *, glob_t *); sl@0: static int glob0(const Char *, glob_t *, int *); sl@0: static int glob1(Char *, glob_t *, int *); sl@0: static int glob2(Char *, Char *, Char *, Char *, glob_t *, int *); sl@0: static int glob3(Char *, Char *, Char *, Char *, Char *, glob_t *, int *); sl@0: static int globextend(const Char *, glob_t *, int *); sl@0: static const Char * sl@0: globtilde(const Char *, Char *, size_t, glob_t *); sl@0: static int globexp1(const Char *, glob_t *, int *); sl@0: static int globexp2(const Char *, const Char *, glob_t *, int *, int *); sl@0: static int match(Char *, Char *, Char *); sl@0: #ifdef DEBUG sl@0: static void qprintf(const char *, Char *); sl@0: #endif sl@0: sl@0: // EXTERNAL FUNCTION PROTOTYPES sl@0: sl@0: sl@0: //-------------------------------------------------------------------------------------------- sl@0: //Function Name : int glob(glob(const char *, int flags, int (*)(const char *, int), glob_t *) sl@0: //Description : It takes pattern to be searched, flags and the structure of the type glob_t. sl@0: // It can optionally also take If errfunc is not NULL, it will be called in sl@0: // error with the arguments epath, a pointer to the case of an path which sl@0: // failed sl@0: // sl@0: //Return Value : If successful returns the pathnames of the searched pattern contained sl@0: // in the glob_t structure member reference pointer gl_pathv. In case sl@0: // failure, gl_pathv is NULL. sl@0: //---------------------------------------------------------------------------------------------- sl@0: sl@0: EXPORT_C int sl@0: glob(pattern, flags, errfunc, pglob) sl@0: const char *pattern; sl@0: int flags, (*errfunc)(const char *, int); sl@0: glob_t *pglob; sl@0: { sl@0: const u_char *patnext; sl@0: int limit; sl@0: Char *bufnext, *bufend, patbuf[MAXPATHLEN], prot; sl@0: mbstate_t mbs; sl@0: wchar_t wc; sl@0: size_t clen; sl@0: sl@0: patnext = (u_char *) pattern; sl@0: sl@0: // Since we are using '\\' as path seprator, this flag needs to be mentioned sl@0: // by default in Symbian. Instead this can be hardcoded here. sl@0: sl@0: #ifdef __SYMBIAN32__ sl@0: flags = flags | GLOB_NOESCAPE; sl@0: #endif sl@0: sl@0: if (!(flags & GLOB_APPEND)) { sl@0: pglob->gl_pathc = 0; sl@0: pglob->gl_pathv = NULL; sl@0: if (!(flags & GLOB_DOOFFS)) sl@0: pglob->gl_offs = 0; sl@0: } sl@0: if (flags & GLOB_LIMIT) { sl@0: limit = pglob->gl_matchc; sl@0: if (limit == 0) sl@0: limit = ARG_MAX; sl@0: } else sl@0: limit = 0; sl@0: pglob->gl_flags = flags & ~GLOB_MAGCHAR; sl@0: pglob->gl_errfunc = errfunc; sl@0: pglob->gl_matchc = 0; sl@0: sl@0: bufnext = patbuf; sl@0: bufend = bufnext + MAXPATHLEN - 1; sl@0: if (flags & GLOB_NOESCAPE) { sl@0: memset(&mbs, 0, sizeof(mbs)); sl@0: while (bufend - bufnext >= MB_CUR_MAX) { sl@0: clen = mbrtowc(&wc,(char*)patnext, MB_LEN_MAX, &mbs); sl@0: if (clen == (size_t)-1 || clen == (size_t)-2) sl@0: return (GLOB_NOMATCH); sl@0: else if (clen == 0) sl@0: break; sl@0: *bufnext++ = wc; sl@0: patnext += clen; sl@0: } sl@0: } else { sl@0: /* Protect the quoted characters. */ sl@0: memset(&mbs, 0, sizeof(mbs)); sl@0: while (bufend - bufnext >= MB_CUR_MAX) { sl@0: if (*patnext == QUOTE) { sl@0: if (*++patnext == EOS) { sl@0: *bufnext++ = QUOTE | M_PROTECT; sl@0: continue; sl@0: } sl@0: prot = M_PROTECT; sl@0: } else sl@0: prot = 0; sl@0: clen = mbrtowc(&wc, (char*)patnext, MB_LEN_MAX, &mbs); sl@0: if (clen == (size_t)-1 || clen == (size_t)-2) sl@0: return (GLOB_NOMATCH); sl@0: else if (clen == 0) sl@0: break; sl@0: *bufnext++ = wc | prot; sl@0: patnext += clen; sl@0: } sl@0: } sl@0: *bufnext = EOS; sl@0: sl@0: if (flags & GLOB_BRACE) sl@0: return globexp1(patbuf, pglob, &limit); sl@0: else sl@0: return glob0(patbuf, pglob, &limit); sl@0: } sl@0: sl@0: /* sl@0: * Expand recursively a glob {} pattern. When there is no more expansion sl@0: * invoke the standard globbing routine to glob the rest of the magic sl@0: * characters sl@0: */ sl@0: static int sl@0: globexp1(pattern, pglob, limit) sl@0: const Char *pattern; sl@0: glob_t *pglob; sl@0: int *limit; sl@0: { sl@0: const Char* ptr = pattern; sl@0: int rv; sl@0: sl@0: /* Protect a single {}, for find(1), like csh */ sl@0: if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS) sl@0: return glob0(pattern, pglob, limit); sl@0: sl@0: while ((ptr = (const Char *) g_strchr((Char *) ptr, LBRACE)) != NULL) sl@0: if (!globexp2(ptr, pattern, pglob, &rv, limit)) sl@0: return rv; sl@0: sl@0: return glob0(pattern, pglob, limit); sl@0: } sl@0: sl@0: sl@0: /* sl@0: * Recursive brace globbing helper. Tries to expand a single brace. sl@0: * If it succeeds then it invokes globexp1 with the new pattern. sl@0: * If it fails then it tries to glob the rest of the pattern and returns. sl@0: */ sl@0: static int sl@0: globexp2(ptr, pattern, pglob, rv, limit) sl@0: const Char *ptr, *pattern; sl@0: glob_t *pglob; sl@0: int *rv, *limit; sl@0: { sl@0: int i; sl@0: Char *lm, *ls; sl@0: const Char *pe, *pm, *pm1, *pl; sl@0: Char patbuf[MAXPATHLEN]; sl@0: sl@0: /* copy part up to the brace */ sl@0: for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++) sl@0: continue; sl@0: *lm = EOS; sl@0: ls = lm; sl@0: sl@0: /* Find the balanced brace */ sl@0: for (i = 0, pe = ++ptr; *pe; pe++) sl@0: if (*pe == LBRACKET) { sl@0: /* Ignore everything between [] */ sl@0: for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++) sl@0: continue; sl@0: if (*pe == EOS) { sl@0: /* sl@0: * We could not find a matching RBRACKET. sl@0: * Ignore and just look for RBRACE sl@0: */ sl@0: pe = pm; sl@0: } sl@0: } sl@0: else if (*pe == LBRACE) sl@0: i++; sl@0: else if (*pe == RBRACE) { sl@0: if (i == 0) sl@0: break; sl@0: i--; sl@0: } sl@0: sl@0: /* Non matching braces; just glob the pattern */ sl@0: if (i != 0 || *pe == EOS) { sl@0: *rv = glob0(patbuf, pglob, limit); sl@0: return 0; sl@0: } sl@0: sl@0: for (i = 0, pl = pm = ptr; pm <= pe; pm++) sl@0: switch (*pm) { sl@0: case LBRACKET: sl@0: /* Ignore everything between [] */ sl@0: for (pm1 = pm++; *pm != RBRACKET && *pm != EOS; pm++) sl@0: continue; sl@0: if (*pm == EOS) { sl@0: /* sl@0: * We could not find a matching RBRACKET. sl@0: * Ignore and just look for RBRACE sl@0: */ sl@0: pm = pm1; sl@0: } sl@0: break; sl@0: sl@0: case LBRACE: sl@0: i++; sl@0: break; sl@0: sl@0: case RBRACE: sl@0: if (i) { sl@0: i--; sl@0: break; sl@0: } sl@0: /* FALLTHROUGH */ sl@0: case COMMA: sl@0: if (i && *pm == COMMA) sl@0: break; sl@0: else { sl@0: /* Append the current string */ sl@0: for (lm = ls; (pl < pm); *lm++ = *pl++) sl@0: continue; sl@0: /* sl@0: * Append the rest of the pattern after the sl@0: * closing brace sl@0: */ sl@0: for (pl = pe + 1; (*lm++ = *pl++) != EOS;) sl@0: continue; sl@0: sl@0: /* Expand the current pattern */ sl@0: #ifdef DEBUG sl@0: qprintf("globexp2:", patbuf); sl@0: #endif sl@0: *rv = globexp1(patbuf, pglob, limit); sl@0: sl@0: /* move after the comma, to the next string */ sl@0: pl = pm + 1; sl@0: } sl@0: break; sl@0: sl@0: default: sl@0: break; sl@0: } sl@0: *rv = 0; sl@0: return 0; sl@0: } sl@0: sl@0: sl@0: /* sl@0: * expand tilde from the passwd file. sl@0: */ sl@0: sl@0: static const Char * sl@0: globtilde(pattern, patbuf, patbuf_len, pglob) sl@0: const Char *pattern; sl@0: Char *patbuf; sl@0: size_t patbuf_len; sl@0: glob_t *pglob; sl@0: { sl@0: sl@0: #ifndef __SYMBIAN32__ sl@0: struct passwd *pwd; sl@0: #endif sl@0: sl@0: char *h; sl@0: const Char *p; sl@0: Char *b, *eb; sl@0: sl@0: if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE)) sl@0: return pattern; sl@0: sl@0: /* sl@0: * Copy up to the end of the string or / sl@0: */ sl@0: sl@0: eb = &patbuf[patbuf_len - 1]; sl@0: sl@0: #ifdef __SYMBIAN32__ sl@0: sl@0: for (p = pattern + 1, h = (char *) patbuf; sl@0: h < (char *)eb && *p && *p != SEP ; *h++ = *p++) sl@0: continue; sl@0: sl@0: *h = EOS; sl@0: sl@0: if (((char *) patbuf)[0] == EOS) { sl@0: /* sl@0: * handle a plain ~ or ~\\ by getting the sl@0: * secure Id for this process. It shall sl@0: * return the path as C:\\private\\ sl@0: * on emulator and Z:\\private\\ sl@0: * on hardware sl@0: */ sl@0: if( NULL == getcwd(h, patbuf_len) ) sl@0: return pattern; sl@0: } sl@0: sl@0: else{ sl@0: /* sl@0: * Its ~\\.. This will not be handled. Returns the sl@0: * pattern as it is. sl@0: */ sl@0: return pattern; sl@0: } sl@0: sl@0: # else sl@0: sl@0: for (p = pattern + 1, h = (char *) patbuf; sl@0: h < (char *)eb && *p && *p != SLASH; *h++ = *p++) sl@0: continue; sl@0: sl@0: *h = EOS; sl@0: sl@0: if (((char *) patbuf)[0] == EOS) { sl@0: /* sl@0: * handle a plain ~ or ~/ by expanding $HOME first (iff sl@0: * we're not running setuid or setgid) and then trying sl@0: * the password file sl@0: */ sl@0: if (issetugid() != 0 || sl@0: (h = getenv("HOME")) == NULL) { sl@0: if (((h = getlogin()) != NULL && sl@0: (pwd = getpwnam(h)) != NULL) || sl@0: (pwd = getpwuid(getuid())) != NULL) sl@0: h = pwd->pw_dir; sl@0: else sl@0: return pattern; sl@0: } sl@0: } sl@0: else { sl@0: /* sl@0: * Expand a ~user sl@0: */ sl@0: if ((pwd = getpwnam((char*) patbuf)) == NULL) sl@0: return pattern; sl@0: else sl@0: h = pwd->pw_dir; sl@0: } sl@0: sl@0: #endif //__SYMBIAN32__ sl@0: sl@0: sl@0: /* Copy the home directory */ sl@0: for (b = patbuf; b < eb && *h; *b++ = *h++) sl@0: continue; sl@0: sl@0: /* Append the rest of the pattern */ sl@0: while (b < eb && (*b++ = *p++) != EOS) sl@0: continue; sl@0: *b = EOS; sl@0: sl@0: return patbuf; sl@0: } sl@0: sl@0: sl@0: /* sl@0: * The main glob() routine: compiles the pattern (optionally processing sl@0: * quotes), calls glob1() to do the real pattern matching, and finally sl@0: * sorts the list (unless unsorted operation is requested). Returns 0 sl@0: * if things went well, nonzero if errors occurred. sl@0: */ sl@0: static int sl@0: glob0(pattern, pglob, limit) sl@0: const Char *pattern; sl@0: glob_t *pglob; sl@0: int *limit; sl@0: { sl@0: const Char *qpatnext; sl@0: int c, err, oldpathc; sl@0: Char *bufnext, patbuf[MAXPATHLEN]; sl@0: sl@0: qpatnext = globtilde(pattern, patbuf, MAXPATHLEN, pglob); sl@0: oldpathc = pglob->gl_pathc; sl@0: bufnext = patbuf; sl@0: sl@0: /* We don't need to check for buffer overflow any more. */ sl@0: while ((c = *qpatnext++) != EOS) { sl@0: switch (c) { sl@0: case LBRACKET: sl@0: c = *qpatnext; sl@0: if (c == NOT) sl@0: ++qpatnext; sl@0: if (*qpatnext == EOS || sl@0: g_strchr((Char *) qpatnext+1, RBRACKET) == NULL) { sl@0: *bufnext++ = LBRACKET; sl@0: if (c == NOT) sl@0: --qpatnext; sl@0: break; sl@0: } sl@0: *bufnext++ = M_SET; sl@0: if (c == NOT) sl@0: *bufnext++ = M_NOT; sl@0: c = *qpatnext++; sl@0: do { sl@0: *bufnext++ = CHAR(c); sl@0: if (*qpatnext == RANGE && sl@0: (c = qpatnext[1]) != RBRACKET) { sl@0: *bufnext++ = M_RNG; sl@0: *bufnext++ = CHAR(c); sl@0: qpatnext += 2; sl@0: } sl@0: } while ((c = *qpatnext++) != RBRACKET); sl@0: pglob->gl_flags |= GLOB_MAGCHAR; sl@0: *bufnext++ = M_END; sl@0: break; sl@0: case QUESTION: sl@0: pglob->gl_flags |= GLOB_MAGCHAR; sl@0: *bufnext++ = M_ONE; sl@0: break; sl@0: case STAR: sl@0: pglob->gl_flags |= GLOB_MAGCHAR; sl@0: /* collapse adjacent stars to one, sl@0: * to avoid exponential behavior sl@0: */ sl@0: if (bufnext == patbuf || bufnext[-1] != M_ALL) sl@0: *bufnext++ = M_ALL; sl@0: break; sl@0: default: sl@0: *bufnext++ = CHAR(c); sl@0: break; sl@0: } sl@0: } sl@0: *bufnext = EOS; sl@0: #ifdef DEBUG sl@0: qprintf("glob0:", patbuf); sl@0: #endif sl@0: sl@0: if ((err = glob1(patbuf, pglob, limit)) != 0) sl@0: return(err); sl@0: sl@0: /* sl@0: * If there was no match we are going to append the pattern sl@0: * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified sl@0: * and the pattern did not contain any magic characters sl@0: * GLOB_NOMAGIC is there just for compatibility with csh. sl@0: */ sl@0: if (pglob->gl_pathc == oldpathc) { sl@0: if (((pglob->gl_flags & GLOB_NOCHECK) || sl@0: ((pglob->gl_flags & GLOB_NOMAGIC) && sl@0: !(pglob->gl_flags & GLOB_MAGCHAR)))) sl@0: return(globextend(pattern, pglob, limit)); sl@0: else sl@0: return(GLOB_NOMATCH); sl@0: } sl@0: if (!(pglob->gl_flags & GLOB_NOSORT)) sl@0: qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc, sl@0: pglob->gl_pathc - oldpathc, sizeof(char *), compare); sl@0: return(0); sl@0: } sl@0: sl@0: static int sl@0: compare(p, q) sl@0: const void *p, *q; sl@0: { sl@0: return(strcmp(*(char **)p, *(char **)q)); sl@0: } sl@0: sl@0: static int sl@0: glob1(pattern, pglob, limit) sl@0: Char *pattern; sl@0: glob_t *pglob; sl@0: int *limit; sl@0: { sl@0: Char pathbuf[MAXPATHLEN]; sl@0: sl@0: /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */ sl@0: if (*pattern == EOS) sl@0: return(0); sl@0: return(glob2(pathbuf, pathbuf, pathbuf + MAXPATHLEN - 1, sl@0: pattern, pglob, limit)); sl@0: } sl@0: sl@0: /* sl@0: * The functions glob2 and glob3 are mutually recursive; there is one level sl@0: * of recursion for each segment in the pattern that contains one or more sl@0: * meta characters. sl@0: */ sl@0: static int sl@0: glob2(pathbuf, pathend, pathend_last, pattern, pglob, limit) sl@0: Char *pathbuf, *pathend, *pathend_last, *pattern; sl@0: glob_t *pglob; sl@0: int *limit; sl@0: { sl@0: struct stat sb; sl@0: Char *p, *q; sl@0: int anymeta; sl@0: sl@0: /* sl@0: * Loop over pattern segments until end of pattern or until sl@0: * segment with meta character found. sl@0: */ sl@0: for (anymeta = 0;;) { sl@0: if (*pattern == EOS) { /* End of pattern? */ sl@0: *pathend = EOS; sl@0: if (g_lstat(pathbuf, &sb, pglob)) sl@0: return(0); sl@0: sl@0: if (((pglob->gl_flags & GLOB_MARK) && sl@0: pathend[-1] != SEP) && (S_ISDIR(sb.st_mode) sl@0: || (S_ISLNK(sb.st_mode) && sl@0: (g_stat(pathbuf, &sb, pglob) == 0) && sl@0: S_ISDIR(sb.st_mode)))) { sl@0: if (pathend + 1 > pathend_last) sl@0: return (GLOB_ABORTED); sl@0: *pathend++ = SEP; sl@0: *pathend = EOS; sl@0: } sl@0: ++pglob->gl_matchc; sl@0: return(globextend(pathbuf, pglob, limit)); sl@0: } sl@0: sl@0: /* Find end of next segment, copy tentatively to pathend. */ sl@0: q = pathend; sl@0: p = pattern; sl@0: while (*p != EOS && *p != SEP) { sl@0: if (ismeta(*p)) sl@0: anymeta = 1; sl@0: if (q + 1 > pathend_last) sl@0: return (GLOB_ABORTED); sl@0: *q++ = *p++; sl@0: } sl@0: sl@0: if (!anymeta) { /* No expansion, do next segment. */ sl@0: pathend = q; sl@0: pattern = p; sl@0: while (*pattern == SEP) { sl@0: if (pathend + 1 > pathend_last) sl@0: return (GLOB_ABORTED); sl@0: *pathend++ = *pattern++; sl@0: } sl@0: } else /* Need expansion, recurse. */ sl@0: return(glob3(pathbuf, pathend, pathend_last, pattern, p, sl@0: pglob, limit)); sl@0: } sl@0: /* NOTREACHED */ sl@0: } sl@0: sl@0: static int sl@0: glob3(pathbuf, pathend, pathend_last, pattern, restpattern, pglob, limit) sl@0: Char *pathbuf, *pathend, *pathend_last, *pattern, *restpattern; sl@0: glob_t *pglob; sl@0: int *limit; sl@0: { sl@0: struct dirent *dp; sl@0: DIR *dirp; sl@0: int err; sl@0: char buf[MAXPATHLEN]; sl@0: sl@0: /* sl@0: * The readdirfunc declaration can't be prototyped, because it is sl@0: * assigned, below, to two functions which are prototyped in glob.h sl@0: * and dirent.h as taking pointers to differently typed opaque sl@0: * structures. sl@0: */ sl@0: struct dirent *(*readdirfunc)(); sl@0: sl@0: if (pathend > pathend_last) sl@0: return (GLOB_ABORTED); sl@0: *pathend = EOS; sl@0: errno = 0; sl@0: sl@0: if ((dirp = g_opendir(pathbuf, pglob)) == NULL) { sl@0: /* TODO: don't call for ENOENT or ENOTDIR? */ sl@0: if (pglob->gl_errfunc) { sl@0: if (g_Ctoc(pathbuf, buf, sizeof(buf))) sl@0: return (GLOB_ABORTED); sl@0: if (pglob->gl_errfunc(buf, errno) || sl@0: pglob->gl_flags & GLOB_ERR) sl@0: return (GLOB_ABORTED); sl@0: } sl@0: return(0); sl@0: } sl@0: sl@0: err = 0; sl@0: sl@0: /* Search directory for matching names. */ sl@0: if (pglob->gl_flags & GLOB_ALTDIRFUNC) sl@0: readdirfunc = pglob->gl_readdir; sl@0: else sl@0: readdirfunc = readdir; sl@0: while (NULL != (dp = (*readdirfunc)(dirp))) { sl@0: u_char *sc; sl@0: Char *dc; sl@0: wchar_t wc; sl@0: size_t clen; sl@0: mbstate_t mbs; sl@0: sl@0: /* Initial DOT must be matched literally. */ sl@0: if (dp->d_name[0] == DOT && *pattern != DOT) sl@0: continue; sl@0: memset(&mbs, 0, sizeof(mbs)); sl@0: dc = pathend; sl@0: sc = (u_char *) dp->d_name; sl@0: while (dc < pathend_last) { sl@0: clen = mbrtowc(&wc,(char*)sc, MB_LEN_MAX, &mbs); sl@0: if (clen == (size_t)-1 || clen == (size_t)-2) { sl@0: wc = *sc; sl@0: clen = 1; sl@0: memset(&mbs, 0, sizeof(mbs)); sl@0: } sl@0: if ((*dc++ = wc) == EOS) sl@0: break; sl@0: sc += clen; sl@0: } sl@0: if (!match(pathend, pattern, restpattern)) { sl@0: *pathend = EOS; sl@0: continue; sl@0: } sl@0: err = glob2(pathbuf, --dc, pathend_last, restpattern, sl@0: pglob, limit); sl@0: if (err) sl@0: break; sl@0: } sl@0: sl@0: if (pglob->gl_flags & GLOB_ALTDIRFUNC) sl@0: (*pglob->gl_closedir)(dirp); sl@0: else sl@0: closedir(dirp); sl@0: return(err); sl@0: } sl@0: sl@0: sl@0: /* sl@0: * Extend the gl_pathv member of a glob_t structure to accomodate a new item, sl@0: * add the new item, and update gl_pathc. sl@0: * sl@0: * This assumes the BSD realloc, which only copies the block when its size sl@0: * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic sl@0: * behavior. sl@0: * sl@0: * Return 0 if new item added, error code if memory couldn't be allocated. sl@0: * sl@0: * Invariant of the glob_t structure: sl@0: * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and sl@0: * gl_pathv points to (gl_offs + gl_pathc + 1) items. sl@0: */ sl@0: static int sl@0: globextend(path, pglob, limit) sl@0: const Char *path; sl@0: glob_t *pglob; sl@0: int *limit; sl@0: { sl@0: char **pathv; sl@0: int i; sl@0: u_int newsize, len; sl@0: char *copy; sl@0: const Char *p; sl@0: sl@0: if (*limit && pglob->gl_pathc > *limit) { sl@0: errno = 0; sl@0: return (GLOB_NOSPACE); sl@0: } sl@0: sl@0: newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs); sl@0: pathv = pglob->gl_pathv ? sl@0: realloc((char *)pglob->gl_pathv, newsize) : sl@0: malloc(newsize); sl@0: if (pathv == NULL) { sl@0: if (pglob->gl_pathv) { sl@0: free(pglob->gl_pathv); sl@0: pglob->gl_pathv = NULL; sl@0: } sl@0: return(GLOB_NOSPACE); sl@0: } sl@0: sl@0: if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) { sl@0: /* first time around -- clear initial gl_offs items */ sl@0: pathv += pglob->gl_offs; sl@0: for (i = pglob->gl_offs; --i >= 0; ) sl@0: *--pathv = NULL; sl@0: } sl@0: pglob->gl_pathv = pathv; sl@0: sl@0: for (p = path; *p++;) sl@0: continue; sl@0: len = MB_CUR_MAX * (size_t)(p - path); /* XXX overallocation */ sl@0: if ((copy = malloc(len)) != NULL) { sl@0: if (g_Ctoc(path, copy, len)) { sl@0: free(copy); sl@0: return (GLOB_NOSPACE); sl@0: } sl@0: pathv[pglob->gl_offs + pglob->gl_pathc++] = copy; sl@0: } sl@0: pathv[pglob->gl_offs + pglob->gl_pathc] = NULL; sl@0: return(copy == NULL ? GLOB_NOSPACE : 0); sl@0: } sl@0: sl@0: /* sl@0: * pattern matching function for filenames. Each occurrence of the * sl@0: * pattern causes a recursion level. sl@0: */ sl@0: static int sl@0: match(name, pat, patend) sl@0: Char *name, *pat, *patend; sl@0: { sl@0: int ok, negate_range; sl@0: Char c, k; sl@0: sl@0: while (pat < patend) { sl@0: c = *pat++; sl@0: switch (c & M_MASK) { sl@0: case M_ALL: sl@0: if (pat == patend) sl@0: return(1); sl@0: do sl@0: if (match(name, pat, patend)) sl@0: return(1); sl@0: while (*name++ != EOS); sl@0: return(0); sl@0: case M_ONE: sl@0: if (*name++ == EOS) sl@0: return(0); sl@0: break; sl@0: case M_SET: sl@0: ok = 0; sl@0: if ((k = *name++) == EOS) sl@0: return(0); sl@0: if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS) sl@0: ++pat; sl@0: while (((c = *pat++) & M_MASK) != M_END) sl@0: if ((*pat & M_MASK) == M_RNG) { sl@0: if ( sl@0: // As used in vfscanf.c sl@0: #ifndef __SYMBIAN32__ sl@0: __collate_load_error ? sl@0: #else sl@0: ((strcmp("C",(const char*) LC_COLLATE_LocaleName)==0) ||(strcmp("POSIX", (const char*) LC_COLLATE_LocaleName)==0 )) ? sl@0: #endif sl@0: CHAR(c) <= CHAR(k) && CHAR(k) <= CHAR(pat[1]) : sl@0: __collate_range_cmp(CHAR(c), CHAR(k)) <= 0 sl@0: && __collate_range_cmp(CHAR(k), CHAR(pat[1])) <= 0 sl@0: ) sl@0: ok = 1; sl@0: pat += 2; sl@0: } else if (c == k) sl@0: ok = 1; sl@0: if (ok == negate_range) sl@0: return(0); sl@0: break; sl@0: default: sl@0: if (*name++ != c) sl@0: return(0); sl@0: break; sl@0: } sl@0: } sl@0: return(*name == EOS); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: // EXTERNAL FUNCTION PROTOTYPES sl@0: //----------------------------------------------------------------------------- sl@0: //Function Name : void globfree(struct glob_t*) sl@0: // sl@0: //Description : Free allocated data belonging to a glob_t structure. Memory sl@0: // which was dynamically allocated storage from an earlier call to glob() sl@0: // sl@0: //Return Value : None sl@0: // sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: sl@0: EXPORT_C void sl@0: globfree(pglob) sl@0: glob_t *pglob; sl@0: { sl@0: int i; sl@0: char **pp; sl@0: sl@0: if (pglob->gl_pathv != NULL) { sl@0: pp = pglob->gl_pathv + pglob->gl_offs; sl@0: for (i = pglob->gl_pathc; i--; ++pp) sl@0: if (*pp) sl@0: free(*pp); sl@0: free(pglob->gl_pathv); sl@0: pglob->gl_pathv = NULL; sl@0: } sl@0: } sl@0: sl@0: static DIR * sl@0: g_opendir(str, pglob) sl@0: Char *str; sl@0: glob_t *pglob; sl@0: { sl@0: char buf[MAXPATHLEN]; sl@0: sl@0: if (!*str) sl@0: strcpy(buf, "."); sl@0: else { sl@0: if (g_Ctoc(str, buf, sizeof(buf))) sl@0: return (NULL); sl@0: } sl@0: sl@0: if (pglob->gl_flags & GLOB_ALTDIRFUNC) sl@0: return((*pglob->gl_opendir)(buf)); sl@0: sl@0: return(opendir(buf)); sl@0: } sl@0: sl@0: static int sl@0: g_lstat(fn, sb, pglob) sl@0: Char *fn; sl@0: struct stat *sb; sl@0: glob_t *pglob; sl@0: { sl@0: char buf[MAXPATHLEN]; sl@0: sl@0: if (g_Ctoc(fn, buf, sizeof(buf))) { sl@0: errno = ENAMETOOLONG; sl@0: return (-1); sl@0: } sl@0: if (pglob->gl_flags & GLOB_ALTDIRFUNC) sl@0: return((*pglob->gl_lstat)(buf, sb)); sl@0: return(lstat(buf, sb)); sl@0: } sl@0: sl@0: static int sl@0: g_stat(fn, sb, pglob) sl@0: Char *fn; sl@0: struct stat *sb; sl@0: glob_t *pglob; sl@0: { sl@0: char buf[MAXPATHLEN]; sl@0: sl@0: if (g_Ctoc(fn, buf, sizeof(buf))) { sl@0: errno = ENAMETOOLONG; sl@0: return (-1); sl@0: } sl@0: if (pglob->gl_flags & GLOB_ALTDIRFUNC) sl@0: return((*pglob->gl_stat)(buf, sb)); sl@0: return(stat(buf, sb)); sl@0: } sl@0: sl@0: static Char * sl@0: g_strchr(Char *str, wchar_t ch) sl@0: { sl@0: do { sl@0: if (*str == ch) sl@0: return (str); sl@0: } while (*str++); sl@0: return (NULL); sl@0: } sl@0: sl@0: static int sl@0: g_Ctoc(str, buf, len) sl@0: const Char *str; sl@0: char *buf; sl@0: u_int len; sl@0: { sl@0: mbstate_t mbs; sl@0: size_t clen; sl@0: sl@0: memset(&mbs, 0, sizeof(mbs)); sl@0: while (len >= MB_CUR_MAX) { sl@0: clen = wcrtomb(buf, *str, &mbs); sl@0: if (clen == (size_t)-1) sl@0: return (1); sl@0: if (*str == L'\0') sl@0: return (0); sl@0: str++; sl@0: buf += clen; sl@0: len -= clen; sl@0: } sl@0: return (1); sl@0: } sl@0: sl@0: #ifdef DEBUG sl@0: static void sl@0: qprintf(str, s) sl@0: const char *str; sl@0: Char *s; sl@0: { sl@0: Char *p; sl@0: sl@0: (void)printf("%s:\n", str); sl@0: for (p = s; *p; p++) sl@0: (void)printf("%c", CHAR(*p)); sl@0: (void)printf("\n"); sl@0: for (p = s; *p; p++) sl@0: (void)printf("%c", *p & M_PROTECT ? '"' : ' '); sl@0: (void)printf("\n"); sl@0: for (p = s; *p; p++) sl@0: (void)printf("%c", ismeta(*p) ? '_' : ' '); sl@0: (void)printf("\n"); sl@0: } sl@0: #endif sl@0: sl@0: #ifdef __cplusplus sl@0: } // extern "C" sl@0: #endif