xref: /PHP-8.1/win32/glob.c (revision 92ac598a)
1 /*
2  * Copyright (c) 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Guido van Rossum.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 /*
38  * glob(3) -- a superset of the one defined in POSIX 1003.2.
39  *
40  * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
41  *
42  * Optional extra services, controlled by flags not defined by POSIX:
43  *
44  * GLOB_QUOTE:
45  *	Escaping convention: \ inhibits any special meaning the following
46  *	character might have (except \ at end of string is retained).
47  * GLOB_MAGCHAR:
48  *	Set in gl_flags if pattern contained a globbing character.
49  * GLOB_NOMAGIC:
50  *	Same as GLOB_NOCHECK, but it will only append pattern if it did
51  *	not contain any magic characters.  [Used in csh style globbing]
52  * GLOB_ALTDIRFUNC:
53  *	Use alternately specified directory access functions.
54  * GLOB_TILDE:
55  *	expand ~user/foo to the /home/dir/of/user/foo
56  * GLOB_BRACE:
57  *	expand {1,2}{a,b} to 1a 1b 2a 2b
58  * gl_matchc:
59  *	Number of matches in the current invocation of glob.
60  */
61 #ifdef PHP_WIN32
62 #if _MSC_VER < 1800
63 # define _POSIX_
64 # include <limits.h>
65 # undef _POSIX_
66 #else
67 /* Visual Studio 2013 removed all the _POSIX_ defines, but we depend on some */
68 # ifndef ARG_MAX
69 #  define ARG_MAX 14500
70 # endif
71 #endif
72 #endif
73 
74 #include "php.h"
75 #include <sys/stat.h>
76 
77 #include <ctype.h>
78 #ifndef PHP_WIN32
79 #include <sys/param.h>
80 #include <dirent.h>
81 #include <pwd.h>
82 #include <unistd.h>
83 #endif
84 #include <errno.h>
85 #include "glob.h"
86 #include <stdio.h>
87 #include <stdlib.h>
88 #include <string.h>
89 
90 #define	DOLLAR		'$'
91 #define	DOT		'.'
92 #define	EOS		'\0'
93 #define	LBRACKET	'['
94 #define	NOT		'!'
95 #define	QUESTION	'?'
96 #define	QUOTE		'\\'
97 #define	RANGE		'-'
98 #define	RBRACKET	']'
99 #define	SEP		DEFAULT_SLASH
100 #define	STAR		'*'
101 #define	TILDE		'~'
102 #define	UNDERSCORE	'_'
103 #define	LBRACE		'{'
104 #define	RBRACE		'}'
105 #define	SLASH		'/'
106 #define	COMMA		','
107 
108 #ifndef DEBUG
109 
110 #define	M_QUOTE		0x8000
111 #define	M_PROTECT	0x4000
112 #define	M_MASK		0xffff
113 #define	M_ASCII		0x00ff
114 
115 typedef u_short Char;
116 
117 #else
118 
119 #define	M_QUOTE		0x80
120 #define	M_PROTECT	0x40
121 #define	M_MASK		0xff
122 #define	M_ASCII		0x7f
123 
124 typedef char Char;
125 
126 #endif
127 
128 
129 #define	CHAR(c)		((Char)((c)&M_ASCII))
130 #define	META(c)		((Char)((c)|M_QUOTE))
131 #define	M_ALL		META('*')
132 #define	M_END		META(']')
133 #define	M_NOT		META('!')
134 #define	M_ONE		META('?')
135 #define	M_RNG		META('-')
136 #define	M_SET		META('[')
137 #define	ismeta(c)	(((c)&M_QUOTE) != 0)
138 
139 static int	 compare(const void *, const void *);
140 static int	 g_Ctoc(const Char *, char *, u_int);
141 static int	 g_lstat(Char *, zend_stat_t *, glob_t *);
142 static DIR	*g_opendir(Char *, glob_t *);
143 static Char	*g_strchr(Char *, int);
144 static int	 g_stat(Char *, zend_stat_t *, glob_t *);
145 static int	 glob0(const Char *, glob_t *);
146 static int	 glob1(Char *, Char *, glob_t *, size_t *);
147 static int	 glob2(Char *, Char *, Char *, Char *, Char *, Char *,
148 				glob_t *, size_t *);
149 static int	 glob3(Char *, Char *, Char *, Char *, Char *, Char *,
150 				Char *, Char *, glob_t *, size_t *);
151 static int	 globextend(const Char *, glob_t *, size_t *);
152 static const Char *globtilde(const Char *, Char *, size_t, glob_t *);
153 static int	 globexp1(const Char *, glob_t *);
154 static int	 globexp2(const Char *, const Char *, glob_t *, int *);
155 static int	 match(Char *, Char *, Char *);
156 #ifdef DEBUG
157 static void	 qprintf(const char *, Char *);
158 #endif
159 
160 PHPAPI int
glob(pattern,flags,errfunc,pglob)161 glob(pattern, flags, errfunc, pglob)
162 	const char *pattern;
163 	int flags, (*errfunc)(const char *, int);
164 	glob_t *pglob;
165 {
166 	const u_char *patnext;
167 	int c;
168 	Char *bufnext, *bufend, patbuf[MAXPATHLEN];
169 
170 #ifdef PHP_WIN32
171 	/* Force skipping escape sequences on windows
172 	 * due to the ambiguity with path backslashes
173 	 */
174 	flags |= GLOB_NOESCAPE;
175 #endif
176 
177 	patnext = (u_char *) pattern;
178 	if (!(flags & GLOB_APPEND)) {
179 		pglob->gl_pathc = 0;
180 		pglob->gl_pathv = NULL;
181 		if (!(flags & GLOB_DOOFFS))
182 			pglob->gl_offs = 0;
183 	}
184 	pglob->gl_flags = flags & ~GLOB_MAGCHAR;
185 	pglob->gl_errfunc = errfunc;
186 	pglob->gl_matchc = 0;
187 
188 	bufnext = patbuf;
189 	bufend = bufnext + MAXPATHLEN - 1;
190 	if (flags & GLOB_NOESCAPE)
191 		while (bufnext < bufend && (c = *patnext++) != EOS)
192 			*bufnext++ = c;
193 	else {
194 		/* Protect the quoted characters. */
195 		while (bufnext < bufend && (c = *patnext++) != EOS)
196 			if (c == QUOTE) {
197 				if ((c = *patnext++) == EOS) {
198 					c = QUOTE;
199 					--patnext;
200 				}
201 				*bufnext++ = c | M_PROTECT;
202 			} else
203 				*bufnext++ = c;
204 	}
205 	*bufnext = EOS;
206 
207 	if (flags & GLOB_BRACE)
208 		return globexp1(patbuf, pglob);
209 	else
210 		return glob0(patbuf, pglob);
211 }
212 
213 /*
214  * Expand recursively a glob {} pattern. When there is no more expansion
215  * invoke the standard globbing routine to glob the rest of the magic
216  * characters
217  */
218 static int
globexp1(pattern,pglob)219 globexp1(pattern, pglob)
220 	const Char *pattern;
221 	glob_t *pglob;
222 {
223 	const Char* ptr = pattern;
224 	int rv;
225 
226 	/* Protect a single {}, for find(1), like csh */
227 	if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS)
228 		return glob0(pattern, pglob);
229 
230 	while ((ptr = (const Char *) g_strchr((Char *) ptr, LBRACE)) != NULL)
231 		if (!globexp2(ptr, pattern, pglob, &rv))
232 			return rv;
233 
234 	return glob0(pattern, pglob);
235 }
236 
237 
238 /*
239  * Recursive brace globbing helper. Tries to expand a single brace.
240  * If it succeeds then it invokes globexp1 with the new pattern.
241  * If it fails then it tries to glob the rest of the pattern and returns.
242  */
243 static int
globexp2(ptr,pattern,pglob,rv)244 globexp2(ptr, pattern, pglob, rv)
245 	const Char *ptr, *pattern;
246 	glob_t *pglob;
247 	int *rv;
248 {
249 	int     i;
250 	Char   *lm, *ls;
251 	const Char *pe, *pm, *pl;
252 	Char    patbuf[MAXPATHLEN];
253 
254 	/* copy part up to the brace */
255 	for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++)
256 		;
257 	*lm = EOS;
258 	ls = lm;
259 
260 	/* Find the balanced brace */
261 	for (i = 0, pe = ++ptr; *pe; pe++)
262 		if (*pe == LBRACKET) {
263 			/* Ignore everything between [] */
264 			for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++)
265 				;
266 			if (*pe == EOS) {
267 				/*
268 				 * We could not find a matching RBRACKET.
269 				 * Ignore and just look for RBRACE
270 				 */
271 				pe = pm;
272 			}
273 		} else if (*pe == LBRACE)
274 			i++;
275 		else if (*pe == RBRACE) {
276 			if (i == 0)
277 				break;
278 			i--;
279 		}
280 
281 	/* Non matching braces; just glob the pattern */
282 	if (i != 0 || *pe == EOS) {
283 		*rv = glob0(patbuf, pglob);
284 		return 0;
285 	}
286 
287 	for (i = 0, pl = pm = ptr; pm <= pe; pm++) {
288 		const Char *pb;
289 
290 		switch (*pm) {
291 		case LBRACKET:
292 			/* Ignore everything between [] */
293 			for (pb = pm++; *pm != RBRACKET && *pm != EOS; pm++)
294 				;
295 			if (*pm == EOS) {
296 				/*
297 				 * We could not find a matching RBRACKET.
298 				 * Ignore and just look for RBRACE
299 				 */
300 				pm = pb;
301 			}
302 			break;
303 
304 		case LBRACE:
305 			i++;
306 			break;
307 
308 		case RBRACE:
309 			if (i) {
310 				i--;
311 				break;
312 			}
313 			/* FALLTHROUGH */
314 		case COMMA:
315 			if (i && *pm == COMMA)
316 				break;
317 			else {
318 				/* Append the current string */
319 				for (lm = ls; (pl < pm); *lm++ = *pl++)
320 					;
321 
322 				/*
323 				 * Append the rest of the pattern after the
324 				 * closing brace
325 				 */
326 				for (pl = pe + 1; (*lm++ = *pl++) != EOS; )
327 					;
328 
329 				/* Expand the current pattern */
330 #ifdef DEBUG
331 				qprintf("globexp2:", patbuf);
332 #endif
333 				*rv = globexp1(patbuf, pglob);
334 
335 				/* move after the comma, to the next string */
336 				pl = pm + 1;
337 			}
338 			break;
339 
340 		default:
341 			break;
342 		}
343 	}
344 	*rv = 0;
345 	return 0;
346 }
347 
348 
349 
350 /*
351  * expand tilde from the passwd file.
352  */
353 static const Char *
globtilde(pattern,patbuf,patbuf_len,pglob)354 globtilde(pattern, patbuf, patbuf_len, pglob)
355 	const Char *pattern;
356 	Char *patbuf;
357 	size_t patbuf_len;
358 	glob_t *pglob;
359 {
360 #ifndef PHP_WIN32
361 	struct passwd *pwd;
362 #endif
363 	char *h;
364 	const Char *p;
365 	Char *b, *eb;
366 
367 	if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE))
368 		return pattern;
369 
370 	/* Copy up to the end of the string or / */
371 	eb = &patbuf[patbuf_len - 1];
372 	for (p = pattern + 1, h = (char *) patbuf;
373 		h < (char *)eb && *p && *p != SLASH; *h++ = (char) *p++)
374 		;
375 
376 	*h = EOS;
377 
378 #if 0
379 	if (h == (char *)eb)
380 		return what;
381 #endif
382 
383 	if (((char *) patbuf)[0] == EOS) {
384 		/*
385 		 * handle a plain ~ or ~/ by expanding $HOME
386 		 * first and then trying the password file
387 		 */
388 		if ((h = getenv("HOME")) == NULL) {
389 #ifndef PHP_WIN32
390 			if ((pwd = getpwuid(getuid())) == NULL)
391 				return pattern;
392 			else
393 				h = pwd->pw_dir;
394 #else
395 			return pattern;
396 #endif
397 		}
398 	} else {
399 		/*
400 		 * Expand a ~user
401 		 */
402 #ifndef PHP_WIN32
403 		if ((pwd = getpwnam((char*) patbuf)) == NULL)
404 			return pattern;
405 		else
406 			h = pwd->pw_dir;
407 #else
408 		return pattern;
409 #endif
410 	}
411 
412 	/* Copy the home directory */
413 	for (b = patbuf; b < eb && *h; *b++ = *h++)
414 		;
415 
416 	/* Append the rest of the pattern */
417 	while (b < eb && (*b++ = *p++) != EOS)
418 		;
419 	*b = EOS;
420 
421 	return patbuf;
422 }
423 
424 
425 /*
426  * The main glob() routine: compiles the pattern (optionally processing
427  * quotes), calls glob1() to do the real pattern matching, and finally
428  * sorts the list (unless unsorted operation is requested).  Returns 0
429  * if things went well, nonzero if errors occurred.  It is not an error
430  * to find no matches.
431  */
432 static int
glob0(pattern,pglob)433 glob0(pattern, pglob)
434 	const Char *pattern;
435 	glob_t *pglob;
436 {
437 	const Char *qpatnext;
438 	int c, err, oldpathc;
439 	Char *bufnext, patbuf[MAXPATHLEN];
440 	size_t limit = 0;
441 
442 	qpatnext = globtilde(pattern, patbuf, MAXPATHLEN, pglob);
443 	oldpathc = pglob->gl_pathc;
444 	bufnext = patbuf;
445 
446 	/* We don't need to check for buffer overflow any more. */
447 	while ((c = *qpatnext++) != EOS) {
448 		switch (c) {
449 		case LBRACKET:
450 			c = *qpatnext;
451 			if (c == NOT)
452 				++qpatnext;
453 			if (*qpatnext == EOS ||
454 				g_strchr((Char *) qpatnext+1, RBRACKET) == NULL) {
455 				*bufnext++ = LBRACKET;
456 				if (c == NOT)
457 					--qpatnext;
458 				break;
459 			}
460 			*bufnext++ = M_SET;
461 			if (c == NOT)
462 				*bufnext++ = M_NOT;
463 			c = *qpatnext++;
464 			do {
465 				*bufnext++ = CHAR(c);
466 				if (*qpatnext == RANGE &&
467 					(c = qpatnext[1]) != RBRACKET) {
468 					*bufnext++ = M_RNG;
469 					*bufnext++ = CHAR(c);
470 					qpatnext += 2;
471 				}
472 			} while ((c = *qpatnext++) != RBRACKET);
473 			pglob->gl_flags |= GLOB_MAGCHAR;
474 			*bufnext++ = M_END;
475 			break;
476 		case QUESTION:
477 			pglob->gl_flags |= GLOB_MAGCHAR;
478 			*bufnext++ = M_ONE;
479 			break;
480 		case STAR:
481 			pglob->gl_flags |= GLOB_MAGCHAR;
482 			/* collapse adjacent stars to one,
483 			 * to avoid exponential behavior
484 			 */
485 			if (bufnext == patbuf || bufnext[-1] != M_ALL)
486 				*bufnext++ = M_ALL;
487 			break;
488 		default:
489 			*bufnext++ = CHAR(c);
490 			break;
491 		}
492 	}
493 	*bufnext = EOS;
494 #ifdef DEBUG
495 	qprintf("glob0:", patbuf);
496 #endif
497 
498 	if ((err = glob1(patbuf, patbuf+MAXPATHLEN-1, pglob, &limit)) != 0)
499 		return(err);
500 
501 	/*
502 	 * If there was no match we are going to append the pattern
503 	 * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
504 	 * and the pattern did not contain any magic characters
505 	 * GLOB_NOMAGIC is there just for compatibility with csh.
506 	 */
507 	if (pglob->gl_pathc == oldpathc) {
508 		if ((pglob->gl_flags & GLOB_NOCHECK) ||
509 			((pglob->gl_flags & GLOB_NOMAGIC) &&
510 			!(pglob->gl_flags & GLOB_MAGCHAR)))
511 			return(globextend(pattern, pglob, &limit));
512 		else
513 			return(GLOB_NOMATCH);
514 	}
515 	if (!(pglob->gl_flags & GLOB_NOSORT))
516 		qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
517 			pglob->gl_pathc - oldpathc, sizeof(char *), compare);
518 	return(0);
519 }
520 
521 static int
compare(const void * p,const void * q)522 compare(const void *p, const void *q)
523 {
524 	return(strcmp(*(char **)p, *(char **)q));
525 }
526 
527 static int
glob1(pattern,pattern_last,pglob,limitp)528 glob1(pattern, pattern_last, pglob, limitp)
529 	Char *pattern, *pattern_last;
530 	glob_t *pglob;
531 	size_t *limitp;
532 {
533 	Char pathbuf[MAXPATHLEN];
534 
535 	/* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
536 	if (*pattern == EOS)
537 		return(0);
538 	return(glob2(pathbuf, pathbuf+MAXPATHLEN-1,
539 		pathbuf, pathbuf+MAXPATHLEN-1,
540 		pattern, pattern_last, pglob, limitp));
541 }
542 
543 /*
544  * The functions glob2 and glob3 are mutually recursive; there is one level
545  * of recursion for each segment in the pattern that contains one or more
546  * meta characters.
547  */
548 static int
glob2(pathbuf,pathbuf_last,pathend,pathend_last,pattern,pattern_last,pglob,limitp)549 glob2(pathbuf, pathbuf_last, pathend, pathend_last, pattern,
550 		pattern_last, pglob, limitp)
551 	Char *pathbuf, *pathbuf_last, *pathend, *pathend_last;
552 	Char *pattern, *pattern_last;
553 	glob_t *pglob;
554 	size_t *limitp;
555 {
556 	zend_stat_t sb;
557 	Char *p, *q;
558 	int anymeta;
559 
560 	/*
561 	 * Loop over pattern segments until end of pattern or until
562 	 * segment with meta character found.
563 	 */
564 	for (anymeta = 0;;) {
565 		if (*pattern == EOS) {		/* End of pattern? */
566 			*pathend = EOS;
567 			if (g_lstat(pathbuf, &sb, pglob))
568 				return(0);
569 
570 			if (((pglob->gl_flags & GLOB_MARK) &&
571 				!IS_SLASH(pathend[-1])) && (S_ISDIR(sb.st_mode) ||
572 				(S_ISLNK(sb.st_mode) &&
573 				(g_stat(pathbuf, &sb, pglob) == 0) &&
574 				S_ISDIR(sb.st_mode)))) {
575 				if (pathend+1 > pathend_last)
576 					return (1);
577 				*pathend++ = SEP;
578 				*pathend = EOS;
579 			}
580 			++pglob->gl_matchc;
581 			return(globextend(pathbuf, pglob, limitp));
582 		}
583 
584 		/* Find end of next segment, copy tentatively to pathend. */
585 		q = pathend;
586 		p = pattern;
587 		while (*p != EOS && !IS_SLASH(*p)) {
588 			if (ismeta(*p))
589 				anymeta = 1;
590 			if (q+1 > pathend_last)
591 				return (1);
592 			*q++ = *p++;
593 		}
594 
595 		if (!anymeta) {		/* No expansion, do next segment. */
596 			pathend = q;
597 			pattern = p;
598 			while (IS_SLASH(*pattern)) {
599 				if (pathend+1 > pathend_last)
600 					return (1);
601 				*pathend++ = *pattern++;
602 			}
603 		} else
604 			/* Need expansion, recurse. */
605 			return(glob3(pathbuf, pathbuf_last, pathend,
606 				pathend_last, pattern, pattern_last,
607 				p, pattern_last, pglob, limitp));
608 	}
609 	/* NOTREACHED */
610 }
611 
612 static int
glob3(pathbuf,pathbuf_last,pathend,pathend_last,pattern,pattern_last,restpattern,restpattern_last,pglob,limitp)613 glob3(pathbuf, pathbuf_last, pathend, pathend_last, pattern, pattern_last,
614 	restpattern, restpattern_last, pglob, limitp)
615 	Char *pathbuf, *pathbuf_last, *pathend, *pathend_last;
616 	Char *pattern, *pattern_last, *restpattern, *restpattern_last;
617 	glob_t *pglob;
618 	size_t *limitp;
619 {
620 	register struct dirent *dp;
621 	DIR *dirp;
622 	int err;
623 
624 	/*
625 	 * The readdirfunc declaration can't be prototyped, because it is
626 	 * assigned, below, to two functions which are prototyped in glob.h
627 	 * and dirent.h as taking pointers to differently typed opaque
628 	 * structures.
629 	 */
630 	struct dirent *(*readdirfunc)();
631 
632 	if (pathend > pathend_last)
633 		return (1);
634 	*pathend = EOS;
635 	errno = 0;
636 
637 	if ((dirp = g_opendir(pathbuf, pglob)) == NULL) {
638 		/* TODO: don't call for ENOENT or ENOTDIR? */
639 		if (pglob->gl_errfunc) {
640 			char buf[MAXPATHLEN];
641 			if (g_Ctoc(pathbuf, buf, sizeof(buf)))
642 				return(GLOB_ABORTED);
643 			if (pglob->gl_errfunc(buf, errno) ||
644 				pglob->gl_flags & GLOB_ERR)
645 				return(GLOB_ABORTED);
646 		}
647 		return(0);
648 	}
649 
650 	err = 0;
651 
652 	/* Search directory for matching names. */
653 	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
654 		readdirfunc = pglob->gl_readdir;
655 	else
656 		readdirfunc = readdir;
657 	while ((dp = (*readdirfunc)(dirp))) {
658 		register u_char *sc;
659 		register Char *dc;
660 
661 		/* Initial DOT must be matched literally. */
662 		if (dp->d_name[0] == DOT && *pattern != DOT)
663 			continue;
664 		dc = pathend;
665 		sc = (u_char *) dp->d_name;
666 		while (dc < pathend_last && (*dc++ = *sc++) != EOS)
667 			;
668 		if (dc >= pathend_last) {
669 			*dc = EOS;
670 			err = 1;
671 			break;
672 		}
673 
674 		if (!match(pathend, pattern, restpattern)) {
675 			*pathend = EOS;
676 			continue;
677 		}
678 		err = glob2(pathbuf, pathbuf_last, --dc, pathend_last,
679 			restpattern, restpattern_last, pglob, limitp);
680 		if (err)
681 			break;
682 	}
683 
684 	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
685 		(*pglob->gl_closedir)(dirp);
686 	else
687 		closedir(dirp);
688 	return(err);
689 }
690 
691 
692 /*
693  * Extend the gl_pathv member of a glob_t structure to accommodate a new item,
694  * add the new item, and update gl_pathc.
695  *
696  * This assumes the BSD realloc, which only copies the block when its size
697  * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
698  * behavior.
699  *
700  * Return 0 if new item added, error code if memory couldn't be allocated.
701  *
702  * Invariant of the glob_t structure:
703  *	Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
704  *	gl_pathv points to (gl_offs + gl_pathc + 1) items.
705  */
706 static int
globextend(path,pglob,limitp)707 globextend(path, pglob, limitp)
708 	const Char *path;
709 	glob_t *pglob;
710 	size_t *limitp;
711 {
712 	register char **pathv;
713 	u_int newsize, len;
714 	char *copy;
715 	const Char *p;
716 
717 	newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
718 	pathv = pglob->gl_pathv ? realloc((char *)pglob->gl_pathv, newsize) :
719 		malloc(newsize);
720 	if (pathv == NULL) {
721 		if (pglob->gl_pathv) {
722 			free(pglob->gl_pathv);
723 			pglob->gl_pathv = NULL;
724 		}
725 		return(GLOB_NOSPACE);
726 	}
727 
728 	if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
729 		register int i;
730 		/* first time around -- clear initial gl_offs items */
731 		pathv += pglob->gl_offs;
732 		for (i = pglob->gl_offs; --i >= 0; )
733 			*--pathv = NULL;
734 	}
735 	pglob->gl_pathv = pathv;
736 
737 	for (p = path; *p++;)
738 		;
739 	len = (u_int)(p - path);
740 	*limitp += len;
741 	if ((copy = malloc(len)) != NULL) {
742 		if (g_Ctoc(path, copy, len)) {
743 			free(copy);
744 			return(GLOB_NOSPACE);
745 		}
746 		pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
747 	}
748 	pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
749 
750 	if ((pglob->gl_flags & GLOB_LIMIT) &&
751 		newsize + *limitp >= ARG_MAX) {
752 		errno = 0;
753 		return(GLOB_NOSPACE);
754 	}
755 
756 	return(copy == NULL ? GLOB_NOSPACE : 0);
757 }
758 
759 
760 /*
761  * pattern matching function for filenames.  Each occurrence of the *
762  * pattern causes a recursion level.
763  */
764 static int
match(name,pat,patend)765 match(name, pat, patend)
766 	register Char *name, *pat, *patend;
767 {
768 	int ok, negate_range;
769 	Char c, k;
770 
771 	while (pat < patend) {
772 		c = *pat++;
773 		switch (c & M_MASK) {
774 		case M_ALL:
775 			if (pat == patend)
776 				return(1);
777 			do
778 				if (match(name, pat, patend))
779 					return(1);
780 			while (*name++ != EOS)
781 				;
782 			return(0);
783 		case M_ONE:
784 			if (*name++ == EOS)
785 				return(0);
786 			break;
787 		case M_SET:
788 			ok = 0;
789 			if ((k = *name++) == EOS)
790 				return(0);
791 			if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS)
792 				++pat;
793 			while (((c = *pat++) & M_MASK) != M_END)
794 				if ((*pat & M_MASK) == M_RNG) {
795 					if (c <= k && k <= pat[1])
796 						ok = 1;
797 					pat += 2;
798 				} else if (c == k)
799 					ok = 1;
800 			if (ok == negate_range)
801 				return(0);
802 			break;
803 		default:
804 			if (*name++ != c)
805 				return(0);
806 			break;
807 		}
808 	}
809 	return(*name == EOS);
810 }
811 
812 /* Free allocated data belonging to a glob_t structure. */
813 PHPAPI void
globfree(pglob)814 globfree(pglob)
815 	glob_t *pglob;
816 {
817 	register int i;
818 	register char **pp;
819 
820 	if (pglob->gl_pathv != NULL) {
821 		pp = pglob->gl_pathv + pglob->gl_offs;
822 		for (i = pglob->gl_pathc; i--; ++pp)
823 			if (*pp)
824 				free(*pp);
825 		free(pglob->gl_pathv);
826 		pglob->gl_pathv = NULL;
827 	}
828 }
829 
830 static DIR *
g_opendir(str,pglob)831 g_opendir(str, pglob)
832 	register Char *str;
833 	glob_t *pglob;
834 {
835 	char buf[MAXPATHLEN];
836 
837 	if (!*str)
838 		strlcpy(buf, ".", sizeof(buf));
839 	else {
840 		if (g_Ctoc(str, buf, sizeof(buf)))
841 			return(NULL);
842 	}
843 
844 	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
845 		return((*pglob->gl_opendir)(buf));
846 
847 	return(opendir(buf));
848 }
849 
850 static int
g_lstat(fn,sb,pglob)851 g_lstat(fn, sb, pglob)
852 	register Char *fn;
853 	zend_stat_t *sb;
854 	glob_t *pglob;
855 {
856 	char buf[MAXPATHLEN];
857 
858 	if (g_Ctoc(fn, buf, sizeof(buf)))
859 		return(-1);
860 	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
861 		return((*pglob->gl_lstat)(buf, sb));
862 	return(php_sys_lstat(buf, sb));
863 }
864 
865 static int
g_stat(fn,sb,pglob)866 g_stat(fn, sb, pglob)
867 	register Char *fn;
868 	zend_stat_t *sb;
869 	glob_t *pglob;
870 {
871 	char buf[MAXPATHLEN];
872 
873 	if (g_Ctoc(fn, buf, sizeof(buf)))
874 		return(-1);
875 	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
876 		return((*pglob->gl_stat)(buf, sb));
877 	return(php_sys_stat(buf, sb));
878 }
879 
880 static Char *
g_strchr(str,ch)881 g_strchr(str, ch)
882 	Char *str;
883 	int ch;
884 {
885 	do {
886 		if (*str == ch)
887 			return (str);
888 	} while (*str++);
889 	return (NULL);
890 }
891 
892 static int
g_Ctoc(str,buf,len)893 g_Ctoc(str, buf, len)
894 	register const Char *str;
895 	char *buf;
896 	u_int len;
897 {
898 
899 	while (len--) {
900 		if ((*buf++ = (char) *str++) == EOS)
901 			return (0);
902 	}
903 	return (1);
904 }
905 
906 #ifdef DEBUG
907 static void
qprintf(str,s)908 qprintf(str, s)
909 	const char *str;
910 	register Char *s;
911 {
912 	register Char *p;
913 
914 	(void)printf("%s:\n", str);
915 	for (p = s; *p; p++)
916 		(void)printf("%c", CHAR(*p));
917 	(void)printf("\n");
918 	for (p = s; *p; p++)
919 		(void)printf("%c", *p & M_PROTECT ? '"' : ' ');
920 	(void)printf("\n");
921 	for (p = s; *p; p++)
922 		(void)printf("%c", ismeta(*p) ? '_' : ' ');
923 	(void)printf("\n");
924 }
925 #endif
926