xref: /csrg-svn/lib/libc/gen/glob.c (revision 46597)
140087Sbostic /*
240087Sbostic  * Copyright (c) 1989 The Regents of the University of California.
340087Sbostic  * All rights reserved.
440087Sbostic  *
540087Sbostic  * This code is derived from software contributed to Berkeley by
640087Sbostic  * Guido van Rossum.
740087Sbostic  *
842625Sbostic  * %sccs.include.redist.c%
940087Sbostic  */
1040087Sbostic 
1140087Sbostic #if defined(LIBC_SCCS) && !defined(lint)
12*46597Sdonn static char sccsid[] = "@(#)glob.c	5.4 (Berkeley) 02/23/91";
1340087Sbostic #endif /* LIBC_SCCS and not lint */
1440087Sbostic 
1540087Sbostic /*
1640087Sbostic  * Glob: the interface is a superset of the one defined in POSIX 1003.2,
1740087Sbostic  * draft 9.
1840087Sbostic  *
1940087Sbostic  * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
2040087Sbostic  *
2140087Sbostic  * Optional extra services, controlled by flags not defined by POSIX:
2240087Sbostic  *	GLOB_QUOTE: escaping convention: \ inhibits any special meaning
2340087Sbostic 		the following character might have (except \ at end of
2440087Sbostic  *		string is kept);
2540087Sbostic  */
2640087Sbostic 
2740087Sbostic #include <sys/param.h>
2840087Sbostic #include <sys/stat.h>
2940087Sbostic #include <dirent.h>
3040087Sbostic #include <glob.h>
3140087Sbostic #include <ctype.h>
3240087Sbostic #include <errno.h>
3340087Sbostic #include <string.h>
3440087Sbostic #include <stdio.h>
35*46597Sdonn #include <stdlib.h>
3640087Sbostic 
3740087Sbostic typedef int bool_t;
3840087Sbostic 
39*46597Sdonn static glob1();
40*46597Sdonn static glob2();
41*46597Sdonn static glob3();
42*46597Sdonn static globextend();
43*46597Sdonn static bool_t match();
44*46597Sdonn 
4540087Sbostic #define	DOLLAR		'$'
4640087Sbostic #define	DOT		'.'
4740087Sbostic #define	EOS		'\0'
4840087Sbostic #define	LBRACKET	'['
4940087Sbostic #define	NOT		'!'
5040087Sbostic #define	QUESTION	'?'
5140087Sbostic #define	QUOTE		'\\'
5240087Sbostic #define	RANGE		'-'
5340087Sbostic #define	RBRACKET	']'
5440087Sbostic #define	SEP		'/'
5540087Sbostic #define	STAR		'*'
5640087Sbostic #define	TILDE		'~'
5740087Sbostic #define	UNDERSCORE	'_'
5840087Sbostic 
5940087Sbostic #define	METABIT		0x80
6040087Sbostic #define	META(c)		((c)|METABIT)
6140087Sbostic #define	M_ALL		META('*')
6240087Sbostic #define	M_END		META(']')
6340087Sbostic #define	M_NOT		META('!')
6440087Sbostic #define	M_ONE		META('?')
6540087Sbostic #define	M_RNG		META('-')
6640087Sbostic #define	M_SET		META('[')
6740087Sbostic #define	ismeta(c)	(((c)&METABIT) != 0)
6840087Sbostic 
6940087Sbostic static
7040087Sbostic compare(p, q)
7143531Sbostic 	void **p, **q;
7240087Sbostic {
7343531Sbostic 	return(strcmp(*(char **)p, *(char **)q));
7440087Sbostic }
7540087Sbostic 
7640087Sbostic 
7740087Sbostic /*
7840087Sbostic  * The main glob() routine: compiles the pattern (optionally processing
7940087Sbostic  * quotes), calls glob1() to do the real pattern matching, and finally
8040087Sbostic  * sorts the list (unless unsorted operation is requested).  Returns 0
8140087Sbostic  * if things went well, nonzero if errors occurred.  It is not an error
8240087Sbostic  * to find no matches.
8340087Sbostic  */
8440087Sbostic glob(pattern, flags, errfunc, pglob)
85*46597Sdonn 	const char *pattern;
86*46597Sdonn 	int flags;
87*46597Sdonn 	int (*errfunc) __P((char *, int));
8840087Sbostic 	glob_t *pglob;
8940087Sbostic {
9040087Sbostic 	int err, oldpathc;
91*46597Sdonn 	char *bufnext, *bufend, *compilebuf;
92*46597Sdonn 	const char *compilepat, *patnext;
9340087Sbostic 	char c, patbuf[MAXPATHLEN+1];
9440087Sbostic 
9540087Sbostic 	patnext = pattern;
9640087Sbostic 	if (!(flags & GLOB_APPEND)) {
9740087Sbostic 		pglob->gl_pathc = 0;
9840087Sbostic 		pglob->gl_pathv = NULL;
9940087Sbostic 		if (!(flags & GLOB_DOOFFS))
10040087Sbostic 			pglob->gl_offs = 0;
10140087Sbostic 	}
10240087Sbostic 	pglob->gl_flags = flags;
10340087Sbostic 	pglob->gl_errfunc = errfunc;
10440087Sbostic 	oldpathc = pglob->gl_pathc;
10540087Sbostic 
10640087Sbostic 	bufnext = patbuf;
10740087Sbostic 	bufend = bufnext+MAXPATHLEN;
10840087Sbostic 
10940087Sbostic 	compilebuf = bufnext;
11040087Sbostic 	compilepat = patnext;
11140087Sbostic 	while (bufnext < bufend && (c = *patnext++) != EOS) {
11240087Sbostic 		switch (c) {
11340087Sbostic 		case LBRACKET:
11440087Sbostic 			c = *patnext;
11540087Sbostic 			if (c == NOT)
11640087Sbostic 				++patnext;
11740087Sbostic 			if (*patnext == EOS ||
11840087Sbostic 			    strchr(patnext+1, RBRACKET) == NULL) {
11940087Sbostic 				*bufnext++ = LBRACKET;
12040087Sbostic 				if (c == NOT)
12140087Sbostic 					--patnext;
12240087Sbostic 				break;
12340087Sbostic 			}
12440087Sbostic 			*bufnext++ = M_SET;
12540087Sbostic 			if (c == NOT)
12640087Sbostic 				*bufnext++ = M_NOT;
12740087Sbostic 			c = *patnext++;
12840087Sbostic 			do {
12940087Sbostic 				/* todo: quoting */
13040087Sbostic 				*bufnext++ = c;
13140087Sbostic 				if (*patnext == RANGE &&
13240087Sbostic 				    (c = patnext[1]) != RBRACKET) {
13340087Sbostic 					*bufnext++ = M_RNG;
13440087Sbostic 					*bufnext++ = c;
13540087Sbostic 					patnext += 2;
13640087Sbostic 				}
13740087Sbostic 			} while ((c = *patnext++) != RBRACKET);
13840087Sbostic 			*bufnext++ = M_END;
13940087Sbostic 			break;
14040087Sbostic 		case QUESTION:
14140087Sbostic 			*bufnext++ = M_ONE;
14240087Sbostic 			break;
14340087Sbostic 		case QUOTE:
14440087Sbostic 			if (!(flags & GLOB_QUOTE))
14540087Sbostic 				*bufnext++ = QUOTE;
14640087Sbostic 			else {
14740087Sbostic 				if ((c = *patnext++) == EOS) {
14840087Sbostic 					c = QUOTE;
14940087Sbostic 					--patnext;
15040087Sbostic 				}
15140087Sbostic 				*bufnext++ = c;
15240087Sbostic 			}
15340087Sbostic 			break;
15440087Sbostic 		case STAR:
15540087Sbostic 			*bufnext++ = M_ALL;
15640087Sbostic 			break;
15740087Sbostic 		default:
15840087Sbostic 			*bufnext++ = c;
15940087Sbostic 			break;
16040087Sbostic 		}
16140087Sbostic 	}
16240087Sbostic 	*bufnext = EOS;
16340087Sbostic 
16440087Sbostic 	if ((err = glob1(patbuf, pglob)) != 0)
16540087Sbostic 		return(err);
16640087Sbostic 
16740087Sbostic 	if (pglob->gl_pathc == oldpathc && flags & GLOB_NOCHECK) {
16840087Sbostic 		if (!(flags & GLOB_QUOTE))
16940087Sbostic 			(void)strcpy(compilebuf, compilepat);
17040087Sbostic 		else {
17140087Sbostic 			/*
17240087Sbostic 			 * copy pattern, interpreting quotes; this is slightly
17340087Sbostic 			 * different than the interpretation of quotes above
17440087Sbostic 			 * -- which should prevail?
17540087Sbostic 			 */
17640087Sbostic 			while (*compilepat != EOS) {
17740087Sbostic 				if (*compilepat == QUOTE) {
17840087Sbostic 					if (*++compilepat == EOS)
17940087Sbostic 						--compilepat;
18040087Sbostic 				}
18140087Sbostic 				*compilebuf++ = *compilepat++;
18240087Sbostic 			}
18340087Sbostic 			*compilebuf = EOS;
18440087Sbostic 		}
18540087Sbostic 		return(globextend(patbuf, pglob));
18640087Sbostic 	} else if (!(flags & GLOB_NOSORT))
18740087Sbostic 		qsort((char*) (pglob->gl_pathv + pglob->gl_offs + oldpathc),
18840087Sbostic 		    pglob->gl_pathc - oldpathc, sizeof(char*), compare);
18940087Sbostic 	return(0);
19040087Sbostic }
19140087Sbostic 
19240087Sbostic static
19340087Sbostic glob1(pattern, pglob)
19440087Sbostic 	char *pattern;
19540087Sbostic 	glob_t *pglob;
19640087Sbostic {
19740087Sbostic 	char pathbuf[MAXPATHLEN+1];
19840087Sbostic 
19940087Sbostic 	/*
20040087Sbostic 	 * a null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
20140087Sbostic 	if (*pattern == EOS)
20240087Sbostic 		return(0);
20340087Sbostic 	return(glob2(pathbuf, pathbuf, pattern, pglob));
20440087Sbostic }
20540087Sbostic 
20640087Sbostic /*
20740087Sbostic  * functions glob2 and glob3 are mutually recursive; there is one level
20840087Sbostic  * of recursion for each segment in the pattern that contains one or
20940087Sbostic  * more meta characters.
21040087Sbostic  */
21140087Sbostic static
21240087Sbostic glob2(pathbuf, pathend, pattern, pglob)
21340087Sbostic 	char *pathbuf, *pathend, *pattern;
21440087Sbostic 	glob_t *pglob;
21540087Sbostic {
21640087Sbostic 	struct stat sbuf;
21740087Sbostic 	bool_t anymeta = 0;
21840087Sbostic 	char *p, *q;
21940087Sbostic 
22040087Sbostic 	/*
22140087Sbostic 	 * loop over pattern segments until end of pattern or until
22240087Sbostic 	 * segment with meta character found.
22340087Sbostic 	 */
22440087Sbostic 	for (;;) {
22540087Sbostic 		if (*pattern == EOS) {		/* end of pattern? */
22640087Sbostic 			*pathend = EOS;
22740087Sbostic 			if (stat(pathbuf, &sbuf) != 0)
22840087Sbostic 				return(0);	/* need error call here? */
22940087Sbostic 			if ((pglob->gl_flags & GLOB_MARK) &&
23040087Sbostic 			    pathend[-1] != SEP && S_ISDIR(sbuf.st_mode)) {
23140087Sbostic 				*pathend++ = SEP;
23240087Sbostic 				*pathend = EOS;
23340087Sbostic 			}
23440087Sbostic 			return(globextend(pathbuf, pglob));
23540087Sbostic 		}
23640087Sbostic 
23740087Sbostic 		/* find end of next segment, copy tentatively to pathend */
23840087Sbostic 		q = pathend;
23940087Sbostic 		p = pattern;
24040087Sbostic 		while (*p != EOS && *p != SEP) {
24140087Sbostic 			if (ismeta(*p))
24240087Sbostic 				anymeta = 1;
24340087Sbostic 			*q++ = *p++;
24440087Sbostic 		}
24540087Sbostic 
24640087Sbostic 		if (!anymeta) {		/* no expansion, do next segment */
24740087Sbostic 			pathend = q;
24840087Sbostic 			pattern = p;
24940087Sbostic 			while (*pattern == SEP)
25040087Sbostic 				*pathend++ = *pattern++;
25140087Sbostic 		} else			/* need expansion, recurse */
25240087Sbostic 			return(glob3(pathbuf, pathend, pattern, p, pglob));
25340087Sbostic 	}
25440087Sbostic 	/* NOTREACHED */
25540087Sbostic }
25640087Sbostic 
25740087Sbostic static
25840087Sbostic glob3(pathbuf, pathend, pattern, restpattern, pglob)
25940087Sbostic 	char *pathbuf, *pathend, *pattern, *restpattern;
26040087Sbostic 	glob_t *pglob;
26140087Sbostic {
26240087Sbostic 	extern int errno;
26340087Sbostic 	DIR *dirp;
26440087Sbostic 	struct dirent *dp;
26540087Sbostic 	int len, err;
26640087Sbostic 
26740087Sbostic 	*pathend = EOS;
26840087Sbostic 	errno = 0;
26940087Sbostic 	if (!(dirp = opendir(pathbuf)))
27040087Sbostic 		/* todo: don't call for ENOENT or ENOTDIR? */
27140087Sbostic 		if (pglob->gl_errfunc &&
27240087Sbostic 		    (*pglob->gl_errfunc)(pathbuf, errno) ||
27340087Sbostic 		    (pglob->gl_flags & GLOB_ERR))
27440087Sbostic 			return(GLOB_ABEND);
27540087Sbostic 		else
27640087Sbostic 			return(0);
27740087Sbostic 
27840087Sbostic 	err = 0;
27940087Sbostic 
28040087Sbostic 	/* search directory for matching names */
28140087Sbostic 	while ((dp = readdir(dirp))) {
28240087Sbostic 		/* initial DOT must be matched literally */
28340087Sbostic 		if (dp->d_name[0] == DOT && *pattern != DOT)
28440087Sbostic 			continue;
28540087Sbostic 		if (!match(dp->d_name, pattern, restpattern))
28640087Sbostic 			continue;
28740087Sbostic 		len = dp->d_namlen;
28840087Sbostic 		(void)strcpy(pathend, dp->d_name);
28940087Sbostic 		err = glob2(pathbuf, pathend+len, restpattern, pglob);
29040087Sbostic 		if (err)
29140087Sbostic 			break;
29240087Sbostic 	}
29340087Sbostic 	/* todo: check error from readdir? */
29440087Sbostic 	(void)closedir(dirp);
29540087Sbostic 	return(err);
29640087Sbostic }
29740087Sbostic 
29840087Sbostic 
29940087Sbostic /*
30040087Sbostic  * Extend the gl_pathv member of a glob_t structure to accomodate a new item,
30140087Sbostic  * add the new item, and update gl_pathc.
30240087Sbostic  *
30340087Sbostic  * This assumes the BSD realloc, which only copies the block when its size
30440087Sbostic  * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
30540087Sbostic  * behavior.
30640087Sbostic  *
30740087Sbostic  * Return 0 if new item added, error code if memory couldn't be allocated.
30840087Sbostic  *
30940087Sbostic  * Invariant of the glob_t structure:
31040087Sbostic  *	Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
31140087Sbostic  *	 gl_pathv points to (gl_offs + gl_pathc + 1) items.
31240087Sbostic  */
31340087Sbostic static
31440087Sbostic globextend(path, pglob)
31540087Sbostic 	char *path;
31640087Sbostic 	glob_t *pglob;
31740087Sbostic {
31840087Sbostic 	register char **pathv;
31940087Sbostic 	register int i;
32040087Sbostic 	u_int copysize, newsize;
32140087Sbostic 	char *copy;
32240087Sbostic 
32340087Sbostic 	newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
32440087Sbostic 	pathv = (char **)realloc((char *)(pathv = pglob->gl_pathv), newsize);
32540087Sbostic 	if (pathv == NULL)
32640087Sbostic 		return(GLOB_NOSPACE);
32740087Sbostic 
32840087Sbostic 	if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
32940087Sbostic 		/* first time around -- clear initial gl_offs items */
33040087Sbostic 		pathv += pglob->gl_offs;
33140087Sbostic 		for (i = pglob->gl_offs; --i >= 0; )
33240087Sbostic 			*--pathv = NULL;
33340087Sbostic 	}
33440087Sbostic 	pglob->gl_pathv = pathv;
33540087Sbostic 
33640087Sbostic 	copysize = strlen(path) + 1;
33740087Sbostic 	if ((copy = malloc(copysize)) != NULL) {
33840087Sbostic 		(void)strcpy(copy, path);
33940087Sbostic 		pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
34040087Sbostic 	}
34140087Sbostic 	pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
34240087Sbostic 	return((copy == NULL) ? GLOB_NOSPACE : 0);
34340087Sbostic }
34440087Sbostic 
34540087Sbostic 
34640087Sbostic /*
34740087Sbostic  * pattern matching function for filenames.  Each occurrence of the *
34840087Sbostic  * pattern causes a recursion level.
34940087Sbostic  */
35040087Sbostic static bool_t
35140087Sbostic match(name, pat, patend)
35240087Sbostic 	register char *name, *pat, *patend;
35340087Sbostic {
35440087Sbostic 	bool_t ok, negate_range;
35540087Sbostic 	char c, k;
35640087Sbostic 
35740087Sbostic 	while (pat < patend) {
35840087Sbostic 		c = *pat++;
35940087Sbostic 		switch (c & 0xff) {
36040087Sbostic 		case M_ALL:
36140087Sbostic 			if (pat == patend)
36240087Sbostic 				return(1);
36340087Sbostic 			for (; *name != EOS; ++name) {
36440087Sbostic 				if (match(name, pat, patend))
36540087Sbostic 					return(1);
36640087Sbostic 			}
36740087Sbostic 			return(0);
36840087Sbostic 		case M_ONE:
36940087Sbostic 			if (*name++ == EOS)
37040087Sbostic 				return(0);
37140087Sbostic 			break;
37240087Sbostic 		case M_SET:
37340087Sbostic 			ok = 0;
37440087Sbostic 			k = *name++;
37540087Sbostic 			if (negate_range = (*pat & 0xff) == M_NOT)
37640087Sbostic 				++pat;
37740087Sbostic 			while (((c = *pat++) & 0xff) != M_END) {
37840087Sbostic 				if ((*pat & 0xff) == M_RNG) {
37940087Sbostic 					if (c <= k && k <= pat[1])
38040087Sbostic 						ok = 1;
38140087Sbostic 					pat += 2;
38240087Sbostic 				}
38340087Sbostic 				else if (c == k)
38440087Sbostic 					ok = 1;
38540087Sbostic 			}
38640087Sbostic 			if (ok == negate_range)
38740087Sbostic 				return(0);
38840087Sbostic 			break;
38940087Sbostic 		default:
39040087Sbostic 			if (*name++ != c)
39140087Sbostic 				return(0);
39240087Sbostic 			break;
39340087Sbostic 		}
39440087Sbostic 	}
39540087Sbostic 	return(*name == EOS);
39640087Sbostic }
39740087Sbostic 
39840087Sbostic /* free allocated data belonging to a glob_t structure */
39940087Sbostic void
40040087Sbostic globfree(pglob)
40140087Sbostic 	glob_t *pglob;
40240087Sbostic {
40340087Sbostic 	register int i;
40440087Sbostic 	register char **pp;
40540087Sbostic 
40640087Sbostic 	if (pglob->gl_pathv != NULL) {
40740087Sbostic 		pp = pglob->gl_pathv + pglob->gl_offs;
40840087Sbostic 		for (i = pglob->gl_pathc; i--; ++pp)
40940087Sbostic 			if (*pp)
41040087Sbostic 				(void)free(*pp);
41140087Sbostic 		(void)free((char *)pp);
41240087Sbostic 	}
41340087Sbostic }
414