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