xref: /freebsd/contrib/tcsh/glob.c (revision a15e6f9a9a49be5eae10ed6689eb2f855462ffbb)
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 
62c80476e4SDavid E. O'Brien #undef Char
63c80476e4SDavid E. O'Brien #undef QUOTE
64c80476e4SDavid E. O'Brien #undef TILDE
65c80476e4SDavid E. O'Brien #undef META
66c80476e4SDavid E. O'Brien #undef ismeta
67c80476e4SDavid E. O'Brien #undef Strchr
68c80476e4SDavid E. O'Brien 
69c80476e4SDavid E. O'Brien #ifndef S_ISDIR
70c80476e4SDavid E. O'Brien #define S_ISDIR(a)	(((a) & S_IFMT) == S_IFDIR)
71c80476e4SDavid E. O'Brien #endif
72c80476e4SDavid E. O'Brien 
73c80476e4SDavid E. O'Brien #if !defined(S_ISLNK) && defined(S_IFLNK)
74c80476e4SDavid E. O'Brien #define S_ISLNK(a)	(((a) & S_IFMT) == S_IFLNK)
75c80476e4SDavid E. O'Brien #endif
76c80476e4SDavid E. O'Brien 
77c80476e4SDavid E. O'Brien #if !defined(S_ISLNK) && !defined(lstat)
78c80476e4SDavid E. O'Brien #define lstat stat
79c80476e4SDavid E. O'Brien #endif
80c80476e4SDavid E. O'Brien 
81c80476e4SDavid E. O'Brien typedef unsigned short Char;
82c80476e4SDavid E. O'Brien 
8345e5710bSMark Peek static	int	 glob1 		(Char *, glob_t *, int);
8445e5710bSMark Peek static	int	 glob2		(struct strbuf *, const Char *, glob_t *, int);
8545e5710bSMark Peek static	int	 glob3		(struct strbuf *, const Char *, const Char *,
8645e5710bSMark Peek 				 glob_t *, int);
8745e5710bSMark Peek static	void	 globextend	(const char *, glob_t *);
8845e5710bSMark Peek static	int	 match		(const char *, const Char *, const Char *,
8945e5710bSMark Peek 				 int);
9045e5710bSMark Peek static	int	 compare	(const void *, const void *);
9145e5710bSMark Peek static 	DIR	*Opendir	(const char *);
92c80476e4SDavid E. O'Brien #ifdef S_IFLNK
9345e5710bSMark Peek static	int	 Lstat		(const char *, struct stat *);
94c80476e4SDavid E. O'Brien #endif
9545e5710bSMark Peek static	int	 Stat		(const char *, struct stat *sb);
9645e5710bSMark Peek static 	Char 	*Strchr		(Char *, int);
97c80476e4SDavid E. O'Brien #ifdef DEBUG
9845e5710bSMark Peek static	void	 qprintf	(const Char *);
99c80476e4SDavid E. O'Brien #endif
100c80476e4SDavid E. O'Brien 
101c80476e4SDavid E. O'Brien #define	DOLLAR		'$'
102c80476e4SDavid E. O'Brien #define	DOT		'.'
103c80476e4SDavid E. O'Brien #define	EOS		'\0'
104c80476e4SDavid E. O'Brien #define	LBRACKET	'['
105c80476e4SDavid E. O'Brien #define	NOT		'!'
106c80476e4SDavid E. O'Brien #define ALTNOT		'^'
107c80476e4SDavid E. O'Brien #define	QUESTION	'?'
108c80476e4SDavid E. O'Brien #define	QUOTE		'\\'
109c80476e4SDavid E. O'Brien #define	RANGE		'-'
110c80476e4SDavid E. O'Brien #define	RBRACKET	']'
111c80476e4SDavid E. O'Brien #define	SEP		'/'
112c80476e4SDavid E. O'Brien #define	STAR		'*'
113c80476e4SDavid E. O'Brien #define	TILDE		'~'
114c80476e4SDavid E. O'Brien #define	UNDERSCORE	'_'
115c80476e4SDavid E. O'Brien 
116c80476e4SDavid E. O'Brien #define	M_META		0x8000
117c80476e4SDavid E. O'Brien #define M_PROTECT	0x4000
118c80476e4SDavid E. O'Brien #define	M_MASK		0xffff
119c80476e4SDavid E. O'Brien #define	M_ASCII		0x00ff
120c80476e4SDavid E. O'Brien 
12145e5710bSMark Peek #define	LCHAR(c)	((c)&M_ASCII)
122c80476e4SDavid E. O'Brien #define	META(c)		((c)|M_META)
123c80476e4SDavid E. O'Brien #define	M_ALL		META('*')
124c80476e4SDavid E. O'Brien #define	M_END		META(']')
125c80476e4SDavid E. O'Brien #define	M_NOT		META('!')
126c80476e4SDavid E. O'Brien #define	M_ALTNOT	META('^')
127c80476e4SDavid E. O'Brien #define	M_ONE		META('?')
128c80476e4SDavid E. O'Brien #define	M_RNG		META('-')
129c80476e4SDavid E. O'Brien #define	M_SET		META('[')
130c80476e4SDavid E. O'Brien #define	ismeta(c)	(((c)&M_META) != 0)
131c80476e4SDavid E. O'Brien 
132c80476e4SDavid E. O'Brien int
13345e5710bSMark Peek globcharcoll(__Char c1, __Char c2, int cs)
134c80476e4SDavid E. O'Brien {
13545e5710bSMark Peek #if defined(NLS) && defined(LC_COLLATE) && defined(HAVE_STRCOLL)
13645e5710bSMark Peek # if defined(WIDE_STRINGS)
13723338178SMark Peek     wchar_t s1[2], s2[2];
13823338178SMark Peek 
13923338178SMark Peek     if (c1 == c2)
14023338178SMark Peek 	return (0);
14123338178SMark Peek     if (cs) {
14223338178SMark Peek 	c1 = towlower(c1);
14323338178SMark Peek 	c2 = towlower(c2);
14423338178SMark Peek     } else {
14523338178SMark Peek 	/* This should not be here, but I'll rather leave it in than engage in
14623338178SMark Peek 	   a LC_COLLATE flamewar about a shell I don't use... */
14723338178SMark Peek 	if (iswlower(c1) && iswupper(c2))
14823338178SMark Peek 	    return (1);
14923338178SMark Peek 	if (iswupper(c1) && iswlower(c2))
15023338178SMark Peek 	    return (-1);
15123338178SMark Peek     }
15223338178SMark Peek     s1[0] = c1;
15323338178SMark Peek     s2[0] = c2;
15423338178SMark Peek     s1[1] = s2[1] = '\0';
15523338178SMark Peek     return wcscoll(s1, s2);
15645e5710bSMark Peek # else /* not WIDE_STRINGS */
157c80476e4SDavid E. O'Brien     char s1[2], s2[2];
158c80476e4SDavid E. O'Brien 
159c80476e4SDavid E. O'Brien     if (c1 == c2)
160c80476e4SDavid E. O'Brien 	return (0);
1618e66bd9eSDavid E. O'Brien     /*
1628e66bd9eSDavid E. O'Brien      * From kevin lyda <kevin@suberic.net>:
1638e66bd9eSDavid E. O'Brien      * strcoll does not guarantee case sorting, so we pre-process now:
1648e66bd9eSDavid E. O'Brien      */
165b2d5d167SMark Peek     if (cs) {
166b2d5d167SMark Peek 	c1 = islower(c1) ? c1 : tolower(c1);
167b2d5d167SMark Peek 	c2 = islower(c2) ? c2 : tolower(c2);
168b2d5d167SMark Peek     } else {
1698e66bd9eSDavid E. O'Brien 	if (islower(c1) && isupper(c2))
1708e66bd9eSDavid E. O'Brien 	    return (1);
17123338178SMark Peek 	if (isupper(c1) && islower(c2))
17223338178SMark Peek 	    return (-1);
173b2d5d167SMark Peek     }
174c80476e4SDavid E. O'Brien     s1[0] = c1;
175c80476e4SDavid E. O'Brien     s2[0] = c2;
176c80476e4SDavid E. O'Brien     s1[1] = s2[1] = '\0';
177c80476e4SDavid E. O'Brien     return strcoll(s1, s2);
17823338178SMark Peek # endif
179c80476e4SDavid E. O'Brien #else
180c80476e4SDavid E. O'Brien     return (c1 - c2);
181c80476e4SDavid E. O'Brien #endif
182c80476e4SDavid E. O'Brien }
183c80476e4SDavid E. O'Brien 
184c80476e4SDavid E. O'Brien /*
185c80476e4SDavid E. O'Brien  * Need to dodge two kernel bugs:
186c80476e4SDavid E. O'Brien  * opendir("") != opendir(".")
187c80476e4SDavid E. O'Brien  * NAMEI_BUG: on plain files trailing slashes are ignored in some kernels.
188c80476e4SDavid E. O'Brien  *            POSIX specifies that they should be ignored in directories.
189c80476e4SDavid E. O'Brien  */
190c80476e4SDavid E. O'Brien 
191c80476e4SDavid E. O'Brien static DIR *
19245e5710bSMark Peek Opendir(const char *str)
193c80476e4SDavid E. O'Brien {
194c80476e4SDavid E. O'Brien #if defined(hpux) || defined(__hpux)
195c80476e4SDavid E. O'Brien     struct stat st;
196c80476e4SDavid E. O'Brien #endif
197c80476e4SDavid E. O'Brien 
198c80476e4SDavid E. O'Brien     if (!*str)
199c80476e4SDavid E. O'Brien 	return (opendir("."));
200c80476e4SDavid E. O'Brien #if defined(hpux) || defined(__hpux)
201c80476e4SDavid E. O'Brien     /*
202c80476e4SDavid E. O'Brien      * Opendir on some device files hangs, so avoid it
203c80476e4SDavid E. O'Brien      */
20445e5710bSMark Peek     if (stat(str, &st) == -1 || !S_ISDIR(st.st_mode))
205c80476e4SDavid E. O'Brien 	return NULL;
206c80476e4SDavid E. O'Brien #endif
20745e5710bSMark Peek     return opendir(str);
208c80476e4SDavid E. O'Brien }
209c80476e4SDavid E. O'Brien 
210c80476e4SDavid E. O'Brien #ifdef S_IFLNK
211c80476e4SDavid E. O'Brien static int
21245e5710bSMark Peek Lstat(const char *fn, struct stat *sb)
213c80476e4SDavid E. O'Brien {
214c80476e4SDavid E. O'Brien     int st;
215c80476e4SDavid E. O'Brien 
21645e5710bSMark Peek     st = lstat(fn, sb);
21745e5710bSMark Peek # ifdef NAMEI_BUG
21845e5710bSMark Peek     if (*fn != 0 && strend(fn)[-1] == '/' && !S_ISDIR(sb->st_mode))
21945e5710bSMark Peek 	st = -1;
220c80476e4SDavid E. O'Brien # endif	/* NAMEI_BUG */
22145e5710bSMark Peek     return st;
222c80476e4SDavid E. O'Brien }
223c80476e4SDavid E. O'Brien #else
224c80476e4SDavid E. O'Brien #define Lstat Stat
225c80476e4SDavid E. O'Brien #endif /* S_IFLNK */
226c80476e4SDavid E. O'Brien 
227c80476e4SDavid E. O'Brien static int
22845e5710bSMark Peek Stat(const char *fn, struct stat *sb)
229c80476e4SDavid E. O'Brien {
230c80476e4SDavid E. O'Brien     int st;
231c80476e4SDavid E. O'Brien 
23245e5710bSMark Peek     st = stat(fn, sb);
23345e5710bSMark Peek #ifdef NAMEI_BUG
23445e5710bSMark Peek     if (*fn != 0 && strend(fn)[-1] == '/' && !S_ISDIR(sb->st_mode))
23545e5710bSMark Peek 	st = -1;
236c80476e4SDavid E. O'Brien #endif /* NAMEI_BUG */
23745e5710bSMark Peek     return st;
238c80476e4SDavid E. O'Brien }
239c80476e4SDavid E. O'Brien 
240c80476e4SDavid E. O'Brien static Char *
24145e5710bSMark Peek Strchr(Char *str, int ch)
242c80476e4SDavid E. O'Brien {
243c80476e4SDavid E. O'Brien     do
244c80476e4SDavid E. O'Brien 	if (*str == ch)
245c80476e4SDavid E. O'Brien 	    return (str);
246c80476e4SDavid E. O'Brien     while (*str++);
247c80476e4SDavid E. O'Brien     return (NULL);
248c80476e4SDavid E. O'Brien }
249c80476e4SDavid E. O'Brien 
250c80476e4SDavid E. O'Brien #ifdef DEBUG
251c80476e4SDavid E. O'Brien static void
25245e5710bSMark Peek qprintf(const Char *s)
253c80476e4SDavid E. O'Brien {
25445e5710bSMark Peek     const Char *p;
255c80476e4SDavid E. O'Brien 
256c80476e4SDavid E. O'Brien     for (p = s; *p; p++)
257c80476e4SDavid E. O'Brien 	printf("%c", *p & 0xff);
258c80476e4SDavid E. O'Brien     printf("\n");
259c80476e4SDavid E. O'Brien     for (p = s; *p; p++)
260c80476e4SDavid E. O'Brien 	printf("%c", *p & M_PROTECT ? '"' : ' ');
261c80476e4SDavid E. O'Brien     printf("\n");
262c80476e4SDavid E. O'Brien     for (p = s; *p; p++)
263c80476e4SDavid E. O'Brien 	printf("%c", *p & M_META ? '_' : ' ');
264c80476e4SDavid E. O'Brien     printf("\n");
265c80476e4SDavid E. O'Brien }
266c80476e4SDavid E. O'Brien #endif /* DEBUG */
267c80476e4SDavid E. O'Brien 
268c80476e4SDavid E. O'Brien static int
26945e5710bSMark Peek compare(const void *p, const void *q)
270c80476e4SDavid E. O'Brien {
27145e5710bSMark Peek #if defined(NLS) && defined(HAVE_STRCOLL)
27245e5710bSMark Peek     return (strcoll(*(char *const *) p, *(char *const *) q));
273c80476e4SDavid E. O'Brien #else
27445e5710bSMark Peek     return (strcmp(*(char *const *) p, *(char *const *) q));
27545e5710bSMark Peek #endif /* NLS && HAVE_STRCOLL */
276c80476e4SDavid E. O'Brien }
277c80476e4SDavid E. O'Brien 
278c80476e4SDavid E. O'Brien /*
279c80476e4SDavid E. O'Brien  * The main glob() routine: compiles the pattern (optionally processing
280c80476e4SDavid E. O'Brien  * quotes), calls glob1() to do the real pattern matching, and finally
281c80476e4SDavid E. O'Brien  * sorts the list (unless unsorted operation is requested).  Returns 0
282c80476e4SDavid E. O'Brien  * if things went well, nonzero if errors occurred.  It is not an error
283c80476e4SDavid E. O'Brien  * to find no matches.
284c80476e4SDavid E. O'Brien  */
285c80476e4SDavid E. O'Brien int
28645e5710bSMark Peek glob(const char *pattern, int flags, int (*errfunc) (const char *, int),
28745e5710bSMark Peek      glob_t *pglob)
288c80476e4SDavid E. O'Brien {
289c80476e4SDavid E. O'Brien     int     err, oldpathc;
29045e5710bSMark Peek     Char *bufnext, m_not;
29145e5710bSMark Peek     const unsigned char *patnext;
292c80476e4SDavid E. O'Brien     int     c, not;
29345e5710bSMark Peek     Char *qpatnext, *patbuf;
294c80476e4SDavid E. O'Brien     int     no_match;
295c80476e4SDavid E. O'Brien 
29623338178SMark Peek     patnext = (const unsigned char *) pattern;
297c80476e4SDavid E. O'Brien     if (!(flags & GLOB_APPEND)) {
298c80476e4SDavid E. O'Brien 	pglob->gl_pathc = 0;
299c80476e4SDavid E. O'Brien 	pglob->gl_pathv = NULL;
300c80476e4SDavid E. O'Brien 	if (!(flags & GLOB_DOOFFS))
301c80476e4SDavid E. O'Brien 	    pglob->gl_offs = 0;
302c80476e4SDavid E. O'Brien     }
303c80476e4SDavid E. O'Brien     pglob->gl_flags = flags & ~GLOB_MAGCHAR;
304c80476e4SDavid E. O'Brien     pglob->gl_errfunc = errfunc;
305c80476e4SDavid E. O'Brien     oldpathc = pglob->gl_pathc;
306c80476e4SDavid E. O'Brien     pglob->gl_matchc = 0;
307c80476e4SDavid E. O'Brien 
308c80476e4SDavid E. O'Brien     if (pglob->gl_flags & GLOB_ALTNOT) {
309c80476e4SDavid E. O'Brien 	not = ALTNOT;
310c80476e4SDavid E. O'Brien 	m_not = M_ALTNOT;
311c80476e4SDavid E. O'Brien     }
312c80476e4SDavid E. O'Brien     else {
313c80476e4SDavid E. O'Brien 	not = NOT;
314c80476e4SDavid E. O'Brien 	m_not = M_NOT;
315c80476e4SDavid E. O'Brien     }
316c80476e4SDavid E. O'Brien 
31745e5710bSMark Peek     patbuf = xmalloc((strlen(pattern) + 1) * sizeof(*patbuf));
318c80476e4SDavid E. O'Brien     bufnext = patbuf;
319c80476e4SDavid E. O'Brien 
320c80476e4SDavid E. O'Brien     no_match = *patnext == not;
321c80476e4SDavid E. O'Brien     if (no_match)
322c80476e4SDavid E. O'Brien 	patnext++;
323c80476e4SDavid E. O'Brien 
324c80476e4SDavid E. O'Brien     if (flags & GLOB_QUOTE) {
325c80476e4SDavid E. O'Brien 	/* Protect the quoted characters */
32645e5710bSMark Peek 	while ((c = *patnext++) != EOS) {
32723338178SMark Peek #ifdef WIDE_STRINGS
32823338178SMark Peek 	    int len;
32923338178SMark Peek 
33023338178SMark Peek 	    len = mblen((const char *)(patnext - 1), MB_LEN_MAX);
33123338178SMark Peek 	    if (len == -1)
332a15e6f9aSMark Peek 		(void)mblen(NULL, 0);
333a15e6f9aSMark Peek 	    else if (len > 1) {
3348e66bd9eSDavid E. O'Brien 		*bufnext++ = (Char) c;
33523338178SMark Peek 		while (--len != 0)
33623338178SMark Peek 		    *bufnext++ = (Char) (*patnext++ | M_PROTECT);
33723338178SMark Peek 	    } else
33823338178SMark Peek #endif /* WIDE_STRINGS */
339c80476e4SDavid E. O'Brien 	    if (c == QUOTE) {
340c80476e4SDavid E. O'Brien 		if ((c = *patnext++) == EOS) {
341c80476e4SDavid E. O'Brien 		    c = QUOTE;
342c80476e4SDavid E. O'Brien 		    --patnext;
343c80476e4SDavid E. O'Brien 		}
344c80476e4SDavid E. O'Brien 		*bufnext++ = (Char) (c | M_PROTECT);
345c80476e4SDavid E. O'Brien 	    }
346c80476e4SDavid E. O'Brien 	    else
347c80476e4SDavid E. O'Brien 		*bufnext++ = (Char) c;
348c80476e4SDavid E. O'Brien 	}
34923338178SMark Peek     }
350c80476e4SDavid E. O'Brien     else
35145e5710bSMark Peek 	while ((c = *patnext++) != EOS)
352c80476e4SDavid E. O'Brien 	    *bufnext++ = (Char) c;
353c80476e4SDavid E. O'Brien     *bufnext = EOS;
354c80476e4SDavid E. O'Brien 
355c80476e4SDavid E. O'Brien     bufnext = patbuf;
356c80476e4SDavid E. O'Brien     qpatnext = patbuf;
357c80476e4SDavid E. O'Brien     while ((c = *qpatnext++) != EOS) {
358c80476e4SDavid E. O'Brien 	switch (c) {
359c80476e4SDavid E. O'Brien 	case LBRACKET:
360c80476e4SDavid E. O'Brien 	    c = *qpatnext;
361c80476e4SDavid E. O'Brien 	    if (c == not)
362c80476e4SDavid E. O'Brien 		++qpatnext;
363c80476e4SDavid E. O'Brien 	    if (*qpatnext == EOS ||
364c80476e4SDavid E. O'Brien 		Strchr(qpatnext + 1, RBRACKET) == NULL) {
365c80476e4SDavid E. O'Brien 		*bufnext++ = LBRACKET;
366c80476e4SDavid E. O'Brien 		if (c == not)
367c80476e4SDavid E. O'Brien 		    --qpatnext;
368c80476e4SDavid E. O'Brien 		break;
369c80476e4SDavid E. O'Brien 	    }
370c80476e4SDavid E. O'Brien 	    pglob->gl_flags |= GLOB_MAGCHAR;
371c80476e4SDavid E. O'Brien 	    *bufnext++ = M_SET;
372c80476e4SDavid E. O'Brien 	    if (c == not)
373c80476e4SDavid E. O'Brien 		*bufnext++ = m_not;
374c80476e4SDavid E. O'Brien 	    c = *qpatnext++;
375c80476e4SDavid E. O'Brien 	    do {
37645e5710bSMark Peek 		*bufnext++ = LCHAR(c);
377c80476e4SDavid E. O'Brien 		if (*qpatnext == RANGE &&
378c80476e4SDavid E. O'Brien 		    (c = qpatnext[1]) != RBRACKET) {
379c80476e4SDavid E. O'Brien 		    *bufnext++ = M_RNG;
38045e5710bSMark Peek 		    *bufnext++ = LCHAR(c);
381c80476e4SDavid E. O'Brien 		    qpatnext += 2;
382c80476e4SDavid E. O'Brien 		}
383c80476e4SDavid E. O'Brien 	    } while ((c = *qpatnext++) != RBRACKET);
384c80476e4SDavid E. O'Brien 	    *bufnext++ = M_END;
385c80476e4SDavid E. O'Brien 	    break;
386c80476e4SDavid E. O'Brien 	case QUESTION:
387c80476e4SDavid E. O'Brien 	    pglob->gl_flags |= GLOB_MAGCHAR;
388c80476e4SDavid E. O'Brien 	    *bufnext++ = M_ONE;
389c80476e4SDavid E. O'Brien 	    break;
390c80476e4SDavid E. O'Brien 	case STAR:
391c80476e4SDavid E. O'Brien 	    pglob->gl_flags |= GLOB_MAGCHAR;
392c80476e4SDavid E. O'Brien 	    /* collapse adjacent stars to one, to avoid
393c80476e4SDavid E. O'Brien 	     * exponential behavior
394c80476e4SDavid E. O'Brien 	     */
395c80476e4SDavid E. O'Brien 	    if (bufnext == patbuf || bufnext[-1] != M_ALL)
396c80476e4SDavid E. O'Brien 		*bufnext++ = M_ALL;
397c80476e4SDavid E. O'Brien 	    break;
398c80476e4SDavid E. O'Brien 	default:
39945e5710bSMark Peek 	    *bufnext++ = LCHAR(c);
400c80476e4SDavid E. O'Brien 	    break;
401c80476e4SDavid E. O'Brien 	}
402c80476e4SDavid E. O'Brien     }
403c80476e4SDavid E. O'Brien     *bufnext = EOS;
404c80476e4SDavid E. O'Brien #ifdef DEBUG
405c80476e4SDavid E. O'Brien     qprintf(patbuf);
406c80476e4SDavid E. O'Brien #endif
407c80476e4SDavid E. O'Brien 
40845e5710bSMark Peek     if ((err = glob1(patbuf, pglob, no_match)) != 0) {
40945e5710bSMark Peek 	xfree(patbuf);
410c80476e4SDavid E. O'Brien 	return (err);
41145e5710bSMark Peek     }
412c80476e4SDavid E. O'Brien 
413c80476e4SDavid E. O'Brien     /*
414c80476e4SDavid E. O'Brien      * If there was no match we are going to append the pattern
415c80476e4SDavid E. O'Brien      * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
416c80476e4SDavid E. O'Brien      * and the pattern did not contain any magic characters
417c80476e4SDavid E. O'Brien      * GLOB_NOMAGIC is there just for compatibility with csh.
418c80476e4SDavid E. O'Brien      */
419c80476e4SDavid E. O'Brien     if (pglob->gl_pathc == oldpathc &&
420c80476e4SDavid E. O'Brien 	((flags & GLOB_NOCHECK) ||
421c80476e4SDavid E. O'Brien 	 ((flags & GLOB_NOMAGIC) && !(pglob->gl_flags & GLOB_MAGCHAR)))) {
42245e5710bSMark Peek 	if (!(flags & GLOB_QUOTE))
42345e5710bSMark Peek 	    globextend(pattern, pglob);
424c80476e4SDavid E. O'Brien 	else {
42545e5710bSMark Peek 	    char *copy, *dest;
42645e5710bSMark Peek 	    const char *src;
42745e5710bSMark Peek 
42845e5710bSMark Peek 	    /* copy pattern, interpreting quotes */
42945e5710bSMark Peek 	    copy = xmalloc(strlen(pattern) + 1);
43045e5710bSMark Peek 	    dest = copy;
43145e5710bSMark Peek 	    src = pattern;
43245e5710bSMark Peek 	    while (*src != EOS) {
43345e5710bSMark Peek 		if (*src == QUOTE) {
43445e5710bSMark Peek 		    if (*++src == EOS)
43545e5710bSMark Peek 			--src;
436c80476e4SDavid E. O'Brien 		}
43745e5710bSMark Peek 		*dest++ = *src++;
438c80476e4SDavid E. O'Brien 	    }
43945e5710bSMark Peek 	    *dest = EOS;
44045e5710bSMark Peek 	    globextend(copy, pglob);
44145e5710bSMark Peek 	    xfree(copy);
442c80476e4SDavid E. O'Brien 	}
44345e5710bSMark Peek 	xfree(patbuf);
44445e5710bSMark Peek 	return 0;
445c80476e4SDavid E. O'Brien     }
4468e66bd9eSDavid E. O'Brien     else if (!(flags & GLOB_NOSORT) && (pglob->gl_pathc != oldpathc))
44745e5710bSMark Peek 	qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
44845e5710bSMark Peek 	      pglob->gl_pathc - oldpathc, sizeof(char *), compare);
44945e5710bSMark Peek     xfree(patbuf);
450c80476e4SDavid E. O'Brien     return (0);
451c80476e4SDavid E. O'Brien }
452c80476e4SDavid E. O'Brien 
453c80476e4SDavid E. O'Brien static int
45445e5710bSMark Peek glob1(Char *pattern, glob_t *pglob, int no_match)
455c80476e4SDavid E. O'Brien {
45645e5710bSMark Peek     struct strbuf pathbuf = strbuf_INIT;
45745e5710bSMark Peek     int err;
458c80476e4SDavid E. O'Brien 
459c80476e4SDavid E. O'Brien     /*
460c80476e4SDavid E. O'Brien      * a null pathname is invalid -- POSIX 1003.1 sect. 2.4.
461c80476e4SDavid E. O'Brien      */
462c80476e4SDavid E. O'Brien     if (*pattern == EOS)
463c80476e4SDavid E. O'Brien 	return (0);
46445e5710bSMark Peek     err = glob2(&pathbuf, pattern, pglob, no_match);
46545e5710bSMark Peek     xfree(pathbuf.s);
46645e5710bSMark Peek     return err;
467c80476e4SDavid E. O'Brien }
468c80476e4SDavid E. O'Brien 
469c80476e4SDavid E. O'Brien /*
470c80476e4SDavid E. O'Brien  * functions glob2 and glob3 are mutually recursive; there is one level
471c80476e4SDavid E. O'Brien  * of recursion for each segment in the pattern that contains one or
472c80476e4SDavid E. O'Brien  * more meta characters.
473c80476e4SDavid E. O'Brien  */
474c80476e4SDavid E. O'Brien static int
47545e5710bSMark Peek glob2(struct strbuf *pathbuf, const Char *pattern, glob_t *pglob, int no_match)
476c80476e4SDavid E. O'Brien {
477c80476e4SDavid E. O'Brien     struct stat sbuf;
478c80476e4SDavid E. O'Brien     int anymeta;
47945e5710bSMark Peek     const Char *p;
48045e5710bSMark Peek     size_t orig_len;
481c80476e4SDavid E. O'Brien 
482c80476e4SDavid E. O'Brien     /*
483c80476e4SDavid E. O'Brien      * loop over pattern segments until end of pattern or until segment with
484c80476e4SDavid E. O'Brien      * meta character found.
485c80476e4SDavid E. O'Brien      */
486c80476e4SDavid E. O'Brien     anymeta = 0;
487c80476e4SDavid E. O'Brien     for (;;) {
488c80476e4SDavid E. O'Brien 	if (*pattern == EOS) {	/* end of pattern? */
48945e5710bSMark Peek 	    strbuf_terminate(pathbuf);
490c80476e4SDavid E. O'Brien 
49145e5710bSMark Peek 	    if (Lstat(pathbuf->s, &sbuf))
492c80476e4SDavid E. O'Brien 		return (0);
493c80476e4SDavid E. O'Brien 
494c80476e4SDavid E. O'Brien 	    if (((pglob->gl_flags & GLOB_MARK) &&
49545e5710bSMark Peek 		 pathbuf->s[pathbuf->len - 1] != SEP) &&
496c80476e4SDavid E. O'Brien 		(S_ISDIR(sbuf.st_mode)
497c80476e4SDavid E. O'Brien #ifdef S_IFLNK
498c80476e4SDavid E. O'Brien 		 || (S_ISLNK(sbuf.st_mode) &&
49945e5710bSMark Peek 		     (Stat(pathbuf->s, &sbuf) == 0) &&
500c80476e4SDavid E. O'Brien 		     S_ISDIR(sbuf.st_mode))
501c80476e4SDavid E. O'Brien #endif
502c80476e4SDavid E. O'Brien 		 )) {
50345e5710bSMark Peek 		strbuf_append1(pathbuf, SEP);
50445e5710bSMark Peek 		strbuf_terminate(pathbuf);
505c80476e4SDavid E. O'Brien 	    }
506c80476e4SDavid E. O'Brien 	    ++pglob->gl_matchc;
50745e5710bSMark Peek 	    globextend(pathbuf->s, pglob);
50845e5710bSMark Peek 	    return 0;
509c80476e4SDavid E. O'Brien 	}
510c80476e4SDavid E. O'Brien 
51145e5710bSMark Peek 	/* find end of next segment, tentatively copy to pathbuf */
512c80476e4SDavid E. O'Brien 	p = pattern;
51345e5710bSMark Peek 	orig_len = pathbuf->len;
514c80476e4SDavid E. O'Brien 	while (*p != EOS && *p != SEP) {
515c80476e4SDavid E. O'Brien 	    if (ismeta(*p))
516c80476e4SDavid E. O'Brien 		anymeta = 1;
51745e5710bSMark Peek 	    strbuf_append1(pathbuf, *p++);
518c80476e4SDavid E. O'Brien 	}
519c80476e4SDavid E. O'Brien 
520c80476e4SDavid E. O'Brien 	if (!anymeta) {		/* no expansion, do next segment */
521c80476e4SDavid E. O'Brien 	    pattern = p;
522c80476e4SDavid E. O'Brien 	    while (*pattern == SEP)
52345e5710bSMark Peek 		strbuf_append1(pathbuf, *pattern++);
524c80476e4SDavid E. O'Brien 	}
52545e5710bSMark Peek 	else {			/* need expansion, recurse */
52645e5710bSMark Peek 	    pathbuf->len = orig_len;
52745e5710bSMark Peek 	    return (glob3(pathbuf, pattern, p, pglob, no_match));
52845e5710bSMark Peek 	}
529c80476e4SDavid E. O'Brien     }
530c80476e4SDavid E. O'Brien     /* NOTREACHED */
531c80476e4SDavid E. O'Brien }
532c80476e4SDavid E. O'Brien 
533c80476e4SDavid E. O'Brien 
534c80476e4SDavid E. O'Brien static int
53545e5710bSMark Peek glob3(struct strbuf *pathbuf, const Char *pattern, const Char *restpattern,
53645e5710bSMark Peek       glob_t *pglob, int no_match)
537c80476e4SDavid E. O'Brien {
538c80476e4SDavid E. O'Brien     DIR    *dirp;
539c80476e4SDavid E. O'Brien     struct dirent *dp;
540c80476e4SDavid E. O'Brien     int     err;
541c80476e4SDavid E. O'Brien     Char m_not = (pglob->gl_flags & GLOB_ALTNOT) ? M_ALTNOT : M_NOT;
54245e5710bSMark Peek     size_t orig_len;
543c80476e4SDavid E. O'Brien 
54445e5710bSMark Peek     strbuf_terminate(pathbuf);
545c80476e4SDavid E. O'Brien     errno = 0;
546c80476e4SDavid E. O'Brien 
54745e5710bSMark Peek     if (!(dirp = Opendir(pathbuf->s))) {
548c80476e4SDavid E. O'Brien 	/* todo: don't call for ENOENT or ENOTDIR? */
54945e5710bSMark Peek 	if ((pglob->gl_errfunc && (*pglob->gl_errfunc) (pathbuf->s, errno)) ||
550c80476e4SDavid E. O'Brien 	    (pglob->gl_flags & GLOB_ERR))
551c80476e4SDavid E. O'Brien 	    return (GLOB_ABEND);
552c80476e4SDavid E. O'Brien 	else
553c80476e4SDavid E. O'Brien 	    return (0);
554c80476e4SDavid E. O'Brien     }
555c80476e4SDavid E. O'Brien 
556c80476e4SDavid E. O'Brien     err = 0;
557c80476e4SDavid E. O'Brien 
55845e5710bSMark Peek     orig_len = pathbuf->len;
559c80476e4SDavid E. O'Brien     /* search directory for matching names */
560c80476e4SDavid E. O'Brien     while ((dp = readdir(dirp)) != NULL) {
561c80476e4SDavid E. O'Brien 	/* initial DOT must be matched literally */
562c80476e4SDavid E. O'Brien 	if (dp->d_name[0] == DOT && *pattern != DOT)
563c80476e4SDavid E. O'Brien 	    continue;
56445e5710bSMark Peek 	pathbuf->len = orig_len;
56545e5710bSMark Peek 	strbuf_append(pathbuf, dp->d_name);
56645e5710bSMark Peek 	strbuf_terminate(pathbuf);
56745e5710bSMark Peek 	if (match(pathbuf->s + orig_len, pattern, restpattern, (int) m_not)
56845e5710bSMark Peek 	    == no_match)
569c80476e4SDavid E. O'Brien 	    continue;
57045e5710bSMark Peek 	err = glob2(pathbuf, restpattern, pglob, no_match);
571c80476e4SDavid E. O'Brien 	if (err)
572c80476e4SDavid E. O'Brien 	    break;
573c80476e4SDavid E. O'Brien     }
574c80476e4SDavid E. O'Brien     /* todo: check error from readdir? */
57545e5710bSMark Peek     closedir(dirp);
576c80476e4SDavid E. O'Brien     return (err);
577c80476e4SDavid E. O'Brien }
578c80476e4SDavid E. O'Brien 
579c80476e4SDavid E. O'Brien 
580c80476e4SDavid E. O'Brien /*
581c80476e4SDavid E. O'Brien  * Extend the gl_pathv member of a glob_t structure to accomodate a new item,
582c80476e4SDavid E. O'Brien  * add the new item, and update gl_pathc.
583c80476e4SDavid E. O'Brien  *
584c80476e4SDavid E. O'Brien  * This assumes the BSD realloc, which only copies the block when its size
585c80476e4SDavid E. O'Brien  * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
586c80476e4SDavid E. O'Brien  * behavior.
587c80476e4SDavid E. O'Brien  *
588c80476e4SDavid E. O'Brien  * Return 0 if new item added, error code if memory couldn't be allocated.
589c80476e4SDavid E. O'Brien  *
590c80476e4SDavid E. O'Brien  * Invariant of the glob_t structure:
591c80476e4SDavid E. O'Brien  *	Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
592c80476e4SDavid E. O'Brien  *	 gl_pathv points to (gl_offs + gl_pathc + 1) items.
593c80476e4SDavid E. O'Brien  */
59445e5710bSMark Peek static void
59545e5710bSMark Peek globextend(const char *path, glob_t *pglob)
596c80476e4SDavid E. O'Brien {
59723338178SMark Peek     char **pathv;
59823338178SMark Peek     int i;
59945e5710bSMark Peek     size_t newsize;
600c80476e4SDavid E. O'Brien 
601c80476e4SDavid E. O'Brien     newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
60245e5710bSMark Peek     pathv = xrealloc(pglob->gl_pathv, newsize);
603c80476e4SDavid E. O'Brien 
604c80476e4SDavid E. O'Brien     if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
605c80476e4SDavid E. O'Brien 	/* first time around -- clear initial gl_offs items */
606c80476e4SDavid E. O'Brien 	pathv += pglob->gl_offs;
607c80476e4SDavid E. O'Brien 	for (i = pglob->gl_offs; --i >= 0;)
608c80476e4SDavid E. O'Brien 	    *--pathv = NULL;
609c80476e4SDavid E. O'Brien     }
610c80476e4SDavid E. O'Brien     pglob->gl_pathv = pathv;
611c80476e4SDavid E. O'Brien 
61245e5710bSMark Peek     pathv[pglob->gl_offs + pglob->gl_pathc++] = strsave(path);
613c80476e4SDavid E. O'Brien     pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
614c80476e4SDavid E. O'Brien }
615c80476e4SDavid E. O'Brien 
61623338178SMark Peek static size_t
61745e5710bSMark Peek One_Char_mbtowc(__Char *pwc, const Char *s, size_t n)
61823338178SMark Peek {
61923338178SMark Peek #ifdef WIDE_STRINGS
62023338178SMark Peek     char buf[MB_LEN_MAX], *p;
62123338178SMark Peek 
62223338178SMark Peek     if (n > MB_LEN_MAX)
62323338178SMark Peek 	n = MB_LEN_MAX;
62423338178SMark Peek     p = buf;
62545e5710bSMark Peek     while (p < buf + n && (*p++ = LCHAR(*s++)) != 0)
62623338178SMark Peek 	;
62723338178SMark Peek     return one_mbtowc(pwc, buf, n);
62823338178SMark Peek #else
62945e5710bSMark Peek     *pwc = *s & CHAR;
63045e5710bSMark Peek     return 1;
63123338178SMark Peek #endif
63223338178SMark Peek }
63323338178SMark Peek 
634c80476e4SDavid E. O'Brien /*
635c80476e4SDavid E. O'Brien  * pattern matching function for filenames.  Each occurrence of the *
636c80476e4SDavid E. O'Brien  * pattern causes a recursion level.
637c80476e4SDavid E. O'Brien  */
638c80476e4SDavid E. O'Brien static  int
63945e5710bSMark Peek match(const char *name, const Char *pat, const Char *patend, int m_not)
640c80476e4SDavid E. O'Brien {
641c80476e4SDavid E. O'Brien     int ok, negate_range;
64245e5710bSMark Peek     Char c;
643c80476e4SDavid E. O'Brien 
644c80476e4SDavid E. O'Brien     while (pat < patend) {
64523338178SMark Peek 	size_t lwk;
64645e5710bSMark Peek 	__Char wc, wk;
64723338178SMark Peek 
64823338178SMark Peek 	c = *pat; /* Only for M_MASK bits */
64945e5710bSMark Peek 	pat += One_Char_mbtowc(&wc, pat, MB_LEN_MAX);
65045e5710bSMark Peek 	lwk = one_mbtowc(&wk, name, MB_LEN_MAX);
651c80476e4SDavid E. O'Brien 	switch (c & M_MASK) {
652c80476e4SDavid E. O'Brien 	case M_ALL:
653c80476e4SDavid E. O'Brien 	    if (pat == patend)
654c80476e4SDavid E. O'Brien 		return (1);
65523338178SMark Peek 	    for (;;) {
656c80476e4SDavid E. O'Brien 		if (match(name, pat, patend, m_not))
657c80476e4SDavid E. O'Brien 		    return (1);
65823338178SMark Peek 		if (*name == EOS)
65923338178SMark Peek 		    break;
66023338178SMark Peek 		name += lwk;
66145e5710bSMark Peek 		lwk = one_mbtowc(&wk, name, MB_LEN_MAX);
66223338178SMark Peek 	    }
663c80476e4SDavid E. O'Brien 	    return (0);
664c80476e4SDavid E. O'Brien 	case M_ONE:
66523338178SMark Peek 	    if (*name == EOS)
666c80476e4SDavid E. O'Brien 		return (0);
66723338178SMark Peek 	    name += lwk;
668c80476e4SDavid E. O'Brien 	    break;
669c80476e4SDavid E. O'Brien 	case M_SET:
670c80476e4SDavid E. O'Brien 	    ok = 0;
67123338178SMark Peek 	    if (*name == EOS)
672c80476e4SDavid E. O'Brien 		return (0);
67323338178SMark Peek 	    name += lwk;
674c80476e4SDavid E. O'Brien 	    if ((negate_range = ((*pat & M_MASK) == m_not)) != 0)
675c80476e4SDavid E. O'Brien 		++pat;
67623338178SMark Peek 	    while ((*pat & M_MASK) != M_END) {
67745e5710bSMark Peek 		pat += One_Char_mbtowc(&wc, pat, MB_LEN_MAX);
678c80476e4SDavid E. O'Brien 		if ((*pat & M_MASK) == M_RNG) {
67945e5710bSMark Peek 		    __Char wc2;
68023338178SMark Peek 
68123338178SMark Peek 		    pat++;
68245e5710bSMark Peek 		    pat += One_Char_mbtowc(&wc2, pat, MB_LEN_MAX);
68323338178SMark Peek 		    if (globcharcoll(wc, wk, 0) <= 0 &&
68423338178SMark Peek 			globcharcoll(wk, wc2, 0) <= 0)
685c80476e4SDavid E. O'Brien 			ok = 1;
68623338178SMark Peek 		} else if (wc == wk)
687c80476e4SDavid E. O'Brien 		    ok = 1;
688c80476e4SDavid E. O'Brien 	    }
68945e5710bSMark Peek 	    pat += One_Char_mbtowc(&wc, pat, MB_LEN_MAX);
690c80476e4SDavid E. O'Brien 	    if (ok == negate_range)
691c80476e4SDavid E. O'Brien 		return (0);
692c80476e4SDavid E. O'Brien 	    break;
693c80476e4SDavid E. O'Brien 	default:
69423338178SMark Peek 	    name += lwk;
69523338178SMark Peek 	    if (samecase(wk) != samecase(wc))
696c80476e4SDavid E. O'Brien 		return (0);
697c80476e4SDavid E. O'Brien 	    break;
698c80476e4SDavid E. O'Brien 	}
699c80476e4SDavid E. O'Brien     }
700c80476e4SDavid E. O'Brien     return (*name == EOS);
701c80476e4SDavid E. O'Brien }
702c80476e4SDavid E. O'Brien 
703c80476e4SDavid E. O'Brien /* free allocated data belonging to a glob_t structure */
704c80476e4SDavid E. O'Brien void
70545e5710bSMark Peek globfree(glob_t *pglob)
706c80476e4SDavid E. O'Brien {
70723338178SMark Peek     int i;
70823338178SMark Peek     char **pp;
709c80476e4SDavid E. O'Brien 
710c80476e4SDavid E. O'Brien     if (pglob->gl_pathv != NULL) {
711c80476e4SDavid E. O'Brien 	pp = pglob->gl_pathv + pglob->gl_offs;
712c80476e4SDavid E. O'Brien 	for (i = pglob->gl_pathc; i--; ++pp)
713c80476e4SDavid E. O'Brien 	    if (*pp)
71445e5710bSMark Peek 		xfree(*pp), *pp = NULL;
71545e5710bSMark Peek 	xfree(pglob->gl_pathv), pglob->gl_pathv = NULL;
716c80476e4SDavid E. O'Brien     }
717c80476e4SDavid E. O'Brien }
718