xref: /openbsd-src/gnu/usr.bin/perl/ext/File-Glob/bsd_glob.c (revision e068048151d29f2562a32185e21a8ba885482260)
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