xref: /freebsd/contrib/tcsh/glob.c (revision 6560ac57ce879857203bc456cdc3849808dc0700)
1c80476e4SDavid E. O'Brien /*
2c80476e4SDavid E. O'Brien  * Copyright (c) 1989 The Regents of the University of California.
3c80476e4SDavid E. O'Brien  * All rights reserved.
4c80476e4SDavid E. O'Brien  *
5c80476e4SDavid E. O'Brien  * This code is derived from software contributed to Berkeley by
6c80476e4SDavid E. O'Brien  * Guido van Rossum.
7c80476e4SDavid E. O'Brien  *
8c80476e4SDavid E. O'Brien  * Redistribution and use in source and binary forms, with or without
9c80476e4SDavid E. O'Brien  * modification, are permitted provided that the following conditions
10c80476e4SDavid E. O'Brien  * are met:
11c80476e4SDavid E. O'Brien  * 1. Redistributions of source code must retain the above copyright
12c80476e4SDavid E. O'Brien  *    notice, this list of conditions and the following disclaimer.
13c80476e4SDavid E. O'Brien  * 2. Redistributions in binary form must reproduce the above copyright
14c80476e4SDavid E. O'Brien  *    notice, this list of conditions and the following disclaimer in the
15c80476e4SDavid E. O'Brien  *    documentation and/or other materials provided with the distribution.
1629301572SMark Peek  * 3. Neither the name of the University nor the names of its contributors
17c80476e4SDavid E. O'Brien  *    may be used to endorse or promote products derived from this software
18c80476e4SDavid E. O'Brien  *    without specific prior written permission.
19c80476e4SDavid E. O'Brien  *
20c80476e4SDavid E. O'Brien  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21c80476e4SDavid E. O'Brien  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22c80476e4SDavid E. O'Brien  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23c80476e4SDavid E. O'Brien  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24c80476e4SDavid E. O'Brien  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25c80476e4SDavid E. O'Brien  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26c80476e4SDavid E. O'Brien  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27c80476e4SDavid E. O'Brien  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28c80476e4SDavid E. O'Brien  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29c80476e4SDavid E. O'Brien  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30c80476e4SDavid E. O'Brien  * SUCH DAMAGE.
31c80476e4SDavid E. O'Brien  */
32c80476e4SDavid E. O'Brien #if defined(LIBC_SCCS) && !defined(lint)
33c80476e4SDavid E. O'Brien static char sccsid[] = "@(#)glob.c	5.12 (Berkeley) 6/24/91";
34c80476e4SDavid E. O'Brien #endif /* LIBC_SCCS and not lint */
35c80476e4SDavid E. O'Brien /*
36c80476e4SDavid E. O'Brien  * Glob: the interface is a superset of the one defined in POSIX 1003.2,
37c80476e4SDavid E. O'Brien  * draft 9.
38c80476e4SDavid E. O'Brien  *
39c80476e4SDavid E. O'Brien  * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
40c80476e4SDavid E. O'Brien  *
41c80476e4SDavid E. O'Brien  * Optional extra services, controlled by flags not defined by POSIX:
42c80476e4SDavid E. O'Brien  *
43c80476e4SDavid E. O'Brien  * GLOB_QUOTE:
44c80476e4SDavid E. O'Brien  *	Escaping convention: \ inhibits any special meaning the following
45c80476e4SDavid E. O'Brien  *	character might have (except \ at end of string is retained).
46c80476e4SDavid E. O'Brien  * GLOB_MAGCHAR:
47c80476e4SDavid E. O'Brien  *	Set in gl_flags if pattern contained a globbing character.
48c80476e4SDavid E. O'Brien  * GLOB_ALTNOT:
49c80476e4SDavid E. O'Brien  *	Use ^ instead of ! for "not".
50c80476e4SDavid E. O'Brien  * gl_matchc:
51c80476e4SDavid E. O'Brien  *	Number of matches in the current invocation of glob.
52c80476e4SDavid E. O'Brien  */
53c80476e4SDavid E. O'Brien 
543b6eaa7bSAndrey A. Chernov #ifdef WINNT_NATIVE
55c80476e4SDavid E. O'Brien 	#pragma warning(disable:4244)
563b6eaa7bSAndrey A. Chernov #endif /* WINNT_NATIVE */
57c80476e4SDavid E. O'Brien 
58c80476e4SDavid E. O'Brien #define Char __Char
59c80476e4SDavid E. O'Brien #include "sh.h"
6023338178SMark Peek #include "glob.h"
6123338178SMark Peek 
6219d2e3deSDmitry Chagin #ifndef HAVE_MBLEN
6319d2e3deSDmitry Chagin #undef mblen
6419d2e3deSDmitry Chagin #define mblen(_s,_n)	mbrlen((_s),(_n),NULL)
6519d2e3deSDmitry Chagin #endif
6619d2e3deSDmitry Chagin 
67c80476e4SDavid E. O'Brien #undef Char
68c80476e4SDavid E. O'Brien #undef QUOTE
69c80476e4SDavid E. O'Brien #undef TILDE
70c80476e4SDavid E. O'Brien #undef META
71c80476e4SDavid E. O'Brien #undef ismeta
72c80476e4SDavid E. O'Brien #undef Strchr
73c80476e4SDavid E. O'Brien 
74c80476e4SDavid E. O'Brien #ifndef S_ISDIR
75c80476e4SDavid E. O'Brien #define S_ISDIR(a)	(((a) & S_IFMT) == S_IFDIR)
76c80476e4SDavid E. O'Brien #endif
77c80476e4SDavid E. O'Brien 
78c80476e4SDavid E. O'Brien #if !defined(S_ISLNK) && defined(S_IFLNK)
79c80476e4SDavid E. O'Brien #define S_ISLNK(a)	(((a) & S_IFMT) == S_IFLNK)
80c80476e4SDavid E. O'Brien #endif
81c80476e4SDavid E. O'Brien 
82c80476e4SDavid E. O'Brien #if !defined(S_ISLNK) && !defined(lstat)
83c80476e4SDavid E. O'Brien #define lstat stat
84c80476e4SDavid E. O'Brien #endif
85c80476e4SDavid E. O'Brien 
86c80476e4SDavid E. O'Brien typedef unsigned short Char;
87c80476e4SDavid E. O'Brien 
8845e5710bSMark Peek static	int	 glob1 		(Char *, glob_t *, int);
8945e5710bSMark Peek static	int	 glob2		(struct strbuf *, const Char *, glob_t *, int);
9045e5710bSMark Peek static	int	 glob3		(struct strbuf *, const Char *, const Char *,
919ccc37e3SMark Peek 				 const Char *, glob_t *, int);
9245e5710bSMark Peek static	void	 globextend	(const char *, glob_t *);
9345e5710bSMark Peek static	int	 match		(const char *, const Char *, const Char *,
9445e5710bSMark Peek 				 int);
9545e5710bSMark Peek static	int	 compare	(const void *, const void *);
9645e5710bSMark Peek static 	DIR	*Opendir	(const char *);
97c80476e4SDavid E. O'Brien #ifdef S_IFLNK
9845e5710bSMark Peek static	int	 Lstat		(const char *, struct stat *);
99c80476e4SDavid E. O'Brien #endif
10045e5710bSMark Peek static	int	 Stat		(const char *, struct stat *sb);
10145e5710bSMark Peek static 	Char 	*Strchr		(Char *, int);
102c80476e4SDavid E. O'Brien #ifdef DEBUG
103*d803a9d0SBrooks Davis static	void	 qprintf	(const char *, const Char *);
104c80476e4SDavid E. O'Brien #endif
105c80476e4SDavid E. O'Brien 
106c80476e4SDavid E. O'Brien #define	DOLLAR		'$'
107c80476e4SDavid E. O'Brien #define	DOT		'.'
108c80476e4SDavid E. O'Brien #define	EOS		'\0'
109c80476e4SDavid E. O'Brien #define	LBRACKET	'['
110c80476e4SDavid E. O'Brien #define	NOT		'!'
111c80476e4SDavid E. O'Brien #define ALTNOT		'^'
112c80476e4SDavid E. O'Brien #define	QUESTION	'?'
113c80476e4SDavid E. O'Brien #define	QUOTE		'\\'
114c80476e4SDavid E. O'Brien #define	RANGE		'-'
115c80476e4SDavid E. O'Brien #define	RBRACKET	']'
116c80476e4SDavid E. O'Brien #define	SEP		'/'
117c80476e4SDavid E. O'Brien #define	STAR		'*'
118c80476e4SDavid E. O'Brien #define	TILDE		'~'
119c80476e4SDavid E. O'Brien #define	UNDERSCORE	'_'
120c80476e4SDavid E. O'Brien 
121c80476e4SDavid E. O'Brien #define	M_META		0x8000
122c80476e4SDavid E. O'Brien #define M_PROTECT	0x4000
123c80476e4SDavid E. O'Brien #define	M_MASK		0xffff
124c80476e4SDavid E. O'Brien #define	M_ASCII		0x00ff
125c80476e4SDavid E. O'Brien 
12645e5710bSMark Peek #define	LCHAR(c)	((c)&M_ASCII)
127c80476e4SDavid E. O'Brien #define	META(c)		((c)|M_META)
128c80476e4SDavid E. O'Brien #define	M_ALL		META('*')
129c80476e4SDavid E. O'Brien #define	M_END		META(']')
130c80476e4SDavid E. O'Brien #define	M_NOT		META('!')
131c80476e4SDavid E. O'Brien #define	M_ALTNOT	META('^')
132c80476e4SDavid E. O'Brien #define	M_ONE		META('?')
133c80476e4SDavid E. O'Brien #define	M_RNG		META('-')
134c80476e4SDavid E. O'Brien #define	M_SET		META('[')
135c80476e4SDavid E. O'Brien #define	ismeta(c)	(((c)&M_META) != 0)
136c80476e4SDavid E. O'Brien 
137c80476e4SDavid E. O'Brien int
globcharcoll(__Char c1,__Char c2,int cs)13845e5710bSMark Peek globcharcoll(__Char c1, __Char c2, int cs)
139c80476e4SDavid E. O'Brien {
14045e5710bSMark Peek #if defined(NLS) && defined(LC_COLLATE) && defined(HAVE_STRCOLL)
14145e5710bSMark Peek # if defined(WIDE_STRINGS)
14223338178SMark Peek     wchar_t s1[2], s2[2];
14323338178SMark Peek 
14423338178SMark Peek     if (c1 == c2)
14523338178SMark Peek 	return (0);
14623338178SMark Peek     if (cs) {
14723338178SMark Peek 	c1 = towlower(c1);
14823338178SMark Peek 	c2 = towlower(c2);
14923338178SMark Peek     } else {
1502219fc0fSAndrey A. Chernov #ifndef __FreeBSD__
15123338178SMark Peek 	/* This should not be here, but I'll rather leave it in than engage in
15223338178SMark Peek 	   a LC_COLLATE flamewar about a shell I don't use... */
15323338178SMark Peek 	if (iswlower(c1) && iswupper(c2))
15423338178SMark Peek 	    return (1);
15523338178SMark Peek 	if (iswupper(c1) && iswlower(c2))
15623338178SMark Peek 	    return (-1);
1572219fc0fSAndrey A. Chernov #endif
15823338178SMark Peek     }
15923338178SMark Peek     s1[0] = c1;
16023338178SMark Peek     s2[0] = c2;
16123338178SMark Peek     s1[1] = s2[1] = '\0';
16223338178SMark Peek     return wcscoll(s1, s2);
16345e5710bSMark Peek # else /* not WIDE_STRINGS */
164c80476e4SDavid E. O'Brien     char s1[2], s2[2];
165c80476e4SDavid E. O'Brien 
166c80476e4SDavid E. O'Brien     if (c1 == c2)
167c80476e4SDavid E. O'Brien 	return (0);
1688e66bd9eSDavid E. O'Brien     /*
1698e66bd9eSDavid E. O'Brien      * From kevin lyda <kevin@suberic.net>:
1708e66bd9eSDavid E. O'Brien      * strcoll does not guarantee case sorting, so we pre-process now:
1718e66bd9eSDavid E. O'Brien      */
172b2d5d167SMark Peek     if (cs) {
173b2d5d167SMark Peek 	c1 = islower(c1) ? c1 : tolower(c1);
174b2d5d167SMark Peek 	c2 = islower(c2) ? c2 : tolower(c2);
175b2d5d167SMark Peek     } else {
1768e66bd9eSDavid E. O'Brien 	if (islower(c1) && isupper(c2))
1778e66bd9eSDavid E. O'Brien 	    return (1);
17823338178SMark Peek 	if (isupper(c1) && islower(c2))
17923338178SMark Peek 	    return (-1);
180b2d5d167SMark Peek     }
181c80476e4SDavid E. O'Brien     s1[0] = c1;
182c80476e4SDavid E. O'Brien     s2[0] = c2;
183c80476e4SDavid E. O'Brien     s1[1] = s2[1] = '\0';
184c80476e4SDavid E. O'Brien     return strcoll(s1, s2);
18523338178SMark Peek # endif
186c80476e4SDavid E. O'Brien #else
187c80476e4SDavid E. O'Brien     return (c1 - c2);
188c80476e4SDavid E. O'Brien #endif
189c80476e4SDavid E. O'Brien }
190c80476e4SDavid E. O'Brien 
191c80476e4SDavid E. O'Brien /*
192c80476e4SDavid E. O'Brien  * Need to dodge two kernel bugs:
193c80476e4SDavid E. O'Brien  * opendir("") != opendir(".")
194c80476e4SDavid E. O'Brien  * NAMEI_BUG: on plain files trailing slashes are ignored in some kernels.
195c80476e4SDavid E. O'Brien  *            POSIX specifies that they should be ignored in directories.
196c80476e4SDavid E. O'Brien  */
197c80476e4SDavid E. O'Brien 
198c80476e4SDavid E. O'Brien static DIR *
Opendir(const char * str)19945e5710bSMark Peek Opendir(const char *str)
200c80476e4SDavid E. O'Brien {
201c80476e4SDavid E. O'Brien #if defined(hpux) || defined(__hpux)
202c80476e4SDavid E. O'Brien     struct stat st;
203c80476e4SDavid E. O'Brien #endif
204c80476e4SDavid E. O'Brien 
205c80476e4SDavid E. O'Brien     if (!*str)
206c80476e4SDavid E. O'Brien 	return (opendir("."));
207c80476e4SDavid E. O'Brien #if defined(hpux) || defined(__hpux)
208c80476e4SDavid E. O'Brien     /*
209c80476e4SDavid E. O'Brien      * Opendir on some device files hangs, so avoid it
210c80476e4SDavid E. O'Brien      */
21145e5710bSMark Peek     if (stat(str, &st) == -1 || !S_ISDIR(st.st_mode))
212c80476e4SDavid E. O'Brien 	return NULL;
213c80476e4SDavid E. O'Brien #endif
21445e5710bSMark Peek     return opendir(str);
215c80476e4SDavid E. O'Brien }
216c80476e4SDavid E. O'Brien 
217c80476e4SDavid E. O'Brien #ifdef S_IFLNK
218c80476e4SDavid E. O'Brien static int
Lstat(const char * fn,struct stat * sb)21945e5710bSMark Peek Lstat(const char *fn, struct stat *sb)
220c80476e4SDavid E. O'Brien {
221c80476e4SDavid E. O'Brien     int st;
222c80476e4SDavid E. O'Brien 
22345e5710bSMark Peek     st = lstat(fn, sb);
22445e5710bSMark Peek # ifdef NAMEI_BUG
22545e5710bSMark Peek     if (*fn != 0 && strend(fn)[-1] == '/' && !S_ISDIR(sb->st_mode))
22645e5710bSMark Peek 	st = -1;
227c80476e4SDavid E. O'Brien # endif	/* NAMEI_BUG */
22845e5710bSMark Peek     return st;
229c80476e4SDavid E. O'Brien }
230c80476e4SDavid E. O'Brien #else
231c80476e4SDavid E. O'Brien #define Lstat Stat
232c80476e4SDavid E. O'Brien #endif /* S_IFLNK */
233c80476e4SDavid E. O'Brien 
234c80476e4SDavid E. O'Brien static int
Stat(const char * fn,struct stat * sb)23545e5710bSMark Peek Stat(const char *fn, struct stat *sb)
236c80476e4SDavid E. O'Brien {
237c80476e4SDavid E. O'Brien     int st;
238c80476e4SDavid E. O'Brien 
23945e5710bSMark Peek     st = stat(fn, sb);
24045e5710bSMark Peek #ifdef NAMEI_BUG
24145e5710bSMark Peek     if (*fn != 0 && strend(fn)[-1] == '/' && !S_ISDIR(sb->st_mode))
24245e5710bSMark Peek 	st = -1;
243c80476e4SDavid E. O'Brien #endif /* NAMEI_BUG */
24445e5710bSMark Peek     return st;
245c80476e4SDavid E. O'Brien }
246c80476e4SDavid E. O'Brien 
247c80476e4SDavid E. O'Brien static Char *
Strchr(Char * str,int ch)24845e5710bSMark Peek Strchr(Char *str, int ch)
249c80476e4SDavid E. O'Brien {
250c80476e4SDavid E. O'Brien     do
251c80476e4SDavid E. O'Brien 	if (*str == ch)
252c80476e4SDavid E. O'Brien 	    return (str);
253c80476e4SDavid E. O'Brien     while (*str++);
254c80476e4SDavid E. O'Brien     return (NULL);
255c80476e4SDavid E. O'Brien }
256c80476e4SDavid E. O'Brien 
257c80476e4SDavid E. O'Brien #ifdef DEBUG
258c80476e4SDavid E. O'Brien static void
qprintf(const char * pre,const Char * s)259*d803a9d0SBrooks Davis qprintf(const char *pre, const Char *s)
260c80476e4SDavid E. O'Brien {
26145e5710bSMark Peek     const Char *p;
262c80476e4SDavid E. O'Brien 
263*d803a9d0SBrooks Davis     xprintf("%s", pre);
264c80476e4SDavid E. O'Brien     for (p = s; *p; p++)
265*d803a9d0SBrooks Davis 	xprintf("%c", *p & 0xff);
266*d803a9d0SBrooks Davis     xprintf("\n%s", pre);
267c80476e4SDavid E. O'Brien     for (p = s; *p; p++)
268*d803a9d0SBrooks Davis 	xprintf("%c", *p & M_PROTECT ? '"' : ' ');
269*d803a9d0SBrooks Davis     xprintf("\n%s", pre);
270c80476e4SDavid E. O'Brien     for (p = s; *p; p++)
271*d803a9d0SBrooks Davis 	xprintf("%c", *p & M_META ? '_' : ' ');
272*d803a9d0SBrooks Davis     xprintf("\n");
273c80476e4SDavid E. O'Brien }
274c80476e4SDavid E. O'Brien #endif /* DEBUG */
275c80476e4SDavid E. O'Brien 
276c80476e4SDavid E. O'Brien static int
compare(const void * p,const void * q)27745e5710bSMark Peek compare(const void *p, const void *q)
278c80476e4SDavid E. O'Brien {
27945e5710bSMark Peek #if defined(NLS) && defined(HAVE_STRCOLL)
28045e5710bSMark Peek     return (strcoll(*(char *const *) p, *(char *const *) q));
281c80476e4SDavid E. O'Brien #else
28245e5710bSMark Peek     return (strcmp(*(char *const *) p, *(char *const *) q));
28345e5710bSMark Peek #endif /* NLS && HAVE_STRCOLL */
284c80476e4SDavid E. O'Brien }
285c80476e4SDavid E. O'Brien 
286c80476e4SDavid E. O'Brien /*
287c80476e4SDavid E. O'Brien  * The main glob() routine: compiles the pattern (optionally processing
288c80476e4SDavid E. O'Brien  * quotes), calls glob1() to do the real pattern matching, and finally
289c80476e4SDavid E. O'Brien  * sorts the list (unless unsorted operation is requested).  Returns 0
290c80476e4SDavid E. O'Brien  * if things went well, nonzero if errors occurred.  It is not an error
291c80476e4SDavid E. O'Brien  * to find no matches.
292c80476e4SDavid E. O'Brien  */
293c80476e4SDavid E. O'Brien int
glob(const char * pattern,int flags,int (* errfunc)(const char *,int),glob_t * pglob)29445e5710bSMark Peek glob(const char *pattern, int flags, int (*errfunc) (const char *, int),
29545e5710bSMark Peek      glob_t *pglob)
296c80476e4SDavid E. O'Brien {
297c80476e4SDavid E. O'Brien     int     err, oldpathc;
29845e5710bSMark Peek     Char *bufnext, m_not;
29945e5710bSMark Peek     const unsigned char *patnext;
300c80476e4SDavid E. O'Brien     int     c, not;
30145e5710bSMark Peek     Char *qpatnext, *patbuf;
302c80476e4SDavid E. O'Brien     int     no_match;
303c80476e4SDavid E. O'Brien 
30423338178SMark Peek     patnext = (const unsigned char *) pattern;
305c80476e4SDavid E. O'Brien     if (!(flags & GLOB_APPEND)) {
306c80476e4SDavid E. O'Brien 	pglob->gl_pathc = 0;
307c80476e4SDavid E. O'Brien 	pglob->gl_pathv = NULL;
308c80476e4SDavid E. O'Brien 	if (!(flags & GLOB_DOOFFS))
309c80476e4SDavid E. O'Brien 	    pglob->gl_offs = 0;
310c80476e4SDavid E. O'Brien     }
311c80476e4SDavid E. O'Brien     pglob->gl_flags = flags & ~GLOB_MAGCHAR;
312c80476e4SDavid E. O'Brien     pglob->gl_errfunc = errfunc;
313c80476e4SDavid E. O'Brien     oldpathc = pglob->gl_pathc;
314c80476e4SDavid E. O'Brien     pglob->gl_matchc = 0;
315c80476e4SDavid E. O'Brien 
316c80476e4SDavid E. O'Brien     if (pglob->gl_flags & GLOB_ALTNOT) {
317c80476e4SDavid E. O'Brien 	not = ALTNOT;
318c80476e4SDavid E. O'Brien 	m_not = M_ALTNOT;
319c80476e4SDavid E. O'Brien     }
320c80476e4SDavid E. O'Brien     else {
321c80476e4SDavid E. O'Brien 	not = NOT;
322c80476e4SDavid E. O'Brien 	m_not = M_NOT;
323c80476e4SDavid E. O'Brien     }
324c80476e4SDavid E. O'Brien 
32545e5710bSMark Peek     patbuf = xmalloc((strlen(pattern) + 1) * sizeof(*patbuf));
326c80476e4SDavid E. O'Brien     bufnext = patbuf;
327c80476e4SDavid E. O'Brien 
328c80476e4SDavid E. O'Brien     no_match = *patnext == not;
329c80476e4SDavid E. O'Brien     if (no_match)
330c80476e4SDavid E. O'Brien 	patnext++;
331c80476e4SDavid E. O'Brien 
332c80476e4SDavid E. O'Brien     if (flags & GLOB_QUOTE) {
333c80476e4SDavid E. O'Brien 	/* Protect the quoted characters */
33445e5710bSMark Peek 	while ((c = *patnext++) != EOS) {
33523338178SMark Peek #ifdef WIDE_STRINGS
33623338178SMark Peek 	    int len;
33723338178SMark Peek 
33823338178SMark Peek 	    len = mblen((const char *)(patnext - 1), MB_LEN_MAX);
33923338178SMark Peek 	    if (len == -1)
3409ccc37e3SMark Peek 		TCSH_IGNORE(mblen(NULL, 0));
341a15e6f9aSMark Peek 	    else if (len > 1) {
3428e66bd9eSDavid E. O'Brien 		*bufnext++ = (Char) c;
34323338178SMark Peek 		while (--len != 0)
34423338178SMark Peek 		    *bufnext++ = (Char) (*patnext++ | M_PROTECT);
34523338178SMark Peek 	    } else
34623338178SMark Peek #endif /* WIDE_STRINGS */
347c80476e4SDavid E. O'Brien 	    if (c == QUOTE) {
348c80476e4SDavid E. O'Brien 		if ((c = *patnext++) == EOS) {
349c80476e4SDavid E. O'Brien 		    c = QUOTE;
350c80476e4SDavid E. O'Brien 		    --patnext;
351c80476e4SDavid E. O'Brien 		}
352c80476e4SDavid E. O'Brien 		*bufnext++ = (Char) (c | M_PROTECT);
353c80476e4SDavid E. O'Brien 	    }
354c80476e4SDavid E. O'Brien 	    else
355c80476e4SDavid E. O'Brien 		*bufnext++ = (Char) c;
356c80476e4SDavid E. O'Brien 	}
35723338178SMark Peek     }
358c80476e4SDavid E. O'Brien     else
35945e5710bSMark Peek 	while ((c = *patnext++) != EOS)
360c80476e4SDavid E. O'Brien 	    *bufnext++ = (Char) c;
361c80476e4SDavid E. O'Brien     *bufnext = EOS;
362c80476e4SDavid E. O'Brien 
363c80476e4SDavid E. O'Brien     bufnext = patbuf;
364c80476e4SDavid E. O'Brien     qpatnext = patbuf;
365c80476e4SDavid E. O'Brien     while ((c = *qpatnext++) != EOS) {
366c80476e4SDavid E. O'Brien 	switch (c) {
367c80476e4SDavid E. O'Brien 	case LBRACKET:
368c80476e4SDavid E. O'Brien 	    c = *qpatnext;
369c80476e4SDavid E. O'Brien 	    if (c == not)
370c80476e4SDavid E. O'Brien 		++qpatnext;
371c80476e4SDavid E. O'Brien 	    if (*qpatnext == EOS ||
372c80476e4SDavid E. O'Brien 		Strchr(qpatnext + 1, RBRACKET) == NULL) {
373c80476e4SDavid E. O'Brien 		*bufnext++ = LBRACKET;
374c80476e4SDavid E. O'Brien 		if (c == not)
375c80476e4SDavid E. O'Brien 		    --qpatnext;
376c80476e4SDavid E. O'Brien 		break;
377c80476e4SDavid E. O'Brien 	    }
378c80476e4SDavid E. O'Brien 	    pglob->gl_flags |= GLOB_MAGCHAR;
379c80476e4SDavid E. O'Brien 	    *bufnext++ = M_SET;
380c80476e4SDavid E. O'Brien 	    if (c == not)
381c80476e4SDavid E. O'Brien 		*bufnext++ = m_not;
382c80476e4SDavid E. O'Brien 	    c = *qpatnext++;
383c80476e4SDavid E. O'Brien 	    do {
38445e5710bSMark Peek 		*bufnext++ = LCHAR(c);
385c80476e4SDavid E. O'Brien 		if (*qpatnext == RANGE &&
386c80476e4SDavid E. O'Brien 		    (c = qpatnext[1]) != RBRACKET) {
387c80476e4SDavid E. O'Brien 		    *bufnext++ = M_RNG;
38845e5710bSMark Peek 		    *bufnext++ = LCHAR(c);
389c80476e4SDavid E. O'Brien 		    qpatnext += 2;
390c80476e4SDavid E. O'Brien 		}
391c80476e4SDavid E. O'Brien 	    } while ((c = *qpatnext++) != RBRACKET);
392c80476e4SDavid E. O'Brien 	    *bufnext++ = M_END;
393c80476e4SDavid E. O'Brien 	    break;
394c80476e4SDavid E. O'Brien 	case QUESTION:
395c80476e4SDavid E. O'Brien 	    pglob->gl_flags |= GLOB_MAGCHAR;
396c80476e4SDavid E. O'Brien 	    *bufnext++ = M_ONE;
397c80476e4SDavid E. O'Brien 	    break;
398c80476e4SDavid E. O'Brien 	case STAR:
399c80476e4SDavid E. O'Brien 	    pglob->gl_flags |= GLOB_MAGCHAR;
4009ccc37e3SMark Peek 	    /* collapse adjacent stars to one [or three if globstar],
4019ccc37e3SMark Peek 	     * to avoid exponential behavior
402c80476e4SDavid E. O'Brien 	     */
4039ccc37e3SMark Peek 	    if (bufnext == patbuf || bufnext[-1] != M_ALL ||
4049ccc37e3SMark Peek 	       ((flags & GLOB_STAR) != 0 &&
4059ccc37e3SMark Peek 		 (bufnext - 1 == patbuf || bufnext[-2] != M_ALL ||
4069ccc37e3SMark Peek 		 bufnext - 2 == patbuf || bufnext[-3] != M_ALL)))
407c80476e4SDavid E. O'Brien 		*bufnext++ = M_ALL;
408c80476e4SDavid E. O'Brien 	    break;
409c80476e4SDavid E. O'Brien 	default:
41045e5710bSMark Peek 	    *bufnext++ = LCHAR(c);
411c80476e4SDavid E. O'Brien 	    break;
412c80476e4SDavid E. O'Brien 	}
413c80476e4SDavid E. O'Brien     }
414c80476e4SDavid E. O'Brien     *bufnext = EOS;
415c80476e4SDavid E. O'Brien #ifdef DEBUG
416*d803a9d0SBrooks Davis     qprintf("patbuf=", patbuf);
417c80476e4SDavid E. O'Brien #endif
418c80476e4SDavid E. O'Brien 
41945e5710bSMark Peek     if ((err = glob1(patbuf, pglob, no_match)) != 0) {
42045e5710bSMark Peek 	xfree(patbuf);
421c80476e4SDavid E. O'Brien 	return (err);
42245e5710bSMark Peek     }
423c80476e4SDavid E. O'Brien 
424c80476e4SDavid E. O'Brien     /*
425c80476e4SDavid E. O'Brien      * If there was no match we are going to append the pattern
426c80476e4SDavid E. O'Brien      * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
427c80476e4SDavid E. O'Brien      * and the pattern did not contain any magic characters
428c80476e4SDavid E. O'Brien      * GLOB_NOMAGIC is there just for compatibility with csh.
429c80476e4SDavid E. O'Brien      */
430c80476e4SDavid E. O'Brien     if (pglob->gl_pathc == oldpathc &&
431c80476e4SDavid E. O'Brien 	((flags & GLOB_NOCHECK) ||
432c80476e4SDavid E. O'Brien 	 ((flags & GLOB_NOMAGIC) && !(pglob->gl_flags & GLOB_MAGCHAR)))) {
43345e5710bSMark Peek 	if (!(flags & GLOB_QUOTE))
43445e5710bSMark Peek 	    globextend(pattern, pglob);
435c80476e4SDavid E. O'Brien 	else {
43645e5710bSMark Peek 	    char *copy, *dest;
43745e5710bSMark Peek 	    const char *src;
43845e5710bSMark Peek 
43945e5710bSMark Peek 	    /* copy pattern, interpreting quotes */
44045e5710bSMark Peek 	    copy = xmalloc(strlen(pattern) + 1);
44145e5710bSMark Peek 	    dest = copy;
44245e5710bSMark Peek 	    src = pattern;
44345e5710bSMark Peek 	    while (*src != EOS) {
44419d2e3deSDmitry Chagin 		/* Don't interpret quotes. The spec does not say we should do */
44545e5710bSMark Peek 		if (*src == QUOTE) {
44645e5710bSMark Peek 		    if (*++src == EOS)
44745e5710bSMark Peek 			--src;
448c80476e4SDavid E. O'Brien 		}
44945e5710bSMark Peek 		*dest++ = *src++;
450c80476e4SDavid E. O'Brien 	    }
45145e5710bSMark Peek 	    *dest = EOS;
45245e5710bSMark Peek 	    globextend(copy, pglob);
45345e5710bSMark Peek 	    xfree(copy);
454c80476e4SDavid E. O'Brien 	}
45545e5710bSMark Peek 	xfree(patbuf);
45645e5710bSMark Peek 	return 0;
457c80476e4SDavid E. O'Brien     }
4588e66bd9eSDavid E. O'Brien     else if (!(flags & GLOB_NOSORT) && (pglob->gl_pathc != oldpathc))
45945e5710bSMark Peek 	qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
46045e5710bSMark Peek 	      pglob->gl_pathc - oldpathc, sizeof(char *), compare);
46145e5710bSMark Peek     xfree(patbuf);
462c80476e4SDavid E. O'Brien     return (0);
463c80476e4SDavid E. O'Brien }
464c80476e4SDavid E. O'Brien 
465c80476e4SDavid E. O'Brien static int
glob1(Char * pattern,glob_t * pglob,int no_match)46645e5710bSMark Peek glob1(Char *pattern, glob_t *pglob, int no_match)
467c80476e4SDavid E. O'Brien {
46845e5710bSMark Peek     struct strbuf pathbuf = strbuf_INIT;
46945e5710bSMark Peek     int err;
470c80476e4SDavid E. O'Brien 
471c80476e4SDavid E. O'Brien     /*
472c80476e4SDavid E. O'Brien      * a null pathname is invalid -- POSIX 1003.1 sect. 2.4.
473c80476e4SDavid E. O'Brien      */
474c80476e4SDavid E. O'Brien     if (*pattern == EOS)
475c80476e4SDavid E. O'Brien 	return (0);
47645e5710bSMark Peek     err = glob2(&pathbuf, pattern, pglob, no_match);
47745e5710bSMark Peek     xfree(pathbuf.s);
47845e5710bSMark Peek     return err;
479c80476e4SDavid E. O'Brien }
480c80476e4SDavid E. O'Brien 
481c80476e4SDavid E. O'Brien /*
482c80476e4SDavid E. O'Brien  * functions glob2 and glob3 are mutually recursive; there is one level
483c80476e4SDavid E. O'Brien  * of recursion for each segment in the pattern that contains one or
484c80476e4SDavid E. O'Brien  * more meta characters.
485c80476e4SDavid E. O'Brien  */
486c80476e4SDavid E. O'Brien static int
glob2(struct strbuf * pathbuf,const Char * pattern,glob_t * pglob,int no_match)48745e5710bSMark Peek glob2(struct strbuf *pathbuf, const Char *pattern, glob_t *pglob, int no_match)
488c80476e4SDavid E. O'Brien {
489c80476e4SDavid E. O'Brien     struct stat sbuf;
490c80476e4SDavid E. O'Brien     int anymeta;
49145e5710bSMark Peek     const Char *p;
49245e5710bSMark Peek     size_t orig_len;
493c80476e4SDavid E. O'Brien 
494c80476e4SDavid E. O'Brien     /*
495c80476e4SDavid E. O'Brien      * loop over pattern segments until end of pattern or until segment with
496c80476e4SDavid E. O'Brien      * meta character found.
497c80476e4SDavid E. O'Brien      */
498c80476e4SDavid E. O'Brien     anymeta = 0;
499c80476e4SDavid E. O'Brien     for (;;) {
500c80476e4SDavid E. O'Brien 	if (*pattern == EOS) {	/* end of pattern? */
50145e5710bSMark Peek 	    strbuf_terminate(pathbuf);
502c80476e4SDavid E. O'Brien 
50345e5710bSMark Peek 	    if (Lstat(pathbuf->s, &sbuf))
504c80476e4SDavid E. O'Brien 		return (0);
505c80476e4SDavid E. O'Brien 
506c80476e4SDavid E. O'Brien 	    if (((pglob->gl_flags & GLOB_MARK) &&
50745e5710bSMark Peek 		 pathbuf->s[pathbuf->len - 1] != SEP) &&
508c80476e4SDavid E. O'Brien 		(S_ISDIR(sbuf.st_mode)
509c80476e4SDavid E. O'Brien #ifdef S_IFLNK
510c80476e4SDavid E. O'Brien 		 || (S_ISLNK(sbuf.st_mode) &&
51145e5710bSMark Peek 		     (Stat(pathbuf->s, &sbuf) == 0) &&
512c80476e4SDavid E. O'Brien 		     S_ISDIR(sbuf.st_mode))
513c80476e4SDavid E. O'Brien #endif
514c80476e4SDavid E. O'Brien 		 )) {
51545e5710bSMark Peek 		strbuf_append1(pathbuf, SEP);
51645e5710bSMark Peek 		strbuf_terminate(pathbuf);
517c80476e4SDavid E. O'Brien 	    }
518c80476e4SDavid E. O'Brien 	    ++pglob->gl_matchc;
51945e5710bSMark Peek 	    globextend(pathbuf->s, pglob);
52045e5710bSMark Peek 	    return 0;
521c80476e4SDavid E. O'Brien 	}
522c80476e4SDavid E. O'Brien 
52345e5710bSMark Peek 	/* find end of next segment, tentatively copy to pathbuf */
524c80476e4SDavid E. O'Brien 	p = pattern;
52545e5710bSMark Peek 	orig_len = pathbuf->len;
526c80476e4SDavid E. O'Brien 	while (*p != EOS && *p != SEP) {
527c80476e4SDavid E. O'Brien 	    if (ismeta(*p))
528c80476e4SDavid E. O'Brien 		anymeta = 1;
52945e5710bSMark Peek 	    strbuf_append1(pathbuf, *p++);
530c80476e4SDavid E. O'Brien 	}
531c80476e4SDavid E. O'Brien 
532c80476e4SDavid E. O'Brien 	if (!anymeta) {		/* no expansion, do next segment */
533c80476e4SDavid E. O'Brien 	    pattern = p;
534c80476e4SDavid E. O'Brien 	    while (*pattern == SEP)
53545e5710bSMark Peek 		strbuf_append1(pathbuf, *pattern++);
536c80476e4SDavid E. O'Brien 	}
53745e5710bSMark Peek 	else {			/* need expansion, recurse */
53845e5710bSMark Peek 	    pathbuf->len = orig_len;
5399ccc37e3SMark Peek 	    return (glob3(pathbuf, pattern, p, pattern, pglob, no_match));
54045e5710bSMark Peek 	}
541c80476e4SDavid E. O'Brien     }
542c80476e4SDavid E. O'Brien     /* NOTREACHED */
543c80476e4SDavid E. O'Brien }
544c80476e4SDavid E. O'Brien 
5459ccc37e3SMark Peek static size_t
One_Char_mbtowc(__Char * pwc,const Char * s,size_t n)5469ccc37e3SMark Peek One_Char_mbtowc(__Char *pwc, const Char *s, size_t n)
5479ccc37e3SMark Peek {
5489ccc37e3SMark Peek #ifdef WIDE_STRINGS
5499ccc37e3SMark Peek     char buf[MB_LEN_MAX], *p;
5509ccc37e3SMark Peek 
5519ccc37e3SMark Peek     if (n > MB_LEN_MAX)
5529ccc37e3SMark Peek 	n = MB_LEN_MAX;
5539ccc37e3SMark Peek     p = buf;
5549ccc37e3SMark Peek     while (p < buf + n && (*p++ = LCHAR(*s++)) != 0)
5559ccc37e3SMark Peek 	;
5569ccc37e3SMark Peek     return one_mbtowc(pwc, buf, n);
5579ccc37e3SMark Peek #else
5589ccc37e3SMark Peek     *pwc = *s & CHAR;
5599ccc37e3SMark Peek     return 1;
5609ccc37e3SMark Peek #endif
5619ccc37e3SMark Peek }
562c80476e4SDavid E. O'Brien 
563c80476e4SDavid E. O'Brien static int
glob3(struct strbuf * pathbuf,const Char * pattern,const Char * restpattern,const Char * pglobstar,glob_t * pglob,int no_match)56445e5710bSMark Peek glob3(struct strbuf *pathbuf, const Char *pattern, const Char *restpattern,
5659ccc37e3SMark Peek       const Char *pglobstar, glob_t *pglob, int no_match)
566c80476e4SDavid E. O'Brien {
567c80476e4SDavid E. O'Brien     DIR    *dirp;
568c80476e4SDavid E. O'Brien     struct dirent *dp;
5699ccc37e3SMark Peek     struct stat sbuf;
570c80476e4SDavid E. O'Brien     int     err;
571c80476e4SDavid E. O'Brien     Char m_not = (pglob->gl_flags & GLOB_ALTNOT) ? M_ALTNOT : M_NOT;
57245e5710bSMark Peek     size_t orig_len;
5739ccc37e3SMark Peek     int globstar = 0;
5749ccc37e3SMark Peek     int chase_symlinks = 0;
5759ccc37e3SMark Peek     const Char *termstar = NULL;
576c80476e4SDavid E. O'Brien 
57745e5710bSMark Peek     strbuf_terminate(pathbuf);
5789ccc37e3SMark Peek     orig_len = pathbuf->len;
5799ccc37e3SMark Peek     errno = err = 0;
5809ccc37e3SMark Peek 
5819ccc37e3SMark Peek     while (pglobstar < restpattern) {
5829ccc37e3SMark Peek 	__Char wc;
5839ccc37e3SMark Peek 	size_t width = One_Char_mbtowc(&wc, pglobstar, MB_LEN_MAX);
5849ccc37e3SMark Peek 	if ((pglobstar[0] & M_MASK) == M_ALL &&
5859ccc37e3SMark Peek 	    (pglobstar[width] & M_MASK) == M_ALL) {
5869ccc37e3SMark Peek 	    globstar = 1;
5879ccc37e3SMark Peek 	    chase_symlinks = (pglobstar[2 * width] & M_MASK) == M_ALL;
5889ccc37e3SMark Peek 	    termstar = pglobstar + (2 + chase_symlinks) * width;
5899ccc37e3SMark Peek 	    break;
5909ccc37e3SMark Peek 	}
5919ccc37e3SMark Peek         pglobstar += width;
5929ccc37e3SMark Peek     }
5939ccc37e3SMark Peek 
5949ccc37e3SMark Peek     if (globstar) {
5959ccc37e3SMark Peek 	err = pglobstar==pattern && termstar==restpattern ?
5969ccc37e3SMark Peek 		*restpattern == EOS ?
5979ccc37e3SMark Peek 		glob2(pathbuf, restpattern - 1, pglob, no_match) :
5989ccc37e3SMark Peek 		glob2(pathbuf, restpattern + 1, pglob, no_match) :
5999ccc37e3SMark Peek 		glob3(pathbuf, pattern, restpattern, termstar, pglob, no_match);
6009ccc37e3SMark Peek 	if (err)
6019ccc37e3SMark Peek 	    return err;
6029ccc37e3SMark Peek 	pathbuf->len = orig_len;
6039ccc37e3SMark Peek 	strbuf_terminate(pathbuf);
6049ccc37e3SMark Peek     }
6059ccc37e3SMark Peek 
6069ccc37e3SMark Peek     if (*pathbuf->s && (Lstat(pathbuf->s, &sbuf) || !S_ISDIR(sbuf.st_mode)
6079ccc37e3SMark Peek #ifdef S_IFLINK
6089ccc37e3SMark Peek 	     && ((globstar && !chase_symlinks) || !S_ISLNK(sbuf.st_mode))
6099ccc37e3SMark Peek #endif
6109ccc37e3SMark Peek 	))
6119ccc37e3SMark Peek 	return 0;
612c80476e4SDavid E. O'Brien 
61345e5710bSMark Peek     if (!(dirp = Opendir(pathbuf->s))) {
614c80476e4SDavid E. O'Brien 	/* todo: don't call for ENOENT or ENOTDIR? */
61545e5710bSMark Peek 	if ((pglob->gl_errfunc && (*pglob->gl_errfunc) (pathbuf->s, errno)) ||
616c80476e4SDavid E. O'Brien 	    (pglob->gl_flags & GLOB_ERR))
617c80476e4SDavid E. O'Brien 	    return (GLOB_ABEND);
618c80476e4SDavid E. O'Brien 	else
619c80476e4SDavid E. O'Brien 	    return (0);
620c80476e4SDavid E. O'Brien     }
621c80476e4SDavid E. O'Brien 
622c80476e4SDavid E. O'Brien     /* search directory for matching names */
623c80476e4SDavid E. O'Brien     while ((dp = readdir(dirp)) != NULL) {
624c80476e4SDavid E. O'Brien 	/* initial DOT must be matched literally */
625c80476e4SDavid E. O'Brien 	if (dp->d_name[0] == DOT && *pattern != DOT)
6269ccc37e3SMark Peek 	    if (!(pglob->gl_flags & GLOB_DOT) || !dp->d_name[1] ||
6279ccc37e3SMark Peek 		(dp->d_name[1] == DOT && !dp->d_name[2]))
6289ccc37e3SMark Peek 		continue; /*unless globdot and not . or .. */
62945e5710bSMark Peek 	pathbuf->len = orig_len;
63045e5710bSMark Peek 	strbuf_append(pathbuf, dp->d_name);
63145e5710bSMark Peek 	strbuf_terminate(pathbuf);
6329ccc37e3SMark Peek 
6339ccc37e3SMark Peek 	if (globstar) {
6349ccc37e3SMark Peek #ifdef S_IFLNK
6359ccc37e3SMark Peek 	    if (!chase_symlinks &&
6369ccc37e3SMark Peek 		(Lstat(pathbuf->s, &sbuf) || S_ISLNK(sbuf.st_mode)))
637c80476e4SDavid E. O'Brien 		    continue;
6389ccc37e3SMark Peek #endif
6399ccc37e3SMark Peek 	    if (match(pathbuf->s + orig_len, pattern, termstar,
6409ccc37e3SMark Peek 		(int)m_not) == no_match)
6419ccc37e3SMark Peek 		    continue;
6429ccc37e3SMark Peek 	    strbuf_append1(pathbuf, SEP);
6439ccc37e3SMark Peek 	    strbuf_terminate(pathbuf);
6449ccc37e3SMark Peek 	    if ((err = glob2(pathbuf, pglobstar, pglob, no_match)) != 0)
645c80476e4SDavid E. O'Brien 		break;
6469ccc37e3SMark Peek 	} else {
6479ccc37e3SMark Peek 	    if (match(pathbuf->s + orig_len, pattern, restpattern,
6489ccc37e3SMark Peek 		(int) m_not) == no_match)
6499ccc37e3SMark Peek 		continue;
6509ccc37e3SMark Peek 	    if ((err = glob2(pathbuf, restpattern, pglob, no_match)) != 0)
6519ccc37e3SMark Peek 		break;
6529ccc37e3SMark Peek 	}
653c80476e4SDavid E. O'Brien     }
654c80476e4SDavid E. O'Brien     /* todo: check error from readdir? */
65545e5710bSMark Peek     closedir(dirp);
656c80476e4SDavid E. O'Brien     return (err);
657c80476e4SDavid E. O'Brien }
658c80476e4SDavid E. O'Brien 
659c80476e4SDavid E. O'Brien 
660c80476e4SDavid E. O'Brien /*
661c80476e4SDavid E. O'Brien  * Extend the gl_pathv member of a glob_t structure to accomodate a new item,
662c80476e4SDavid E. O'Brien  * add the new item, and update gl_pathc.
663c80476e4SDavid E. O'Brien  *
664c80476e4SDavid E. O'Brien  * This assumes the BSD realloc, which only copies the block when its size
665c80476e4SDavid E. O'Brien  * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
666c80476e4SDavid E. O'Brien  * behavior.
667c80476e4SDavid E. O'Brien  *
668c80476e4SDavid E. O'Brien  * Return 0 if new item added, error code if memory couldn't be allocated.
669c80476e4SDavid E. O'Brien  *
670c80476e4SDavid E. O'Brien  * Invariant of the glob_t structure:
671c80476e4SDavid E. O'Brien  *	Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
672c80476e4SDavid E. O'Brien  *	 gl_pathv points to (gl_offs + gl_pathc + 1) items.
673c80476e4SDavid E. O'Brien  */
67445e5710bSMark Peek static void
globextend(const char * path,glob_t * pglob)67545e5710bSMark Peek globextend(const char *path, glob_t *pglob)
676c80476e4SDavid E. O'Brien {
67723338178SMark Peek     char **pathv;
67823338178SMark Peek     int i;
67945e5710bSMark Peek     size_t newsize;
680c80476e4SDavid E. O'Brien 
681c80476e4SDavid E. O'Brien     newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
68245e5710bSMark Peek     pathv = xrealloc(pglob->gl_pathv, newsize);
683c80476e4SDavid E. O'Brien 
684c80476e4SDavid E. O'Brien     if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
685c80476e4SDavid E. O'Brien 	/* first time around -- clear initial gl_offs items */
686c80476e4SDavid E. O'Brien 	pathv += pglob->gl_offs;
687c80476e4SDavid E. O'Brien 	for (i = pglob->gl_offs; --i >= 0;)
688c80476e4SDavid E. O'Brien 	    *--pathv = NULL;
689c80476e4SDavid E. O'Brien     }
690c80476e4SDavid E. O'Brien     pglob->gl_pathv = pathv;
691c80476e4SDavid E. O'Brien 
69245e5710bSMark Peek     pathv[pglob->gl_offs + pglob->gl_pathc++] = strsave(path);
693c80476e4SDavid E. O'Brien     pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
694c80476e4SDavid E. O'Brien }
695c80476e4SDavid E. O'Brien 
696c80476e4SDavid E. O'Brien /*
697cc698b49SBrooks Davis  * pattern matching function for filenames.
698c80476e4SDavid E. O'Brien  */
699c80476e4SDavid E. O'Brien static  int
match(const char * name,const Char * pat,const Char * patend,int m_not)70045e5710bSMark Peek match(const char *name, const Char *pat, const Char *patend, int m_not)
701c80476e4SDavid E. O'Brien {
702c80476e4SDavid E. O'Brien     int ok, negate_range;
703cc698b49SBrooks Davis     const Char *patNext;
704cc698b49SBrooks Davis     const char *nameNext, *nameStart, *nameEnd;
70545e5710bSMark Peek     Char c;
706c80476e4SDavid E. O'Brien 
707cc698b49SBrooks Davis     patNext = pat;
708cc698b49SBrooks Davis     nameStart = nameNext = name;
709cc698b49SBrooks Davis     nameEnd = NULL;
710cc698b49SBrooks Davis 
711cc698b49SBrooks Davis     while (pat < patend || *name) {
712cc698b49SBrooks Davis 	size_t lwk, pwk;
713*d803a9d0SBrooks Davis 	__Char wc, wk, wc1;
71423338178SMark Peek 
71523338178SMark Peek 	c = *pat; /* Only for M_MASK bits */
716cc698b49SBrooks Davis 	if (*name == EOS)
717cc698b49SBrooks Davis 		nameEnd = name;
718cc698b49SBrooks Davis 
719cc698b49SBrooks Davis 	pwk = One_Char_mbtowc(&wc, pat, MB_LEN_MAX);
72045e5710bSMark Peek 	lwk = one_mbtowc(&wk, name, MB_LEN_MAX);
721c80476e4SDavid E. O'Brien 	switch (c & M_MASK) {
722c80476e4SDavid E. O'Brien 	case M_ALL:
723cc698b49SBrooks Davis 	    while ((*(pat + pwk) & M_MASK) == M_ALL) {
724cc698b49SBrooks Davis 		pat += pwk;
725cc698b49SBrooks Davis 		pwk = One_Char_mbtowc(&wc, pat, MB_LEN_MAX);
72623338178SMark Peek 	    }
727cc698b49SBrooks Davis 	    patNext = pat;
728cc698b49SBrooks Davis 	    nameNext = name + lwk;
729cc698b49SBrooks Davis 	    pat += pwk;
730cc698b49SBrooks Davis 	    continue;
731c80476e4SDavid E. O'Brien 	case M_ONE:
73223338178SMark Peek 	    if (*name == EOS)
733c80476e4SDavid E. O'Brien 		break;
734cc698b49SBrooks Davis 	    name += lwk;
735cc698b49SBrooks Davis 	    pat += pwk;
736cc698b49SBrooks Davis 	    continue;
737c80476e4SDavid E. O'Brien 	case M_SET:
738c80476e4SDavid E. O'Brien 	    ok = 0;
73923338178SMark Peek 	    if (*name == EOS)
740cc698b49SBrooks Davis 		break;
741cc698b49SBrooks Davis 	    pat += pwk;
742cc698b49SBrooks Davis 	    pwk = One_Char_mbtowc(&wc, pat, MB_LEN_MAX);
74323338178SMark Peek 	    name += lwk;
744cc698b49SBrooks Davis 	    if ((negate_range = ((*pat & M_MASK) == m_not)) != 0) {
745cc698b49SBrooks Davis 		pat += pwk;
746cc698b49SBrooks Davis 		pwk = One_Char_mbtowc(&wc, pat, MB_LEN_MAX);
747cc698b49SBrooks Davis 	    }
748*d803a9d0SBrooks Davis 	    wc1 = wc;
74923338178SMark Peek 	    while ((*pat & M_MASK) != M_END) {
750c80476e4SDavid E. O'Brien 		if ((*pat & M_MASK) == M_RNG) {
75145e5710bSMark Peek 		    __Char wc2;
75223338178SMark Peek 
753cc698b49SBrooks Davis 		    pat += pwk;
754cc698b49SBrooks Davis 		    pwk = One_Char_mbtowc(&wc2, pat, MB_LEN_MAX);
755*d803a9d0SBrooks Davis 		    if (globcharcoll(wc1, wk, 0) <= 0 &&
75623338178SMark Peek 			globcharcoll(wk, wc2, 0) <= 0)
757c80476e4SDavid E. O'Brien 			ok = 1;
75823338178SMark Peek 		} else if (wc == wk)
759c80476e4SDavid E. O'Brien 		    ok = 1;
760cc698b49SBrooks Davis 		pat += pwk;
761*d803a9d0SBrooks Davis 		wc1 = wc;
762cc698b49SBrooks Davis 		pwk = One_Char_mbtowc(&wc, pat, MB_LEN_MAX);
763c80476e4SDavid E. O'Brien 	    }
764cc698b49SBrooks Davis 	    pat += pwk;
765cc698b49SBrooks Davis 	    pwk = One_Char_mbtowc(&wc, pat, MB_LEN_MAX);
766c80476e4SDavid E. O'Brien 	    if (ok == negate_range)
767c80476e4SDavid E. O'Brien 		break;
768cc698b49SBrooks Davis 	    continue;
769c80476e4SDavid E. O'Brien 	default:
7709ccc37e3SMark Peek 	    if (*name == EOS || samecase(wk) != samecase(wc))
771c80476e4SDavid E. O'Brien 		break;
772cc698b49SBrooks Davis 	    name += lwk;
773cc698b49SBrooks Davis 	    pat += pwk;
774cc698b49SBrooks Davis 	    continue;
775c80476e4SDavid E. O'Brien 	}
776cc698b49SBrooks Davis 	if (nameNext != nameStart
777cc698b49SBrooks Davis 	    && (nameEnd == NULL || nameNext <= nameEnd)) {
778cc698b49SBrooks Davis 	    pat = patNext;
779cc698b49SBrooks Davis 	    name = nameNext;
780cc698b49SBrooks Davis 	    continue;
781c80476e4SDavid E. O'Brien 	}
782cc698b49SBrooks Davis 	return 0;
783cc698b49SBrooks Davis     }
784cc698b49SBrooks Davis     return 1;
785c80476e4SDavid E. O'Brien }
786c80476e4SDavid E. O'Brien 
787c80476e4SDavid E. O'Brien /* free allocated data belonging to a glob_t structure */
788c80476e4SDavid E. O'Brien void
globfree(glob_t * pglob)78945e5710bSMark Peek globfree(glob_t *pglob)
790c80476e4SDavid E. O'Brien {
79123338178SMark Peek     int i;
79223338178SMark Peek     char **pp;
793c80476e4SDavid E. O'Brien 
794c80476e4SDavid E. O'Brien     if (pglob->gl_pathv != NULL) {
795c80476e4SDavid E. O'Brien 	pp = pglob->gl_pathv + pglob->gl_offs;
796c80476e4SDavid E. O'Brien 	for (i = pglob->gl_pathc; i--; ++pp)
797c80476e4SDavid E. O'Brien 	    if (*pp)
79845e5710bSMark Peek 		xfree(*pp), *pp = NULL;
79945e5710bSMark Peek 	xfree(pglob->gl_pathv), pglob->gl_pathv = NULL;
800c80476e4SDavid E. O'Brien     }
801c80476e4SDavid E. O'Brien }
802