|
|
1.1 root 1: /*
2: * Copyright (c) 1989 The Regents of the University of California.
3: * 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 are permitted provided
9: * that: (1) source distributions retain this entire copyright notice and
10: * comment, and (2) distributions including binaries display the following
11: * acknowledgement: ``This product includes software developed by the
12: * University of California, Berkeley and its contributors'' in the
13: * documentation or other materials provided with the distribution and in
14: * all advertising materials mentioning features or use of this software.
15: * Neither the name of the University nor the names of its contributors may
16: * be used to endorse or promote products derived from this software without
17: * specific prior written permission.
18: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
19: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
20: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
21: */
22:
23: #if defined(LIBC_SCCS) && !defined(lint)
24: static char sccsid[] = "@(#)glob.c 5.3 (Berkeley) 6/23/90";
25: #endif /* LIBC_SCCS and not lint */
26:
27: /*
28: * Glob: the interface is a superset of the one defined in POSIX 1003.2,
29: * draft 9.
30: *
31: * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
32: *
33: * Optional extra services, controlled by flags not defined by POSIX:
34: * GLOB_QUOTE: escaping convention: \ inhibits any special meaning
35: the following character might have (except \ at end of
36: * string is kept);
37: */
38:
39: #include <sys/param.h>
40: #include <sys/stat.h>
41: #include <dirent.h>
42: #include <glob.h>
43: #include <ctype.h>
44: #include <errno.h>
45: #include <string.h>
46: #include <stdio.h>
47:
48: char *malloc(), *realloc();
49:
50: typedef int bool_t;
51:
52: #define DOLLAR '$'
53: #define DOT '.'
54: #define EOS '\0'
55: #define LBRACKET '['
56: #define NOT '!'
57: #define QUESTION '?'
58: #define QUOTE '\\'
59: #define RANGE '-'
60: #define RBRACKET ']'
61: #define SEP '/'
62: #define STAR '*'
63: #define TILDE '~'
64: #define UNDERSCORE '_'
65:
66: #define METABIT 0x80
67: #define META(c) ((c)|METABIT)
68: #define M_ALL META('*')
69: #define M_END META(']')
70: #define M_NOT META('!')
71: #define M_ONE META('?')
72: #define M_RNG META('-')
73: #define M_SET META('[')
74: #define ismeta(c) (((c)&METABIT) != 0)
75:
76: static
77: compare(p, q)
78: void **p, **q;
79: {
80: return(strcmp(*(char **)p, *(char **)q));
81: }
82:
83:
84: /*
85: * The main glob() routine: compiles the pattern (optionally processing
86: * quotes), calls glob1() to do the real pattern matching, and finally
87: * sorts the list (unless unsorted operation is requested). Returns 0
88: * if things went well, nonzero if errors occurred. It is not an error
89: * to find no matches.
90: */
91: glob(pattern, flags, errfunc, pglob)
92: char *pattern;
93: int flags, (*errfunc)();
94: glob_t *pglob;
95: {
96: int err, oldpathc;
97: char *bufnext, *bufend, *compilebuf, *compilepat, *patnext;
98: char c, patbuf[MAXPATHLEN+1];
99:
100: patnext = pattern;
101: if (!(flags & GLOB_APPEND)) {
102: pglob->gl_pathc = 0;
103: pglob->gl_pathv = NULL;
104: if (!(flags & GLOB_DOOFFS))
105: pglob->gl_offs = 0;
106: }
107: pglob->gl_flags = flags;
108: pglob->gl_errfunc = errfunc;
109: oldpathc = pglob->gl_pathc;
110:
111: bufnext = patbuf;
112: bufend = bufnext+MAXPATHLEN;
113:
114: compilebuf = bufnext;
115: compilepat = patnext;
116: while (bufnext < bufend && (c = *patnext++) != EOS) {
117: switch (c) {
118: case LBRACKET:
119: c = *patnext;
120: if (c == NOT)
121: ++patnext;
122: if (*patnext == EOS ||
123: strchr(patnext+1, RBRACKET) == NULL) {
124: *bufnext++ = LBRACKET;
125: if (c == NOT)
126: --patnext;
127: break;
128: }
129: *bufnext++ = M_SET;
130: if (c == NOT)
131: *bufnext++ = M_NOT;
132: c = *patnext++;
133: do {
134: /* todo: quoting */
135: *bufnext++ = c;
136: if (*patnext == RANGE &&
137: (c = patnext[1]) != RBRACKET) {
138: *bufnext++ = M_RNG;
139: *bufnext++ = c;
140: patnext += 2;
141: }
142: } while ((c = *patnext++) != RBRACKET);
143: *bufnext++ = M_END;
144: break;
145: case QUESTION:
146: *bufnext++ = M_ONE;
147: break;
148: case QUOTE:
149: if (!(flags & GLOB_QUOTE))
150: *bufnext++ = QUOTE;
151: else {
152: if ((c = *patnext++) == EOS) {
153: c = QUOTE;
154: --patnext;
155: }
156: *bufnext++ = c;
157: }
158: break;
159: case STAR:
160: *bufnext++ = M_ALL;
161: break;
162: default:
163: *bufnext++ = c;
164: break;
165: }
166: }
167: *bufnext = EOS;
168:
169: if ((err = glob1(patbuf, pglob)) != 0)
170: return(err);
171:
172: if (pglob->gl_pathc == oldpathc && flags & GLOB_NOCHECK) {
173: if (!(flags & GLOB_QUOTE))
174: (void)strcpy(compilebuf, compilepat);
175: else {
176: /*
177: * copy pattern, interpreting quotes; this is slightly
178: * different than the interpretation of quotes above
179: * -- which should prevail?
180: */
181: while (*compilepat != EOS) {
182: if (*compilepat == QUOTE) {
183: if (*++compilepat == EOS)
184: --compilepat;
185: }
186: *compilebuf++ = *compilepat++;
187: }
188: *compilebuf = EOS;
189: }
190: return(globextend(patbuf, pglob));
191: } else if (!(flags & GLOB_NOSORT))
192: qsort((char*) (pglob->gl_pathv + pglob->gl_offs + oldpathc),
193: pglob->gl_pathc - oldpathc, sizeof(char*), compare);
194: return(0);
195: }
196:
197: static
198: glob1(pattern, pglob)
199: char *pattern;
200: glob_t *pglob;
201: {
202: char pathbuf[MAXPATHLEN+1];
203:
204: /*
205: * a null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
206: if (*pattern == EOS)
207: return(0);
208: return(glob2(pathbuf, pathbuf, pattern, pglob));
209: }
210:
211: /*
212: * functions glob2 and glob3 are mutually recursive; there is one level
213: * of recursion for each segment in the pattern that contains one or
214: * more meta characters.
215: */
216: static
217: glob2(pathbuf, pathend, pattern, pglob)
218: char *pathbuf, *pathend, *pattern;
219: glob_t *pglob;
220: {
221: struct stat sbuf;
222: bool_t anymeta = 0;
223: char *p, *q;
224:
225: /*
226: * loop over pattern segments until end of pattern or until
227: * segment with meta character found.
228: */
229: for (;;) {
230: if (*pattern == EOS) { /* end of pattern? */
231: *pathend = EOS;
232: if (stat(pathbuf, &sbuf) != 0)
233: return(0); /* need error call here? */
234: if ((pglob->gl_flags & GLOB_MARK) &&
235: pathend[-1] != SEP && S_ISDIR(sbuf.st_mode)) {
236: *pathend++ = SEP;
237: *pathend = EOS;
238: }
239: return(globextend(pathbuf, pglob));
240: }
241:
242: /* find end of next segment, copy tentatively to pathend */
243: q = pathend;
244: p = pattern;
245: while (*p != EOS && *p != SEP) {
246: if (ismeta(*p))
247: anymeta = 1;
248: *q++ = *p++;
249: }
250:
251: if (!anymeta) { /* no expansion, do next segment */
252: pathend = q;
253: pattern = p;
254: while (*pattern == SEP)
255: *pathend++ = *pattern++;
256: } else /* need expansion, recurse */
257: return(glob3(pathbuf, pathend, pattern, p, pglob));
258: }
259: /* NOTREACHED */
260: }
261:
262: static
263: glob3(pathbuf, pathend, pattern, restpattern, pglob)
264: char *pathbuf, *pathend, *pattern, *restpattern;
265: glob_t *pglob;
266: {
267: extern int errno;
268: DIR *dirp;
269: struct dirent *dp;
270: int len, err;
271:
272: *pathend = EOS;
273: errno = 0;
274: if (!(dirp = opendir(pathbuf)))
275: /* todo: don't call for ENOENT or ENOTDIR? */
276: if (pglob->gl_errfunc &&
277: (*pglob->gl_errfunc)(pathbuf, errno) ||
278: (pglob->gl_flags & GLOB_ERR))
279: return(GLOB_ABEND);
280: else
281: return(0);
282:
283: err = 0;
284:
285: /* search directory for matching names */
286: while ((dp = readdir(dirp))) {
287: /* initial DOT must be matched literally */
288: if (dp->d_name[0] == DOT && *pattern != DOT)
289: continue;
290: if (!match(dp->d_name, pattern, restpattern))
291: continue;
292: len = dp->d_namlen;
293: (void)strcpy(pathend, dp->d_name);
294: err = glob2(pathbuf, pathend+len, restpattern, pglob);
295: if (err)
296: break;
297: }
298: /* todo: check error from readdir? */
299: (void)closedir(dirp);
300: return(err);
301: }
302:
303:
304: /*
305: * Extend the gl_pathv member of a glob_t structure to accomodate a new item,
306: * add the new item, and update gl_pathc.
307: *
308: * This assumes the BSD realloc, which only copies the block when its size
309: * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
310: * behavior.
311: *
312: * Return 0 if new item added, error code if memory couldn't be allocated.
313: *
314: * Invariant of the glob_t structure:
315: * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
316: * gl_pathv points to (gl_offs + gl_pathc + 1) items.
317: */
318: static
319: globextend(path, pglob)
320: char *path;
321: glob_t *pglob;
322: {
323: register char **pathv;
324: register int i;
325: u_int copysize, newsize;
326: char *copy;
327:
328: newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
329: pathv = (char **)realloc((char *)(pathv = pglob->gl_pathv), newsize);
330: if (pathv == NULL)
331: return(GLOB_NOSPACE);
332:
333: if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
334: /* first time around -- clear initial gl_offs items */
335: pathv += pglob->gl_offs;
336: for (i = pglob->gl_offs; --i >= 0; )
337: *--pathv = NULL;
338: }
339: pglob->gl_pathv = pathv;
340:
341: copysize = strlen(path) + 1;
342: if ((copy = malloc(copysize)) != NULL) {
343: (void)strcpy(copy, path);
344: pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
345: }
346: pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
347: return((copy == NULL) ? GLOB_NOSPACE : 0);
348: }
349:
350:
351: /*
352: * pattern matching function for filenames. Each occurrence of the *
353: * pattern causes a recursion level.
354: */
355: static bool_t
356: match(name, pat, patend)
357: register char *name, *pat, *patend;
358: {
359: bool_t ok, negate_range;
360: char c, k;
361:
362: while (pat < patend) {
363: c = *pat++;
364: switch (c & 0xff) {
365: case M_ALL:
366: if (pat == patend)
367: return(1);
368: for (; *name != EOS; ++name) {
369: if (match(name, pat, patend))
370: return(1);
371: }
372: return(0);
373: case M_ONE:
374: if (*name++ == EOS)
375: return(0);
376: break;
377: case M_SET:
378: ok = 0;
379: k = *name++;
380: if (negate_range = (*pat & 0xff) == M_NOT)
381: ++pat;
382: while (((c = *pat++) & 0xff) != M_END) {
383: if ((*pat & 0xff) == M_RNG) {
384: if (c <= k && k <= pat[1])
385: ok = 1;
386: pat += 2;
387: }
388: else if (c == k)
389: ok = 1;
390: }
391: if (ok == negate_range)
392: return(0);
393: break;
394: default:
395: if (*name++ != c)
396: return(0);
397: break;
398: }
399: }
400: return(*name == EOS);
401: }
402:
403: /* free allocated data belonging to a glob_t structure */
404: void
405: globfree(pglob)
406: glob_t *pglob;
407: {
408: register int i;
409: register char **pp;
410:
411: if (pglob->gl_pathv != NULL) {
412: pp = pglob->gl_pathv + pglob->gl_offs;
413: for (i = pglob->gl_pathc; i--; ++pp)
414: if (*pp)
415: (void)free(*pp);
416: (void)free((char *)pp);
417: }
418: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.