143003dfeSmillert /*
243003dfeSmillert * Copyright (c) 1989, 1993
343003dfeSmillert * The Regents of the University of California. All rights reserved.
443003dfeSmillert *
543003dfeSmillert * This code is derived from software contributed to Berkeley by
643003dfeSmillert * Guido van Rossum.
743003dfeSmillert *
843003dfeSmillert * Redistribution and use in source and binary forms, with or without
943003dfeSmillert * modification, are permitted provided that the following conditions
1043003dfeSmillert * are met:
1143003dfeSmillert * 1. Redistributions of source code must retain the above copyright
1243003dfeSmillert * notice, this list of conditions and the following disclaimer.
1343003dfeSmillert * 2. Redistributions in binary form must reproduce the above copyright
1443003dfeSmillert * notice, this list of conditions and the following disclaimer in the
1543003dfeSmillert * documentation and/or other materials provided with the distribution.
1643003dfeSmillert * 3. Neither the name of the University nor the names of its contributors
1743003dfeSmillert * may be used to endorse or promote products derived from this software
1843003dfeSmillert * without specific prior written permission.
1943003dfeSmillert *
2048950c12Ssthen * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
2143003dfeSmillert * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2243003dfeSmillert * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2343003dfeSmillert * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2443003dfeSmillert * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2543003dfeSmillert * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2643003dfeSmillert * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2743003dfeSmillert * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2843003dfeSmillert * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2943003dfeSmillert * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3043003dfeSmillert * SUCH DAMAGE.
3143003dfeSmillert */
3243003dfeSmillert
3343003dfeSmillert #if defined(LIBC_SCCS) && !defined(lint)
3443003dfeSmillert static char sccsid[] = "@(#)glob.c 8.3 (Berkeley) 10/13/93";
3543003dfeSmillert /* most changes between the version above and the one below have been ported:
3643003dfeSmillert static char sscsid[]= "$OpenBSD: glob.c,v 1.8.10.1 2001/04/10 jason Exp $";
3743003dfeSmillert */
3843003dfeSmillert #endif /* LIBC_SCCS and not lint */
3943003dfeSmillert
4043003dfeSmillert /*
4143003dfeSmillert * glob(3) -- a superset of the one defined in POSIX 1003.2.
4243003dfeSmillert *
4343003dfeSmillert * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
4443003dfeSmillert *
4543003dfeSmillert * Optional extra services, controlled by flags not defined by POSIX:
4643003dfeSmillert *
4743003dfeSmillert * GLOB_QUOTE:
4843003dfeSmillert * Escaping convention: \ inhibits any special meaning the following
4943003dfeSmillert * character might have (except \ at end of string is retained).
5043003dfeSmillert * GLOB_MAGCHAR:
5143003dfeSmillert * Set in gl_flags if pattern contained a globbing character.
5243003dfeSmillert * GLOB_NOMAGIC:
5343003dfeSmillert * Same as GLOB_NOCHECK, but it will only append pattern if it did
5443003dfeSmillert * not contain any magic characters. [Used in csh style globbing]
5543003dfeSmillert * GLOB_ALTDIRFUNC:
5643003dfeSmillert * Use alternately specified directory access functions.
5743003dfeSmillert * GLOB_TILDE:
5843003dfeSmillert * expand ~user/foo to the /home/dir/of/user/foo
5943003dfeSmillert * GLOB_BRACE:
6043003dfeSmillert * expand {1,2}{a,b} to 1a 1b 2a 2b
6143003dfeSmillert * gl_matchc:
6243003dfeSmillert * Number of matches in the current invocation of glob.
6343003dfeSmillert * GLOB_ALPHASORT:
6443003dfeSmillert * sort alphabetically like csh (case doesn't matter) instead of in ASCII
6543003dfeSmillert * order
6643003dfeSmillert */
6743003dfeSmillert
6843003dfeSmillert #include <EXTERN.h>
6943003dfeSmillert #include <perl.h>
7043003dfeSmillert #include <XSUB.h>
7143003dfeSmillert
7243003dfeSmillert #include "bsd_glob.h"
7343003dfeSmillert #ifdef I_PWD
7443003dfeSmillert # include <pwd.h>
7543003dfeSmillert #else
7643003dfeSmillert #if defined(HAS_PASSWD) && !defined(VMS)
7743003dfeSmillert struct passwd *getpwnam(char *);
7843003dfeSmillert struct passwd *getpwuid(Uid_t);
7943003dfeSmillert #endif
8043003dfeSmillert #endif
8143003dfeSmillert
8243003dfeSmillert #ifndef MAXPATHLEN
8343003dfeSmillert # ifdef PATH_MAX
8443003dfeSmillert # define MAXPATHLEN PATH_MAX
8543003dfeSmillert # else
8643003dfeSmillert # define MAXPATHLEN 1024
8743003dfeSmillert # endif
8843003dfeSmillert #endif
8943003dfeSmillert
9043003dfeSmillert #include <limits.h>
9143003dfeSmillert
9243003dfeSmillert #ifndef ARG_MAX
9343003dfeSmillert # ifdef _SC_ARG_MAX
9443003dfeSmillert # define ARG_MAX (sysconf(_SC_ARG_MAX))
9543003dfeSmillert # else
9643003dfeSmillert # ifdef _POSIX_ARG_MAX
9743003dfeSmillert # define ARG_MAX _POSIX_ARG_MAX
9843003dfeSmillert # else
9943003dfeSmillert # ifdef WIN32
10043003dfeSmillert # define ARG_MAX 14500 /* from VC's limits.h */
10143003dfeSmillert # else
10243003dfeSmillert # define ARG_MAX 4096 /* from POSIX, be conservative */
10343003dfeSmillert # endif
10443003dfeSmillert # endif
10543003dfeSmillert # endif
10643003dfeSmillert #endif
10743003dfeSmillert
10843003dfeSmillert #define BG_DOLLAR '$'
10943003dfeSmillert #define BG_DOT '.'
11043003dfeSmillert #define BG_EOS '\0'
11143003dfeSmillert #define BG_LBRACKET '['
11243003dfeSmillert #define BG_NOT '!'
11343003dfeSmillert #define BG_QUESTION '?'
11443003dfeSmillert #define BG_QUOTE '\\'
11543003dfeSmillert #define BG_RANGE '-'
11643003dfeSmillert #define BG_RBRACKET ']'
11743003dfeSmillert #define BG_SEP '/'
11843003dfeSmillert #ifdef DOSISH
11943003dfeSmillert #define BG_SEP2 '\\'
12043003dfeSmillert #endif
12143003dfeSmillert #define BG_STAR '*'
12243003dfeSmillert #define BG_TILDE '~'
12343003dfeSmillert #define BG_UNDERSCORE '_'
12443003dfeSmillert #define BG_LBRACE '{'
12543003dfeSmillert #define BG_RBRACE '}'
12643003dfeSmillert #define BG_SLASH '/'
12743003dfeSmillert #define BG_COMMA ','
12843003dfeSmillert
12943003dfeSmillert #ifndef GLOB_DEBUG
13043003dfeSmillert
13143003dfeSmillert #define M_QUOTE 0x8000
13243003dfeSmillert #define M_PROTECT 0x4000
13343003dfeSmillert #define M_MASK 0xffff
13443003dfeSmillert #define M_ASCII 0x00ff
13543003dfeSmillert
13643003dfeSmillert typedef U16 Char;
13743003dfeSmillert
13843003dfeSmillert #else
13943003dfeSmillert
14043003dfeSmillert #define M_QUOTE 0x80
14143003dfeSmillert #define M_PROTECT 0x40
14243003dfeSmillert #define M_MASK 0xff
14343003dfeSmillert #define M_ASCII 0x7f
14443003dfeSmillert
14543003dfeSmillert typedef U8 Char;
14643003dfeSmillert
14743003dfeSmillert #endif /* !GLOB_DEBUG */
14843003dfeSmillert
14943003dfeSmillert
15043003dfeSmillert #define CHAR(c) ((Char)((c)&M_ASCII))
15143003dfeSmillert #define META(c) ((Char)((c)|M_QUOTE))
15243003dfeSmillert #define M_ALL META('*')
15343003dfeSmillert #define M_END META(']')
15443003dfeSmillert #define M_NOT META('!')
15543003dfeSmillert #define M_ONE META('?')
15643003dfeSmillert #define M_RNG META('-')
15743003dfeSmillert #define M_SET META('[')
15843003dfeSmillert #define ismeta(c) (((c)&M_QUOTE) != 0)
15943003dfeSmillert
16043003dfeSmillert
16143003dfeSmillert static int compare(const void *, const void *);
16243003dfeSmillert static int ci_compare(const void *, const void *);
16343003dfeSmillert static int g_Ctoc(const Char *, char *, STRLEN);
16443003dfeSmillert static int g_lstat(Char *, Stat_t *, glob_t *);
16543003dfeSmillert static DIR *g_opendir(Char *, glob_t *);
16643003dfeSmillert static Char *g_strchr(Char *, int);
16743003dfeSmillert static int g_stat(Char *, Stat_t *, glob_t *);
16843003dfeSmillert static int glob0(const Char *, glob_t *);
16943003dfeSmillert static int glob1(Char *, Char *, glob_t *, size_t *);
17043003dfeSmillert static int glob2(Char *, Char *, Char *, Char *, Char *, Char *,
17143003dfeSmillert glob_t *, size_t *);
172e5157e49Safresh1 static int glob3(Char *, Char *, Char *, Char *, Char *,
17343003dfeSmillert Char *, Char *, glob_t *, size_t *);
17443003dfeSmillert static int globextend(const Char *, glob_t *, size_t *);
17543003dfeSmillert static const Char *
17643003dfeSmillert globtilde(const Char *, Char *, size_t, glob_t *);
17743003dfeSmillert static int globexp1(const Char *, glob_t *);
17843003dfeSmillert static int globexp2(const Char *, const Char *, glob_t *, int *);
17943003dfeSmillert static int match(Char *, Char *, Char *, int);
18043003dfeSmillert #ifdef GLOB_DEBUG
18143003dfeSmillert static void qprintf(const char *, Char *);
18243003dfeSmillert #endif /* GLOB_DEBUG */
18343003dfeSmillert
184*eac174f2Safresh1 #ifdef MULTIPLICITY
18543003dfeSmillert static Direntry_t * my_readdir(DIR*);
18643003dfeSmillert
18743003dfeSmillert static Direntry_t *
my_readdir(DIR * d)18843003dfeSmillert my_readdir(DIR *d)
18943003dfeSmillert {
19043003dfeSmillert return PerlDir_read(d);
19143003dfeSmillert }
19243003dfeSmillert #else
19343003dfeSmillert
19443003dfeSmillert /* ReliantUNIX (OS formerly known as SINIX) defines readdir
19543003dfeSmillert * in LFS-mode to be a 64-bit version of readdir. */
19643003dfeSmillert
19743003dfeSmillert # ifdef sinix
19843003dfeSmillert static Direntry_t * my_readdir(DIR*);
19943003dfeSmillert
20043003dfeSmillert static Direntry_t *
my_readdir(DIR * d)20143003dfeSmillert my_readdir(DIR *d)
20243003dfeSmillert {
20343003dfeSmillert return readdir(d);
20443003dfeSmillert }
20543003dfeSmillert # else
20643003dfeSmillert
20743003dfeSmillert # define my_readdir readdir
20843003dfeSmillert
20943003dfeSmillert # endif
21043003dfeSmillert
21143003dfeSmillert #endif
21243003dfeSmillert
21343003dfeSmillert int
bsd_glob(const char * pattern,int flags,int (* errfunc)(const char *,int),glob_t * pglob)21443003dfeSmillert bsd_glob(const char *pattern, int flags,
21543003dfeSmillert int (*errfunc)(const char *, int), glob_t *pglob)
21643003dfeSmillert {
21743003dfeSmillert const U8 *patnext;
21843003dfeSmillert int c;
21943003dfeSmillert Char *bufnext, *bufend, patbuf[MAXPATHLEN];
22043003dfeSmillert patnext = (U8 *) pattern;
22143003dfeSmillert /* TODO: GLOB_APPEND / GLOB_DOOFFS aren't supported yet */
22243003dfeSmillert #if 0
22343003dfeSmillert if (!(flags & GLOB_APPEND)) {
22443003dfeSmillert pglob->gl_pathc = 0;
22543003dfeSmillert pglob->gl_pathv = NULL;
22643003dfeSmillert if (!(flags & GLOB_DOOFFS))
22743003dfeSmillert pglob->gl_offs = 0;
22843003dfeSmillert }
22943003dfeSmillert #else
23043003dfeSmillert pglob->gl_pathc = 0;
23143003dfeSmillert pglob->gl_pathv = NULL;
23243003dfeSmillert pglob->gl_offs = 0;
23343003dfeSmillert #endif
23443003dfeSmillert pglob->gl_flags = flags & ~GLOB_MAGCHAR;
23543003dfeSmillert pglob->gl_errfunc = errfunc;
23643003dfeSmillert pglob->gl_matchc = 0;
23743003dfeSmillert
23843003dfeSmillert bufnext = patbuf;
23943003dfeSmillert bufend = bufnext + MAXPATHLEN - 1;
24043003dfeSmillert #ifdef DOSISH
24143003dfeSmillert /* Nasty hack to treat patterns like "C:*" correctly. In this
24243003dfeSmillert * case, the * should match any file in the current directory
24343003dfeSmillert * on the C: drive. However, the glob code does not treat the
24443003dfeSmillert * colon specially, so it looks for files beginning "C:" in
24543003dfeSmillert * the current directory. To fix this, change the pattern to
24643003dfeSmillert * add an explicit "./" at the start (just after the drive
24743003dfeSmillert * letter and colon - ie change to "C:./").
24843003dfeSmillert */
24943003dfeSmillert if (isalpha(pattern[0]) && pattern[1] == ':' &&
25043003dfeSmillert pattern[2] != BG_SEP && pattern[2] != BG_SEP2 &&
25143003dfeSmillert bufend - bufnext > 4) {
25243003dfeSmillert *bufnext++ = pattern[0];
25343003dfeSmillert *bufnext++ = ':';
25443003dfeSmillert *bufnext++ = '.';
25543003dfeSmillert *bufnext++ = BG_SEP;
25643003dfeSmillert patnext += 2;
25743003dfeSmillert }
25843003dfeSmillert #endif
25943003dfeSmillert
26043003dfeSmillert if (flags & GLOB_QUOTE) {
26143003dfeSmillert /* Protect the quoted characters. */
26243003dfeSmillert while (bufnext < bufend && (c = *patnext++) != BG_EOS)
26343003dfeSmillert if (c == BG_QUOTE) {
26443003dfeSmillert #ifdef DOSISH
26543003dfeSmillert /* To avoid backslashitis on Win32,
26643003dfeSmillert * we only treat \ as a quoting character
26743003dfeSmillert * if it precedes one of the
26843003dfeSmillert * metacharacters []-{}~\
26943003dfeSmillert */
27043003dfeSmillert if ((c = *patnext++) != '[' && c != ']' &&
27143003dfeSmillert c != '-' && c != '{' && c != '}' &&
27243003dfeSmillert c != '~' && c != '\\') {
27343003dfeSmillert #else
27443003dfeSmillert if ((c = *patnext++) == BG_EOS) {
27543003dfeSmillert #endif
27643003dfeSmillert c = BG_QUOTE;
27743003dfeSmillert --patnext;
27843003dfeSmillert }
27943003dfeSmillert *bufnext++ = c | M_PROTECT;
28043003dfeSmillert } else
28143003dfeSmillert *bufnext++ = c;
28243003dfeSmillert } else
28343003dfeSmillert while (bufnext < bufend && (c = *patnext++) != BG_EOS)
28443003dfeSmillert *bufnext++ = c;
28543003dfeSmillert *bufnext = BG_EOS;
28643003dfeSmillert
28743003dfeSmillert if (flags & GLOB_BRACE)
28843003dfeSmillert return globexp1(patbuf, pglob);
28943003dfeSmillert else
29043003dfeSmillert return glob0(patbuf, pglob);
29143003dfeSmillert }
29243003dfeSmillert
29343003dfeSmillert /*
29443003dfeSmillert * Expand recursively a glob {} pattern. When there is no more expansion
29543003dfeSmillert * invoke the standard globbing routine to glob the rest of the magic
29643003dfeSmillert * characters
29743003dfeSmillert */
29843003dfeSmillert static int
29943003dfeSmillert globexp1(const Char *pattern, glob_t *pglob)
30043003dfeSmillert {
30143003dfeSmillert const Char* ptr = pattern;
30243003dfeSmillert int rv;
30343003dfeSmillert
30443003dfeSmillert /* Protect a single {}, for find(1), like csh */
30543003dfeSmillert if (pattern[0] == BG_LBRACE && pattern[1] == BG_RBRACE && pattern[2] == BG_EOS)
30643003dfeSmillert return glob0(pattern, pglob);
30743003dfeSmillert
30843003dfeSmillert while ((ptr = (const Char *) g_strchr((Char *) ptr, BG_LBRACE)) != NULL)
30943003dfeSmillert if (!globexp2(ptr, pattern, pglob, &rv))
31043003dfeSmillert return rv;
31143003dfeSmillert
31243003dfeSmillert return glob0(pattern, pglob);
31343003dfeSmillert }
31443003dfeSmillert
31543003dfeSmillert
31643003dfeSmillert /*
31743003dfeSmillert * Recursive brace globbing helper. Tries to expand a single brace.
31843003dfeSmillert * If it succeeds then it invokes globexp1 with the new pattern.
31943003dfeSmillert * If it fails then it tries to glob the rest of the pattern and returns.
32043003dfeSmillert */
32143003dfeSmillert static int
32243003dfeSmillert globexp2(const Char *ptr, const Char *pattern,
32343003dfeSmillert glob_t *pglob, int *rv)
32443003dfeSmillert {
32543003dfeSmillert int i;
32643003dfeSmillert Char *lm, *ls;
32743003dfeSmillert const Char *pe, *pm, *pm1, *pl;
32843003dfeSmillert Char patbuf[MAXPATHLEN];
32943003dfeSmillert
33043003dfeSmillert /* copy part up to the brace */
33143003dfeSmillert for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++)
33243003dfeSmillert ;
33343003dfeSmillert *lm = BG_EOS;
33443003dfeSmillert ls = lm;
33543003dfeSmillert
33643003dfeSmillert /* Find the balanced brace */
33743003dfeSmillert for (i = 0, pe = ++ptr; *pe; pe++)
33843003dfeSmillert if (*pe == BG_LBRACKET) {
33943003dfeSmillert /* Ignore everything between [] */
34043003dfeSmillert for (pm = pe++; *pe != BG_RBRACKET && *pe != BG_EOS; pe++)
34143003dfeSmillert ;
34243003dfeSmillert if (*pe == BG_EOS) {
34343003dfeSmillert /*
34443003dfeSmillert * We could not find a matching BG_RBRACKET.
34543003dfeSmillert * Ignore and just look for BG_RBRACE
34643003dfeSmillert */
34743003dfeSmillert pe = pm;
34843003dfeSmillert }
34943003dfeSmillert } else if (*pe == BG_LBRACE)
35043003dfeSmillert i++;
35143003dfeSmillert else if (*pe == BG_RBRACE) {
35243003dfeSmillert if (i == 0)
35343003dfeSmillert break;
35443003dfeSmillert i--;
35543003dfeSmillert }
35643003dfeSmillert
35743003dfeSmillert /* Non matching braces; just glob the pattern */
35843003dfeSmillert if (i != 0 || *pe == BG_EOS) {
35943003dfeSmillert *rv = glob0(patbuf, pglob);
36043003dfeSmillert return 0;
36143003dfeSmillert }
36243003dfeSmillert
36343003dfeSmillert for (i = 0, pl = pm = ptr; pm <= pe; pm++) {
36443003dfeSmillert switch (*pm) {
36543003dfeSmillert case BG_LBRACKET:
36643003dfeSmillert /* Ignore everything between [] */
36743003dfeSmillert for (pm1 = pm++; *pm != BG_RBRACKET && *pm != BG_EOS; pm++)
36843003dfeSmillert ;
36943003dfeSmillert if (*pm == BG_EOS) {
37043003dfeSmillert /*
37143003dfeSmillert * We could not find a matching BG_RBRACKET.
37243003dfeSmillert * Ignore and just look for BG_RBRACE
37343003dfeSmillert */
37443003dfeSmillert pm = pm1;
37543003dfeSmillert }
37643003dfeSmillert break;
37743003dfeSmillert
37843003dfeSmillert case BG_LBRACE:
37943003dfeSmillert i++;
38043003dfeSmillert break;
38143003dfeSmillert
38243003dfeSmillert case BG_RBRACE:
38343003dfeSmillert if (i) {
38443003dfeSmillert i--;
38543003dfeSmillert break;
38643003dfeSmillert }
38743003dfeSmillert /* FALLTHROUGH */
38843003dfeSmillert case BG_COMMA:
38943003dfeSmillert if (i && *pm == BG_COMMA)
39043003dfeSmillert break;
39143003dfeSmillert else {
39243003dfeSmillert /* Append the current string */
39343003dfeSmillert for (lm = ls; (pl < pm); *lm++ = *pl++)
39443003dfeSmillert ;
39543003dfeSmillert
39643003dfeSmillert /*
39743003dfeSmillert * Append the rest of the pattern after the
39843003dfeSmillert * closing brace
39943003dfeSmillert */
40043003dfeSmillert for (pl = pe + 1; (*lm++ = *pl++) != BG_EOS; )
40143003dfeSmillert ;
40243003dfeSmillert
40343003dfeSmillert /* Expand the current pattern */
40443003dfeSmillert #ifdef GLOB_DEBUG
40543003dfeSmillert qprintf("globexp2:", patbuf);
40643003dfeSmillert #endif /* GLOB_DEBUG */
40743003dfeSmillert *rv = globexp1(patbuf, pglob);
40843003dfeSmillert
40943003dfeSmillert /* move after the comma, to the next string */
41043003dfeSmillert pl = pm + 1;
41143003dfeSmillert }
41243003dfeSmillert break;
41343003dfeSmillert
41443003dfeSmillert default:
41543003dfeSmillert break;
41643003dfeSmillert }
41743003dfeSmillert }
41843003dfeSmillert *rv = 0;
41943003dfeSmillert return 0;
42043003dfeSmillert }
42143003dfeSmillert
42243003dfeSmillert
42343003dfeSmillert
42443003dfeSmillert /*
42543003dfeSmillert * expand tilde from the passwd file.
42643003dfeSmillert */
42743003dfeSmillert static const Char *
42843003dfeSmillert globtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t *pglob)
42943003dfeSmillert {
43043003dfeSmillert char *h;
43143003dfeSmillert const Char *p;
43243003dfeSmillert Char *b, *eb;
43343003dfeSmillert
43443003dfeSmillert if (*pattern != BG_TILDE || !(pglob->gl_flags & GLOB_TILDE))
43543003dfeSmillert return pattern;
43643003dfeSmillert
43743003dfeSmillert /* Copy up to the end of the string or / */
43843003dfeSmillert eb = &patbuf[patbuf_len - 1];
43943003dfeSmillert for (p = pattern + 1, h = (char *) patbuf;
44043003dfeSmillert h < (char*)eb && *p && *p != BG_SLASH; *h++ = (char)*p++)
44143003dfeSmillert ;
44243003dfeSmillert
44343003dfeSmillert *h = BG_EOS;
44443003dfeSmillert
44543003dfeSmillert #if 0
44643003dfeSmillert if (h == (char *)eb)
44743003dfeSmillert return what;
44843003dfeSmillert #endif
44943003dfeSmillert
45043003dfeSmillert if (((char *) patbuf)[0] == BG_EOS) {
45143003dfeSmillert /*
45243003dfeSmillert * handle a plain ~ or ~/ by expanding $HOME
45343003dfeSmillert * first and then trying the password file
45448950c12Ssthen * or $USERPROFILE on DOSISH systems
45543003dfeSmillert */
45656d68f1eSafresh1 if ((h = PerlEnv_getenv("HOME")) == NULL) {
45743003dfeSmillert #ifdef HAS_PASSWD
45843003dfeSmillert struct passwd *pwd;
45943003dfeSmillert if ((pwd = getpwuid(getuid())) == NULL)
46043003dfeSmillert return pattern;
46143003dfeSmillert else
46243003dfeSmillert h = pwd->pw_dir;
46348950c12Ssthen #elif DOSISH
46448950c12Ssthen /*
46548950c12Ssthen * When no passwd file, fallback to the USERPROFILE
46648950c12Ssthen * environment variable on DOSish systems.
46748950c12Ssthen */
46856d68f1eSafresh1 if ((h = PerlEnv_getenv("USERPROFILE")) == NULL) {
46948950c12Ssthen return pattern;
47048950c12Ssthen }
47143003dfeSmillert #else
47243003dfeSmillert return pattern;
47343003dfeSmillert #endif
47443003dfeSmillert }
47543003dfeSmillert } else {
47643003dfeSmillert /*
47743003dfeSmillert * Expand a ~user
47843003dfeSmillert */
47943003dfeSmillert #ifdef HAS_PASSWD
48043003dfeSmillert struct passwd *pwd;
48143003dfeSmillert if ((pwd = getpwnam((char*) patbuf)) == NULL)
48243003dfeSmillert return pattern;
48343003dfeSmillert else
48443003dfeSmillert h = pwd->pw_dir;
48543003dfeSmillert #else
48643003dfeSmillert return pattern;
48743003dfeSmillert #endif
48843003dfeSmillert }
48943003dfeSmillert
49043003dfeSmillert /* Copy the home directory */
49143003dfeSmillert for (b = patbuf; b < eb && *h; *b++ = *h++)
49243003dfeSmillert ;
49343003dfeSmillert
49443003dfeSmillert /* Append the rest of the pattern */
49543003dfeSmillert while (b < eb && (*b++ = *p++) != BG_EOS)
49643003dfeSmillert ;
49743003dfeSmillert *b = BG_EOS;
49843003dfeSmillert
49943003dfeSmillert return patbuf;
50043003dfeSmillert }
50143003dfeSmillert
50243003dfeSmillert
50343003dfeSmillert /*
50443003dfeSmillert * The main glob() routine: compiles the pattern (optionally processing
50543003dfeSmillert * quotes), calls glob1() to do the real pattern matching, and finally
50643003dfeSmillert * sorts the list (unless unsorted operation is requested). Returns 0
50743003dfeSmillert * if things went well, nonzero if errors occurred. It is not an error
50843003dfeSmillert * to find no matches.
50943003dfeSmillert */
51043003dfeSmillert static int
51143003dfeSmillert glob0(const Char *pattern, glob_t *pglob)
51243003dfeSmillert {
51343003dfeSmillert const Char *qpat, *qpatnext;
51443003dfeSmillert int c, err, oldflags, oldpathc;
51543003dfeSmillert Char *bufnext, patbuf[MAXPATHLEN];
51643003dfeSmillert size_t limit = 0;
51743003dfeSmillert
51843003dfeSmillert qpat = globtilde(pattern, patbuf, MAXPATHLEN, pglob);
51943003dfeSmillert qpatnext = qpat;
52043003dfeSmillert oldflags = pglob->gl_flags;
52143003dfeSmillert oldpathc = pglob->gl_pathc;
52243003dfeSmillert bufnext = patbuf;
52343003dfeSmillert
52443003dfeSmillert /* We don't need to check for buffer overflow any more. */
52543003dfeSmillert while ((c = *qpatnext++) != BG_EOS) {
52643003dfeSmillert switch (c) {
52743003dfeSmillert case BG_LBRACKET:
52843003dfeSmillert c = *qpatnext;
52943003dfeSmillert if (c == BG_NOT)
53043003dfeSmillert ++qpatnext;
53143003dfeSmillert if (*qpatnext == BG_EOS ||
53243003dfeSmillert g_strchr((Char *) qpatnext+1, BG_RBRACKET) == NULL) {
53343003dfeSmillert *bufnext++ = BG_LBRACKET;
53443003dfeSmillert if (c == BG_NOT)
53543003dfeSmillert --qpatnext;
53643003dfeSmillert break;
53743003dfeSmillert }
53843003dfeSmillert *bufnext++ = M_SET;
53943003dfeSmillert if (c == BG_NOT)
54043003dfeSmillert *bufnext++ = M_NOT;
54143003dfeSmillert c = *qpatnext++;
54243003dfeSmillert do {
54343003dfeSmillert *bufnext++ = CHAR(c);
54443003dfeSmillert if (*qpatnext == BG_RANGE &&
54543003dfeSmillert (c = qpatnext[1]) != BG_RBRACKET) {
54643003dfeSmillert *bufnext++ = M_RNG;
54743003dfeSmillert *bufnext++ = CHAR(c);
54843003dfeSmillert qpatnext += 2;
54943003dfeSmillert }
55043003dfeSmillert } while ((c = *qpatnext++) != BG_RBRACKET);
55143003dfeSmillert pglob->gl_flags |= GLOB_MAGCHAR;
55243003dfeSmillert *bufnext++ = M_END;
55343003dfeSmillert break;
55443003dfeSmillert case BG_QUESTION:
55543003dfeSmillert pglob->gl_flags |= GLOB_MAGCHAR;
55643003dfeSmillert *bufnext++ = M_ONE;
55743003dfeSmillert break;
55843003dfeSmillert case BG_STAR:
55943003dfeSmillert pglob->gl_flags |= GLOB_MAGCHAR;
5609f11ffb7Safresh1 /* Collapse adjacent stars to one.
5619f11ffb7Safresh1 * This is required to ensure that a pattern like
5629f11ffb7Safresh1 * "a**" matches a name like "a", as without this
5639f11ffb7Safresh1 * check when the first star matched everything it would
5649f11ffb7Safresh1 * cause the second star to return a match fail.
5659f11ffb7Safresh1 * As long ** is folded here this does not happen.
56643003dfeSmillert */
56743003dfeSmillert if (bufnext == patbuf || bufnext[-1] != M_ALL)
56843003dfeSmillert *bufnext++ = M_ALL;
56943003dfeSmillert break;
57043003dfeSmillert default:
57143003dfeSmillert *bufnext++ = CHAR(c);
57243003dfeSmillert break;
57343003dfeSmillert }
57443003dfeSmillert }
57543003dfeSmillert *bufnext = BG_EOS;
57643003dfeSmillert #ifdef GLOB_DEBUG
57743003dfeSmillert qprintf("glob0:", patbuf);
57843003dfeSmillert #endif /* GLOB_DEBUG */
57943003dfeSmillert
58043003dfeSmillert if ((err = glob1(patbuf, patbuf+MAXPATHLEN-1, pglob, &limit)) != 0) {
58143003dfeSmillert pglob->gl_flags = oldflags;
58243003dfeSmillert return(err);
58343003dfeSmillert }
58443003dfeSmillert
58543003dfeSmillert /*
58643003dfeSmillert * If there was no match we are going to append the pattern
58743003dfeSmillert * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
58843003dfeSmillert * and the pattern did not contain any magic characters
58943003dfeSmillert * GLOB_NOMAGIC is there just for compatibility with csh.
59043003dfeSmillert */
59143003dfeSmillert if (pglob->gl_pathc == oldpathc &&
59243003dfeSmillert ((pglob->gl_flags & GLOB_NOCHECK) ||
59343003dfeSmillert ((pglob->gl_flags & GLOB_NOMAGIC) &&
59443003dfeSmillert !(pglob->gl_flags & GLOB_MAGCHAR))))
59543003dfeSmillert {
59643003dfeSmillert #ifdef GLOB_DEBUG
59743003dfeSmillert printf("calling globextend from glob0\n");
59843003dfeSmillert #endif /* GLOB_DEBUG */
59943003dfeSmillert pglob->gl_flags = oldflags;
60043003dfeSmillert return(globextend(qpat, pglob, &limit));
60143003dfeSmillert }
60243003dfeSmillert else if (!(pglob->gl_flags & GLOB_NOSORT))
603b8851fccSafresh1 if (pglob->gl_pathv)
60443003dfeSmillert qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
60543003dfeSmillert pglob->gl_pathc - oldpathc, sizeof(char *),
60643003dfeSmillert (pglob->gl_flags & (GLOB_ALPHASORT|GLOB_NOCASE))
60743003dfeSmillert ? ci_compare : compare);
60843003dfeSmillert pglob->gl_flags = oldflags;
60943003dfeSmillert return(0);
61043003dfeSmillert }
61143003dfeSmillert
61243003dfeSmillert static int
61343003dfeSmillert ci_compare(const void *p, const void *q)
61443003dfeSmillert {
61543003dfeSmillert const char *pp = *(const char **)p;
61643003dfeSmillert const char *qq = *(const char **)q;
61743003dfeSmillert int ci;
61843003dfeSmillert while (*pp && *qq) {
619e5157e49Safresh1 if (toFOLD(*pp) != toFOLD(*qq))
62043003dfeSmillert break;
62143003dfeSmillert ++pp;
62243003dfeSmillert ++qq;
62343003dfeSmillert }
624e5157e49Safresh1 ci = toFOLD(*pp) - toFOLD(*qq);
62543003dfeSmillert if (ci == 0)
62643003dfeSmillert return compare(p, q);
62743003dfeSmillert return ci;
62843003dfeSmillert }
62943003dfeSmillert
63043003dfeSmillert static int
63143003dfeSmillert compare(const void *p, const void *q)
63243003dfeSmillert {
63343003dfeSmillert return(strcmp(*(char **)p, *(char **)q));
63443003dfeSmillert }
63543003dfeSmillert
63643003dfeSmillert static int
63743003dfeSmillert glob1(Char *pattern, Char *pattern_last, glob_t *pglob, size_t *limitp)
63843003dfeSmillert {
63943003dfeSmillert Char pathbuf[MAXPATHLEN];
64043003dfeSmillert
641e5157e49Safresh1 assert(pattern < pattern_last);
642e5157e49Safresh1
64343003dfeSmillert /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
64443003dfeSmillert if (*pattern == BG_EOS)
64543003dfeSmillert return(0);
64643003dfeSmillert return(glob2(pathbuf, pathbuf+MAXPATHLEN-1,
64743003dfeSmillert pathbuf, pathbuf+MAXPATHLEN-1,
64843003dfeSmillert pattern, pattern_last, pglob, limitp));
64943003dfeSmillert }
65043003dfeSmillert
65143003dfeSmillert /*
65243003dfeSmillert * The functions glob2 and glob3 are mutually recursive; there is one level
65343003dfeSmillert * of recursion for each segment in the pattern that contains one or more
65443003dfeSmillert * meta characters.
65543003dfeSmillert */
65643003dfeSmillert static int
65743003dfeSmillert glob2(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last,
65843003dfeSmillert Char *pattern, Char *pattern_last, glob_t *pglob, size_t *limitp)
65943003dfeSmillert {
66043003dfeSmillert Stat_t sb;
66143003dfeSmillert Char *p, *q;
66243003dfeSmillert int anymeta;
66343003dfeSmillert
664e5157e49Safresh1 assert(pattern < pattern_last);
665e5157e49Safresh1
66643003dfeSmillert /*
66743003dfeSmillert * Loop over pattern segments until end of pattern or until
66843003dfeSmillert * segment with meta character found.
66943003dfeSmillert */
67043003dfeSmillert for (anymeta = 0;;) {
67143003dfeSmillert if (*pattern == BG_EOS) { /* End of pattern? */
67243003dfeSmillert *pathend = BG_EOS;
67343003dfeSmillert if (g_lstat(pathbuf, &sb, pglob))
67443003dfeSmillert return(0);
67543003dfeSmillert
67643003dfeSmillert if (((pglob->gl_flags & GLOB_MARK) &&
67743003dfeSmillert pathend[-1] != BG_SEP
67843003dfeSmillert #ifdef DOSISH
67943003dfeSmillert && pathend[-1] != BG_SEP2
68043003dfeSmillert #endif
68143003dfeSmillert ) && (S_ISDIR(sb.st_mode) ||
68243003dfeSmillert (S_ISLNK(sb.st_mode) &&
68343003dfeSmillert (g_stat(pathbuf, &sb, pglob) == 0) &&
68443003dfeSmillert S_ISDIR(sb.st_mode)))) {
68543003dfeSmillert if (pathend+1 > pathend_last)
68643003dfeSmillert return (1);
68743003dfeSmillert *pathend++ = BG_SEP;
68843003dfeSmillert *pathend = BG_EOS;
68943003dfeSmillert }
69043003dfeSmillert ++pglob->gl_matchc;
69143003dfeSmillert #ifdef GLOB_DEBUG
69243003dfeSmillert printf("calling globextend from glob2\n");
69343003dfeSmillert #endif /* GLOB_DEBUG */
69443003dfeSmillert return(globextend(pathbuf, pglob, limitp));
69543003dfeSmillert }
69643003dfeSmillert
69743003dfeSmillert /* Find end of next segment, copy tentatively to pathend. */
69843003dfeSmillert q = pathend;
69943003dfeSmillert p = pattern;
70043003dfeSmillert while (*p != BG_EOS && *p != BG_SEP
70143003dfeSmillert #ifdef DOSISH
70243003dfeSmillert && *p != BG_SEP2
70343003dfeSmillert #endif
70443003dfeSmillert ) {
705e5157e49Safresh1 assert(p < pattern_last);
70643003dfeSmillert if (ismeta(*p))
70743003dfeSmillert anymeta = 1;
70843003dfeSmillert if (q+1 > pathend_last)
70943003dfeSmillert return (1);
71043003dfeSmillert *q++ = *p++;
71143003dfeSmillert }
71243003dfeSmillert
71343003dfeSmillert if (!anymeta) { /* No expansion, do next segment. */
71443003dfeSmillert pathend = q;
71543003dfeSmillert pattern = p;
71643003dfeSmillert while (*pattern == BG_SEP
71743003dfeSmillert #ifdef DOSISH
71843003dfeSmillert || *pattern == BG_SEP2
71943003dfeSmillert #endif
72043003dfeSmillert ) {
721e5157e49Safresh1 assert(p < pattern_last);
72243003dfeSmillert if (pathend+1 > pathend_last)
72343003dfeSmillert return (1);
72443003dfeSmillert *pathend++ = *pattern++;
72543003dfeSmillert }
72643003dfeSmillert } else
72743003dfeSmillert /* Need expansion, recurse. */
72843003dfeSmillert return(glob3(pathbuf, pathbuf_last, pathend,
729e5157e49Safresh1 pathend_last, pattern,
73043003dfeSmillert p, pattern_last, pglob, limitp));
73143003dfeSmillert }
73243003dfeSmillert /* NOTREACHED */
73343003dfeSmillert }
73443003dfeSmillert
73543003dfeSmillert static int
73643003dfeSmillert glob3(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last,
737e5157e49Safresh1 Char *pattern,
73843003dfeSmillert Char *restpattern, Char *restpattern_last, glob_t *pglob, size_t *limitp)
73943003dfeSmillert {
740e9ce3842Safresh1 Direntry_t *dp;
74143003dfeSmillert DIR *dirp;
74243003dfeSmillert int err;
74343003dfeSmillert int nocase;
74443003dfeSmillert char buf[MAXPATHLEN];
74543003dfeSmillert
74643003dfeSmillert /*
74743003dfeSmillert * The readdirfunc declaration can't be prototyped, because it is
74843003dfeSmillert * assigned, below, to two functions which are prototyped in glob.h
74943003dfeSmillert * and dirent.h as taking pointers to differently typed opaque
75043003dfeSmillert * structures.
75143003dfeSmillert */
75243003dfeSmillert Direntry_t *(*readdirfunc)(DIR*);
75343003dfeSmillert
754e5157e49Safresh1 assert(pattern < restpattern_last);
755e5157e49Safresh1 assert(restpattern < restpattern_last);
756e5157e49Safresh1
75743003dfeSmillert if (pathend > pathend_last)
75843003dfeSmillert return (1);
75943003dfeSmillert *pathend = BG_EOS;
76043003dfeSmillert errno = 0;
76143003dfeSmillert
76243003dfeSmillert #ifdef VMS
76343003dfeSmillert {
76443003dfeSmillert Char *q = pathend;
76543003dfeSmillert if (q - pathbuf > 5) {
76643003dfeSmillert q -= 5;
76743003dfeSmillert if (q[0] == '.' &&
76843003dfeSmillert tolower(q[1]) == 'd' && tolower(q[2]) == 'i' &&
76943003dfeSmillert tolower(q[3]) == 'r' && q[4] == '/')
77043003dfeSmillert {
77143003dfeSmillert q[0] = '/';
77243003dfeSmillert q[1] = BG_EOS;
77343003dfeSmillert pathend = q+1;
77443003dfeSmillert }
77543003dfeSmillert }
77643003dfeSmillert }
77743003dfeSmillert #endif
77843003dfeSmillert
77943003dfeSmillert if ((dirp = g_opendir(pathbuf, pglob)) == NULL) {
78043003dfeSmillert /* TODO: don't call for ENOENT or ENOTDIR? */
78143003dfeSmillert if (pglob->gl_errfunc) {
78243003dfeSmillert if (g_Ctoc(pathbuf, buf, sizeof(buf)))
78343003dfeSmillert return (GLOB_ABEND);
78443003dfeSmillert if (pglob->gl_errfunc(buf, errno) ||
78543003dfeSmillert (pglob->gl_flags & GLOB_ERR))
78643003dfeSmillert return (GLOB_ABEND);
78743003dfeSmillert }
78843003dfeSmillert return(0);
78943003dfeSmillert }
79043003dfeSmillert
79143003dfeSmillert err = 0;
79243003dfeSmillert nocase = ((pglob->gl_flags & GLOB_NOCASE) != 0);
79343003dfeSmillert
79443003dfeSmillert /* Search directory for matching names. */
79543003dfeSmillert if (pglob->gl_flags & GLOB_ALTDIRFUNC)
79643003dfeSmillert readdirfunc = (Direntry_t *(*)(DIR *))pglob->gl_readdir;
79743003dfeSmillert else
79843003dfeSmillert readdirfunc = (Direntry_t *(*)(DIR *))my_readdir;
79943003dfeSmillert while ((dp = (*readdirfunc)(dirp))) {
800e9ce3842Safresh1 U8 *sc;
801e9ce3842Safresh1 Char *dc;
80243003dfeSmillert
80343003dfeSmillert /* Initial BG_DOT must be matched literally. */
80443003dfeSmillert if (dp->d_name[0] == BG_DOT && *pattern != BG_DOT)
80543003dfeSmillert continue;
80643003dfeSmillert dc = pathend;
80743003dfeSmillert sc = (U8 *) dp->d_name;
80843003dfeSmillert while (dc < pathend_last && (*dc++ = *sc++) != BG_EOS)
80943003dfeSmillert ;
81043003dfeSmillert if (dc >= pathend_last) {
81143003dfeSmillert *dc = BG_EOS;
81243003dfeSmillert err = 1;
81343003dfeSmillert break;
81443003dfeSmillert }
81543003dfeSmillert
81643003dfeSmillert if (!match(pathend, pattern, restpattern, nocase)) {
81743003dfeSmillert *pathend = BG_EOS;
81843003dfeSmillert continue;
81943003dfeSmillert }
82043003dfeSmillert err = glob2(pathbuf, pathbuf_last, --dc, pathend_last,
82143003dfeSmillert restpattern, restpattern_last, pglob, limitp);
82243003dfeSmillert if (err)
82343003dfeSmillert break;
82443003dfeSmillert }
82543003dfeSmillert
82643003dfeSmillert if (pglob->gl_flags & GLOB_ALTDIRFUNC)
82743003dfeSmillert (*pglob->gl_closedir)(dirp);
82843003dfeSmillert else
82943003dfeSmillert PerlDir_close(dirp);
83043003dfeSmillert return(err);
83143003dfeSmillert }
83243003dfeSmillert
83343003dfeSmillert
83443003dfeSmillert /*
83548950c12Ssthen * Extend the gl_pathv member of a glob_t structure to accommodate a new item,
83643003dfeSmillert * add the new item, and update gl_pathc.
83743003dfeSmillert *
83843003dfeSmillert * This assumes the BSD realloc, which only copies the block when its size
83943003dfeSmillert * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
84043003dfeSmillert * behavior.
84143003dfeSmillert *
84243003dfeSmillert * Return 0 if new item added, error code if memory couldn't be allocated.
84343003dfeSmillert *
84443003dfeSmillert * Invariant of the glob_t structure:
84543003dfeSmillert * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
84643003dfeSmillert * gl_pathv points to (gl_offs + gl_pathc + 1) items.
84743003dfeSmillert */
84843003dfeSmillert static int
84943003dfeSmillert globextend(const Char *path, glob_t *pglob, size_t *limitp)
85043003dfeSmillert {
851e9ce3842Safresh1 char **pathv;
852e9ce3842Safresh1 int i;
85343003dfeSmillert STRLEN newsize, len;
85443003dfeSmillert char *copy;
85543003dfeSmillert const Char *p;
85643003dfeSmillert
85743003dfeSmillert #ifdef GLOB_DEBUG
85843003dfeSmillert printf("Adding ");
85943003dfeSmillert for (p = path; *p; p++)
86043003dfeSmillert (void)printf("%c", CHAR(*p));
86143003dfeSmillert printf("\n");
86243003dfeSmillert #endif /* GLOB_DEBUG */
86343003dfeSmillert
86443003dfeSmillert newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
86543003dfeSmillert if (pglob->gl_pathv)
86643003dfeSmillert pathv = Renew(pglob->gl_pathv,newsize,char*);
86743003dfeSmillert else
86843003dfeSmillert Newx(pathv,newsize,char*);
86943003dfeSmillert if (pathv == NULL) {
87043003dfeSmillert if (pglob->gl_pathv) {
87143003dfeSmillert Safefree(pglob->gl_pathv);
87243003dfeSmillert pglob->gl_pathv = NULL;
87343003dfeSmillert }
87443003dfeSmillert return(GLOB_NOSPACE);
87543003dfeSmillert }
87643003dfeSmillert
87743003dfeSmillert if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
87843003dfeSmillert /* first time around -- clear initial gl_offs items */
87943003dfeSmillert pathv += pglob->gl_offs;
88043003dfeSmillert for (i = pglob->gl_offs; --i >= 0; )
88143003dfeSmillert *--pathv = NULL;
88243003dfeSmillert }
88343003dfeSmillert pglob->gl_pathv = pathv;
88443003dfeSmillert
88543003dfeSmillert for (p = path; *p++;)
88643003dfeSmillert ;
88743003dfeSmillert len = (STRLEN)(p - path);
88843003dfeSmillert *limitp += len;
88943003dfeSmillert Newx(copy, p-path, char);
89043003dfeSmillert if (copy != NULL) {
89143003dfeSmillert if (g_Ctoc(path, copy, len)) {
89243003dfeSmillert Safefree(copy);
89343003dfeSmillert return(GLOB_NOSPACE);
89443003dfeSmillert }
89543003dfeSmillert pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
89643003dfeSmillert }
89743003dfeSmillert pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
89843003dfeSmillert
89943003dfeSmillert if ((pglob->gl_flags & GLOB_LIMIT) &&
900e5157e49Safresh1 newsize + *limitp >= (unsigned long)ARG_MAX) {
90143003dfeSmillert errno = 0;
90243003dfeSmillert return(GLOB_NOSPACE);
90343003dfeSmillert }
90443003dfeSmillert
90543003dfeSmillert return(copy == NULL ? GLOB_NOSPACE : 0);
90643003dfeSmillert }
90743003dfeSmillert
90843003dfeSmillert
90943003dfeSmillert /*
9109f11ffb7Safresh1 * pattern matching function for filenames using state machine to avoid
9119f11ffb7Safresh1 * recursion. We maintain a "nextp" and "nextn" to allow us to backtrack
9129f11ffb7Safresh1 * without additional callframes, and to do cleanly prune the backtracking
9139f11ffb7Safresh1 * state when multiple '*' (start) matches are included in the pattern.
9149f11ffb7Safresh1 *
9159f11ffb7Safresh1 * Thanks to Russ Cox for the improved state machine logic to avoid quadratic
9169f11ffb7Safresh1 * matching on failure.
9179f11ffb7Safresh1 *
9189f11ffb7Safresh1 * https://research.swtch.com/glob
9199f11ffb7Safresh1 *
9209f11ffb7Safresh1 * An example would be a pattern
9219f11ffb7Safresh1 * ("a*" x 100) . "y"
9229f11ffb7Safresh1 * against a file name like
9239f11ffb7Safresh1 * ("a" x 100) . "x"
9249f11ffb7Safresh1 *
92543003dfeSmillert */
92643003dfeSmillert static int
927e9ce3842Safresh1 match(Char *name, Char *pat, Char *patend, int nocase)
92843003dfeSmillert {
92943003dfeSmillert int ok, negate_range;
93043003dfeSmillert Char c, k;
9319f11ffb7Safresh1 Char *nextp = NULL;
9329f11ffb7Safresh1 Char *nextn = NULL;
93343003dfeSmillert
9349f11ffb7Safresh1 redo:
93543003dfeSmillert while (pat < patend) {
93643003dfeSmillert c = *pat++;
93743003dfeSmillert switch (c & M_MASK) {
93843003dfeSmillert case M_ALL:
93943003dfeSmillert if (pat == patend)
94043003dfeSmillert return(1);
9419f11ffb7Safresh1 if (*name == BG_EOS)
9429f11ffb7Safresh1 return 0;
9439f11ffb7Safresh1 nextn = name + 1;
9449f11ffb7Safresh1 nextp = pat - 1;
9459f11ffb7Safresh1 break;
94643003dfeSmillert case M_ONE:
9479f11ffb7Safresh1 /* since * matches leftmost-shortest first *
9489f11ffb7Safresh1 * if we encounter the EOS then backtracking *
9499f11ffb7Safresh1 * will not help, so we can exit early here. */
95043003dfeSmillert if (*name++ == BG_EOS)
9519f11ffb7Safresh1 return 0;
95243003dfeSmillert break;
95343003dfeSmillert case M_SET:
95443003dfeSmillert ok = 0;
9559f11ffb7Safresh1 /* since * matches leftmost-shortest first *
9569f11ffb7Safresh1 * if we encounter the EOS then backtracking *
9579f11ffb7Safresh1 * will not help, so we can exit early here. */
95843003dfeSmillert if ((k = *name++) == BG_EOS)
9599f11ffb7Safresh1 return 0;
96043003dfeSmillert if ((negate_range = ((*pat & M_MASK) == M_NOT)) != BG_EOS)
96143003dfeSmillert ++pat;
96243003dfeSmillert while (((c = *pat++) & M_MASK) != M_END)
96343003dfeSmillert if ((*pat & M_MASK) == M_RNG) {
96443003dfeSmillert if (nocase) {
96543003dfeSmillert if (tolower(c) <= tolower(k) && tolower(k) <= tolower(pat[1]))
96643003dfeSmillert ok = 1;
96743003dfeSmillert } else {
96843003dfeSmillert if (c <= k && k <= pat[1])
96943003dfeSmillert ok = 1;
97043003dfeSmillert }
97143003dfeSmillert pat += 2;
97243003dfeSmillert } else if (nocase ? (tolower(c) == tolower(k)) : (c == k))
97343003dfeSmillert ok = 1;
97443003dfeSmillert if (ok == negate_range)
9759f11ffb7Safresh1 goto fail;
97643003dfeSmillert break;
97743003dfeSmillert default:
97843003dfeSmillert k = *name++;
97943003dfeSmillert if (nocase ? (tolower(k) != tolower(c)) : (k != c))
9809f11ffb7Safresh1 goto fail;
98143003dfeSmillert break;
98243003dfeSmillert }
98343003dfeSmillert }
9849f11ffb7Safresh1 if (*name == BG_EOS)
9859f11ffb7Safresh1 return 1;
9869f11ffb7Safresh1
9879f11ffb7Safresh1 fail:
9889f11ffb7Safresh1 if (nextn) {
9899f11ffb7Safresh1 pat = nextp;
9909f11ffb7Safresh1 name = nextn;
9919f11ffb7Safresh1 goto redo;
9929f11ffb7Safresh1 }
9939f11ffb7Safresh1 return 0;
99443003dfeSmillert }
99543003dfeSmillert
99643003dfeSmillert /* Free allocated data belonging to a glob_t structure. */
99743003dfeSmillert void
99843003dfeSmillert bsd_globfree(glob_t *pglob)
99943003dfeSmillert {
1000e9ce3842Safresh1 int i;
1001e9ce3842Safresh1 char **pp;
100243003dfeSmillert
100343003dfeSmillert if (pglob->gl_pathv != NULL) {
100443003dfeSmillert pp = pglob->gl_pathv + pglob->gl_offs;
100543003dfeSmillert for (i = pglob->gl_pathc; i--; ++pp)
100643003dfeSmillert if (*pp)
100743003dfeSmillert Safefree(*pp);
100843003dfeSmillert Safefree(pglob->gl_pathv);
100943003dfeSmillert pglob->gl_pathv = NULL;
101043003dfeSmillert }
101143003dfeSmillert }
101243003dfeSmillert
101343003dfeSmillert static DIR *
1014e9ce3842Safresh1 g_opendir(Char *str, glob_t *pglob)
101543003dfeSmillert {
101643003dfeSmillert char buf[MAXPATHLEN];
101743003dfeSmillert
101843003dfeSmillert if (!*str) {
101943003dfeSmillert my_strlcpy(buf, ".", sizeof(buf));
102043003dfeSmillert } else {
102143003dfeSmillert if (g_Ctoc(str, buf, sizeof(buf)))
102243003dfeSmillert return(NULL);
102343003dfeSmillert }
102443003dfeSmillert
102543003dfeSmillert if (pglob->gl_flags & GLOB_ALTDIRFUNC)
102643003dfeSmillert return((DIR*)(*pglob->gl_opendir)(buf));
102743003dfeSmillert
102843003dfeSmillert return(PerlDir_open(buf));
102943003dfeSmillert }
103043003dfeSmillert
103143003dfeSmillert static int
1032e9ce3842Safresh1 g_lstat(Char *fn, Stat_t *sb, glob_t *pglob)
103343003dfeSmillert {
103443003dfeSmillert char buf[MAXPATHLEN];
103543003dfeSmillert
103643003dfeSmillert if (g_Ctoc(fn, buf, sizeof(buf)))
103743003dfeSmillert return(-1);
103843003dfeSmillert if (pglob->gl_flags & GLOB_ALTDIRFUNC)
103943003dfeSmillert return((*pglob->gl_lstat)(buf, sb));
104043003dfeSmillert #ifdef HAS_LSTAT
104143003dfeSmillert return(PerlLIO_lstat(buf, sb));
104243003dfeSmillert #else
104343003dfeSmillert return(PerlLIO_stat(buf, sb));
104443003dfeSmillert #endif /* HAS_LSTAT */
104543003dfeSmillert }
104643003dfeSmillert
104743003dfeSmillert static int
1048e9ce3842Safresh1 g_stat(Char *fn, Stat_t *sb, glob_t *pglob)
104943003dfeSmillert {
105043003dfeSmillert char buf[MAXPATHLEN];
105143003dfeSmillert
105243003dfeSmillert if (g_Ctoc(fn, buf, sizeof(buf)))
105343003dfeSmillert return(-1);
105443003dfeSmillert if (pglob->gl_flags & GLOB_ALTDIRFUNC)
105543003dfeSmillert return((*pglob->gl_stat)(buf, sb));
105643003dfeSmillert return(PerlLIO_stat(buf, sb));
105743003dfeSmillert }
105843003dfeSmillert
105943003dfeSmillert static Char *
106043003dfeSmillert g_strchr(Char *str, int ch)
106143003dfeSmillert {
106243003dfeSmillert do {
106343003dfeSmillert if (*str == ch)
106443003dfeSmillert return (str);
106543003dfeSmillert } while (*str++);
106643003dfeSmillert return (NULL);
106743003dfeSmillert }
106843003dfeSmillert
106943003dfeSmillert static int
1070e9ce3842Safresh1 g_Ctoc(const Char *str, char *buf, STRLEN len)
107143003dfeSmillert {
107243003dfeSmillert while (len--) {
107343003dfeSmillert if ((*buf++ = (char)*str++) == BG_EOS)
107443003dfeSmillert return (0);
107543003dfeSmillert }
107643003dfeSmillert return (1);
107743003dfeSmillert }
107843003dfeSmillert
107943003dfeSmillert #ifdef GLOB_DEBUG
108043003dfeSmillert static void
1081e9ce3842Safresh1 qprintf(const char *str, Char *s)
108243003dfeSmillert {
1083e9ce3842Safresh1 Char *p;
108443003dfeSmillert
108543003dfeSmillert (void)printf("%s:\n", str);
108643003dfeSmillert for (p = s; *p; p++)
108743003dfeSmillert (void)printf("%c", CHAR(*p));
108843003dfeSmillert (void)printf("\n");
108943003dfeSmillert for (p = s; *p; p++)
109043003dfeSmillert (void)printf("%c", *p & M_PROTECT ? '"' : ' ');
109143003dfeSmillert (void)printf("\n");
109243003dfeSmillert for (p = s; *p; p++)
109343003dfeSmillert (void)printf("%c", ismeta(*p) ? '_' : ' ');
109443003dfeSmillert (void)printf("\n");
109543003dfeSmillert }
109643003dfeSmillert #endif /* GLOB_DEBUG */
1097