glob.c revision 69408
1244769Sglebius/*
2130610Smlaier * Copyright (c) 1989 The Regents of the University of California.
3130610Smlaier * All rights reserved.
4244769Sglebius *
5244769Sglebius * This code is derived from software contributed to Berkeley by
6244769Sglebius * Guido van Rossum.
7130610Smlaier *
8130610Smlaier * Redistribution and use in source and binary forms, with or without
9130610Smlaier * modification, are permitted provided that the following conditions
10130610Smlaier * are met:
11130610Smlaier * 1. Redistributions of source code must retain the above copyright
12130610Smlaier *    notice, this list of conditions and the following disclaimer.
13130610Smlaier * 2. Redistributions in binary form must reproduce the above copyright
14130610Smlaier *    notice, this list of conditions and the following disclaimer in the
15130610Smlaier *    documentation and/or other materials provided with the distribution.
16130610Smlaier * 3. All advertising materials mentioning features or use of this software
17130610Smlaier *    must display the following acknowledgement:
18130610Smlaier *	This product includes software developed by the University of
19130610Smlaier *	California, Berkeley and its contributors.
20130610Smlaier * 4. Neither the name of the University nor the names of its contributors
21130610Smlaier *    may be used to endorse or promote products derived from this software
22130610Smlaier *    without specific prior written permission.
23130610Smlaier *
24130610Smlaier * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25130610Smlaier * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26130610Smlaier * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27130610Smlaier * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28130610Smlaier * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29130610Smlaier * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30130610Smlaier * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31130610Smlaier * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32244769Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33244769Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34130610Smlaier * SUCH DAMAGE.
35130610Smlaier */
36240233Sglebius#if defined(LIBC_SCCS) && !defined(lint)
37240233Sglebiusstatic char sccsid[] = "@(#)glob.c	5.12 (Berkeley) 6/24/91";
38240233Sglebius#endif /* LIBC_SCCS and not lint */
39130613Smlaier/*
40130613Smlaier * Glob: the interface is a superset of the one defined in POSIX 1003.2,
41171168Smlaier * draft 9.
42130610Smlaier *
43240233Sglebius * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
44130610Smlaier *
45130610Smlaier * Optional extra services, controlled by flags not defined by POSIX:
46130610Smlaier *
47130610Smlaier * GLOB_QUOTE:
48240233Sglebius *	Escaping convention: \ inhibits any special meaning the following
49130610Smlaier *	character might have (except \ at end of string is retained).
50240233Sglebius * GLOB_MAGCHAR:
51240233Sglebius *	Set in gl_flags if pattern contained a globbing character.
52240233Sglebius * GLOB_ALTNOT:
53240233Sglebius *	Use ^ instead of ! for "not".
54130610Smlaier * gl_matchc:
55240233Sglebius *	Number of matches in the current invocation of glob.
56240233Sglebius */
57240233Sglebius
58223637Sbz#ifdef notdef
59223637Sbz#include <sys/types.h>
60223637Sbz#include <sys/param.h>
61240233Sglebius#include <sys/stat.h>
62223637Sbz#include <dirent.h>
63223637Sbz#include <ctype.h>
64223637Sbztypedef void * ptr_t;
65223637Sbz#endif
66223637Sbz#ifdef WINNT_NATIVE
67223637Sbz	#pragma warning(disable:4244)
68130610Smlaier#endif /* WINNT_NATIVE */
69240233Sglebius
70240233Sglebius#define Char __Char
71130610Smlaier#include "sh.h"
72240233Sglebius#undef Char
73240233Sglebius#undef QUOTE
74240233Sglebius#undef TILDE
75240233Sglebius#undef META
76240233Sglebius#undef CHAR
77240233Sglebius#undef ismeta
78240233Sglebius#undef Strchr
79240233Sglebius
80240233Sglebius#include "glob.h"
81240233Sglebius
82240233Sglebius#ifndef S_ISDIR
83240233Sglebius#define S_ISDIR(a)	(((a) & S_IFMT) == S_IFDIR)
84240233Sglebius#endif
85240233Sglebius
86240233Sglebius#if !defined(S_ISLNK) && defined(S_IFLNK)
87130610Smlaier#define S_ISLNK(a)	(((a) & S_IFMT) == S_IFLNK)
88240233Sglebius#endif
89240233Sglebius
90240233Sglebius#if !defined(S_ISLNK) && !defined(lstat)
91240233Sglebius#define lstat stat
92240233Sglebius#endif
93130610Smlaier
94240233Sglebiustypedef unsigned short Char;
95240233Sglebius
96240233Sglebiusstatic	int	 glob1 		__P((Char *, glob_t *, int));
97240233Sglebiusstatic	int	 glob2		__P((Char *, Char *, Char *, glob_t *, int));
98240233Sglebiusstatic	int	 glob3		__P((Char *, Char *, Char *, Char *,
99240233Sglebius				     glob_t *, int));
100240233Sglebiusstatic	int	 globextend	__P((Char *, glob_t *));
101240233Sglebiusstatic	int	 match		__P((Char *, Char *, Char *, int));
102130610Smlaier#ifndef __clipper__
103130610Smlaierstatic	int	 compare	__P((const ptr_t, const ptr_t));
104130610Smlaier#endif
105240233Sglebiusstatic 	DIR	*Opendir	__P((Char *));
106240233Sglebius#ifdef S_IFLNK
107240233Sglebiusstatic	int	 Lstat		__P((Char *, struct stat *));
108130610Smlaier#endif
109223637Sbzstatic	int	 Stat		__P((Char *, struct stat *sb));
110223637Sbzstatic 	Char 	*Strchr		__P((Char *, int));
111223637Sbz#ifdef DEBUG
112223637Sbzstatic	void	 qprintf	__P((Char *));
113240233Sglebius#endif
114171168Smlaier
115240233Sglebius#define	DOLLAR		'$'
116240233Sglebius#define	DOT		'.'
117240233Sglebius#define	EOS		'\0'
118240233Sglebius#define	LBRACKET	'['
119171168Smlaier#define	NOT		'!'
120130613Smlaier#define ALTNOT		'^'
121181803Sbz#define	QUESTION	'?'
122171168Smlaier#define	QUOTE		'\\'
123181803Sbz#define	RANGE		'-'
124137159Smlaier#define	RBRACKET	']'
125130613Smlaier#define	SEP		'/'
126171168Smlaier#define	STAR		'*'
127130613Smlaier#define	TILDE		'~'
128130613Smlaier#define	UNDERSCORE	'_'
129130613Smlaier
130130613Smlaier#define	M_META		0x8000
131171168Smlaier#define M_PROTECT	0x4000
132223637Sbz#define	M_MASK		0xffff
133171168Smlaier#define	M_ASCII		0x00ff
134223637Sbz
135171168Smlaier#define	CHAR(c)		((c)&M_ASCII)
136223637Sbz#define	META(c)		((c)|M_META)
137171168Smlaier#define	M_ALL		META('*')
138171168Smlaier#define	M_END		META(']')
139130610Smlaier#define	M_NOT		META('!')
140130610Smlaier#define	M_ALTNOT	META('^')
141173822Smlaier#define	M_ONE		META('?')
142173822Smlaier#define	M_RNG		META('-')
143173822Smlaier#define	M_SET		META('[')
144173822Smlaier#define	ismeta(c)	(((c)&M_META) != 0)
145173822Smlaier
146173822Smlaier#ifndef BUFSIZE
147173822Smlaier#define GLOBBUFLEN	MAXPATHLEN
148173822Smlaier#else
149173822Smlaier#define GLOBBUFLEN	BUFSIZE
150173822Smlaier#endif
151173822Smlaier
152173822Smlaierint
153223637Sbzglobcharcoll(c1, c2)
154223637Sbz    int c1, c2;
155223637Sbz{
156173822Smlaier#if defined(NLS) && defined(LC_COLLATE) && !defined(NOSTRCOLL)
157173822Smlaier    char s1[2], s2[2];
158173822Smlaier
159240233Sglebius    if (c1 == c2)
160240233Sglebius	return (0);
161240233Sglebius    /*
162240233Sglebius     * From kevin lyda <kevin@suberic.net>:
163240233Sglebius     * strcoll does not guarantee case sorting, so we pre-process now:
164240233Sglebius     */
165240233Sglebius    if (islower(c1) && isupper(c2))
166223637Sbz	return (1);
167173822Smlaier    s1[0] = c1;
168173822Smlaier    s2[0] = c2;
169171168Smlaier    s1[1] = s2[1] = '\0';
170240233Sglebius    return strcoll(s1, s2);
171130613Smlaier#else
172240233Sglebius    return (c1 - c2);
173130613Smlaier#endif
174240233Sglebius}
175240233Sglebius
176171168Smlaier/*
177171168Smlaier * Need to dodge two kernel bugs:
178132567Smlaier * opendir("") != opendir(".")
179240233Sglebius * NAMEI_BUG: on plain files trailing slashes are ignored in some kernels.
180240233Sglebius *            POSIX specifies that they should be ignored in directories.
181130613Smlaier */
182240233Sglebius
183240233Sglebiusstatic DIR *
184240233SglebiusOpendir(str)
185240233Sglebius    register Char *str;
186240233Sglebius{
187240233Sglebius    char    buf[GLOBBUFLEN];
188240233Sglebius    register char *dc = buf;
189240233Sglebius#if defined(hpux) || defined(__hpux)
190240233Sglebius    struct stat st;
191240233Sglebius#endif
192240233Sglebius
193240233Sglebius    if (!*str)
194240233Sglebius	return (opendir("."));
195240233Sglebius    while ((*dc++ = *str++) != '\0')
196240233Sglebius	continue;
197171168Smlaier#if defined(hpux) || defined(__hpux)
198173825Smlaier    /*
199173825Smlaier     * Opendir on some device files hangs, so avoid it
200173825Smlaier     */
201173825Smlaier    if (stat(buf, &st) == -1 || !S_ISDIR(st.st_mode))
202173825Smlaier	return NULL;
203240233Sglebius#endif
204173825Smlaier    return (opendir(buf));
205173825Smlaier}
206171168Smlaier
207130613Smlaier#ifdef S_IFLNK
208223637Sbzstatic int
209223637SbzLstat(fn, sb)
210171168Smlaier    register Char *fn;
211130613Smlaier    struct stat *sb;
212130613Smlaier{
213130613Smlaier    char    buf[GLOBBUFLEN];
214240233Sglebius    register char *dc = buf;
215130613Smlaier
216240233Sglebius    while ((*dc++ = *fn++) != '\0')
217240233Sglebius	continue;
218240233Sglebius# ifdef NAMEI_BUG
219130613Smlaier    {
220130613Smlaier	int     st;
221130613Smlaier
222240233Sglebius	st = lstat(buf, sb);
223130613Smlaier	if (*buf)
224130613Smlaier	    dc--;
225240233Sglebius	return (*--dc == '/' && !S_ISDIR(sb->st_mode) ? -1 : st);
226240233Sglebius    }
227171168Smlaier# else
228240233Sglebius    return (lstat(buf, sb));
229240233Sglebius# endif	/* NAMEI_BUG */
230240233Sglebius}
231171168Smlaier#else
232171168Smlaier#define Lstat Stat
233240233Sglebius#endif /* S_IFLNK */
234240233Sglebius
235171168Smlaierstatic int
236171168SmlaierStat(fn, sb)
237223637Sbz    register Char *fn;
238240233Sglebius    struct stat *sb;
239240233Sglebius{
240240233Sglebius    char    buf[GLOBBUFLEN];
241240233Sglebius    register char *dc = buf;
242240233Sglebius
243240233Sglebius    while ((*dc++ = *fn++) != '\0')
244130613Smlaier	continue;
245130613Smlaier#ifdef NAMEI_BUG
246240233Sglebius    {
247240233Sglebius	int     st;
248240233Sglebius
249240233Sglebius	st = stat(buf, sb);
250240233Sglebius	if (*buf)
251240233Sglebius	    dc--;
252240233Sglebius	return (*--dc == '/' && !S_ISDIR(sb->st_mode) ? -1 : st);
253240233Sglebius    }
254240233Sglebius#else
255240233Sglebius    return (stat(buf, sb));
256240233Sglebius#endif /* NAMEI_BUG */
257240233Sglebius}
258240233Sglebius
259240233Sglebiusstatic Char *
260240233SglebiusStrchr(str, ch)
261240233Sglebius    Char *str;
262240233Sglebius    int ch;
263240233Sglebius{
264240233Sglebius    do
265240233Sglebius	if (*str == ch)
266171168Smlaier	    return (str);
267171168Smlaier    while (*str++);
268130613Smlaier    return (NULL);
269171168Smlaier}
270130613Smlaier
271171168Smlaier#ifdef DEBUG
272171168Smlaierstatic void
273171168Smlaierqprintf(s)
274171168SmlaierChar *s;
275240233Sglebius{
276171168Smlaier    Char *p;
277171168Smlaier
278171168Smlaier    for (p = s; *p; p++)
279171168Smlaier	printf("%c", *p & 0xff);
280171168Smlaier    printf("\n");
281130610Smlaier    for (p = s; *p; p++)
282130610Smlaier	printf("%c", *p & M_PROTECT ? '"' : ' ');
283240233Sglebius    printf("\n");
284130610Smlaier    for (p = s; *p; p++)
285130610Smlaier	printf("%c", *p & M_META ? '_' : ' ');
286240233Sglebius    printf("\n");
287130610Smlaier}
288240233Sglebius#endif /* DEBUG */
289240233Sglebius
290240233Sglebiusstatic int
291223637Sbzcompare(p, q)
292240233Sglebius    const ptr_t  p, q;
293130610Smlaier{
294171168Smlaier#if defined(NLS) && !defined(NOSTRCOLL)
295240233Sglebius    errno = 0;  /* strcoll sets errno, another brain-damage */
296130610Smlaier
297171168Smlaier    return (strcoll(*(char **) p, *(char **) q));
298240233Sglebius#else
299130610Smlaier    return (strcmp(*(char **) p, *(char **) q));
300130610Smlaier#endif /* NLS && !NOSTRCOLL */
301240233Sglebius}
302240233Sglebius
303130610Smlaier/*
304240233Sglebius * The main glob() routine: compiles the pattern (optionally processing
305130610Smlaier * quotes), calls glob1() to do the real pattern matching, and finally
306240233Sglebius * sorts the list (unless unsorted operation is requested).  Returns 0
307130610Smlaier * if things went well, nonzero if errors occurred.  It is not an error
308240233Sglebius * to find no matches.
309223637Sbz */
310240233Sglebiusint
311171168Smlaierglob(pattern, flags, errfunc, pglob)
312171168Smlaier    const char *pattern;
313240233Sglebius    int     flags;
314240233Sglebius    int     (*errfunc) __P((const char *, int));
315130610Smlaier    glob_t *pglob;
316130610Smlaier{
317171168Smlaier    int     err, oldpathc;
318171168Smlaier    Char *bufnext, *bufend, *compilebuf, m_not;
319130610Smlaier    const unsigned char *compilepat, *patnext;
320171168Smlaier    int     c, not;
321171168Smlaier    Char patbuf[GLOBBUFLEN + 1], *qpatnext;
322171168Smlaier    int     no_match;
323171168Smlaier
324171168Smlaier    patnext = (unsigned char *) pattern;
325171168Smlaier    if (!(flags & GLOB_APPEND)) {
326171168Smlaier	pglob->gl_pathc = 0;
327171168Smlaier	pglob->gl_pathv = NULL;
328171168Smlaier	if (!(flags & GLOB_DOOFFS))
329171168Smlaier	    pglob->gl_offs = 0;
330171168Smlaier    }
331171168Smlaier    pglob->gl_flags = flags & ~GLOB_MAGCHAR;
332171168Smlaier    pglob->gl_errfunc = errfunc;
333171168Smlaier    oldpathc = pglob->gl_pathc;
334171168Smlaier    pglob->gl_matchc = 0;
335171168Smlaier
336171168Smlaier    if (pglob->gl_flags & GLOB_ALTNOT) {
337171168Smlaier	not = ALTNOT;
338171168Smlaier	m_not = M_ALTNOT;
339171168Smlaier    }
340171168Smlaier    else {
341171168Smlaier	not = NOT;
342171168Smlaier	m_not = M_NOT;
343171168Smlaier    }
344171168Smlaier
345171168Smlaier    bufnext = patbuf;
346171168Smlaier    bufend = bufnext + GLOBBUFLEN;
347171168Smlaier    compilebuf = bufnext;
348171168Smlaier    compilepat = patnext;
349130610Smlaier
350130610Smlaier    no_match = *patnext == not;
351130610Smlaier    if (no_match)
352130610Smlaier	patnext++;
353130610Smlaier
354130610Smlaier    if (flags & GLOB_QUOTE) {
355130610Smlaier	/* Protect the quoted characters */
356130610Smlaier	while (bufnext < bufend && (c = *patnext++) != EOS)
357130610Smlaier#ifdef DSPMBYTE
358240233Sglebius	    if (Ismbyte1(c) && *patnext != EOS)
359240233Sglebius	    {
360130610Smlaier	      *bufnext++ = (Char) c;
361240233Sglebius	      *bufnext++ = (Char) *patnext++;
362240233Sglebius	    }
363240233Sglebius	    else
364240233Sglebius#endif /* DSPMBYTE */
365130610Smlaier	    if (c == QUOTE) {
366240233Sglebius		if ((c = *patnext++) == EOS) {
367240233Sglebius		    c = QUOTE;
368240233Sglebius		    --patnext;
369240233Sglebius		}
370240233Sglebius		*bufnext++ = (Char) (c | M_PROTECT);
371240233Sglebius	    }
372240233Sglebius	    else
373240233Sglebius		*bufnext++ = (Char) c;
374171168Smlaier    }
375240233Sglebius    else
376171168Smlaier	while (bufnext < bufend && (c = *patnext++) != EOS)
377240233Sglebius	    *bufnext++ = (Char) c;
378240233Sglebius    *bufnext = EOS;
379130610Smlaier
380130610Smlaier    bufnext = patbuf;
381130610Smlaier    qpatnext = patbuf;
382130610Smlaier    /* we don't need to check for buffer overflow any more */
383130610Smlaier    while ((c = *qpatnext++) != EOS) {
384130610Smlaier#ifdef DSPMBYTE
385130610Smlaier	if (Ismbyte1(c) && *qpatnext != EOS)
386130610Smlaier	{
387130610Smlaier	  *bufnext++ = CHAR(c);
388130610Smlaier	  *bufnext++ = CHAR(*qpatnext++);
389130610Smlaier	}
390130610Smlaier	else
391130610Smlaier#endif /* DSPMBYTE */
392130610Smlaier	switch (c) {
393130610Smlaier	case LBRACKET:
394130610Smlaier	    c = *qpatnext;
395171168Smlaier	    if (c == not)
396240233Sglebius		++qpatnext;
397171168Smlaier	    if (*qpatnext == EOS ||
398171168Smlaier		Strchr(qpatnext + 1, RBRACKET) == NULL) {
399130610Smlaier		*bufnext++ = LBRACKET;
400240233Sglebius		if (c == not)
401240233Sglebius		    --qpatnext;
402171168Smlaier		break;
403171168Smlaier	    }
404130610Smlaier	    pglob->gl_flags |= GLOB_MAGCHAR;
405130610Smlaier	    *bufnext++ = M_SET;
406130610Smlaier	    if (c == not)
407130610Smlaier		*bufnext++ = m_not;
408130610Smlaier	    c = *qpatnext++;
409171168Smlaier	    do {
410130610Smlaier		*bufnext++ = CHAR(c);
411171168Smlaier		if (*qpatnext == RANGE &&
412240233Sglebius		    (c = qpatnext[1]) != RBRACKET) {
413130610Smlaier		    *bufnext++ = M_RNG;
414130610Smlaier		    *bufnext++ = CHAR(c);
415130610Smlaier		    qpatnext += 2;
416130610Smlaier		}
417130610Smlaier	    } while ((c = *qpatnext++) != RBRACKET);
418130610Smlaier	    *bufnext++ = M_END;
419130610Smlaier	    break;
420130610Smlaier	case QUESTION:
421240233Sglebius	    pglob->gl_flags |= GLOB_MAGCHAR;
422240233Sglebius	    *bufnext++ = M_ONE;
423240233Sglebius	    break;
424130610Smlaier	case STAR:
425130610Smlaier	    pglob->gl_flags |= GLOB_MAGCHAR;
426130610Smlaier	    /* collapse adjacent stars to one, to avoid
427240233Sglebius	     * exponential behavior
428171168Smlaier	     */
429130610Smlaier	    if (bufnext == patbuf || bufnext[-1] != M_ALL)
430171168Smlaier		*bufnext++ = M_ALL;
431171168Smlaier	    break;
432171168Smlaier	default:
433240233Sglebius	    *bufnext++ = CHAR(c);
434240233Sglebius	    break;
435171168Smlaier	}
436171168Smlaier    }
437171168Smlaier    *bufnext = EOS;
438171168Smlaier#ifdef DEBUG
439171168Smlaier    qprintf(patbuf);
440240233Sglebius#endif
441240233Sglebius
442171168Smlaier    if ((err = glob1(patbuf, pglob, no_match)) != 0)
443171168Smlaier	return (err);
444171168Smlaier
445240233Sglebius    /*
446240233Sglebius     * If there was no match we are going to append the pattern
447171168Smlaier     * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
448171168Smlaier     * and the pattern did not contain any magic characters
449240233Sglebius     * GLOB_NOMAGIC is there just for compatibility with csh.
450171168Smlaier     */
451171168Smlaier    if (pglob->gl_pathc == oldpathc &&
452145836Smlaier	((flags & GLOB_NOCHECK) ||
453145836Smlaier	 ((flags & GLOB_NOMAGIC) && !(pglob->gl_flags & GLOB_MAGCHAR)))) {
454130610Smlaier	if (!(flags & GLOB_QUOTE)) {
455240233Sglebius	    Char *dp = compilebuf;
456240233Sglebius	    const unsigned char *sp = compilepat;
457240233Sglebius
458145836Smlaier	    while ((*dp++ = *sp++) != '\0')
459145836Smlaier		continue;
460145836Smlaier	}
461171168Smlaier	else {
462223637Sbz	    /*
463130610Smlaier	     * copy pattern, interpreting quotes; this is slightly different
464130610Smlaier	     * than the interpretation of quotes above -- which should prevail?
465223637Sbz	     */
466130610Smlaier	    while (*compilepat != EOS) {
467130610Smlaier		if (*compilepat == QUOTE) {
468130610Smlaier		    if (*++compilepat == EOS)
469130610Smlaier			--compilepat;
470240233Sglebius		}
471130610Smlaier		*compilebuf++ = (unsigned char) *compilepat++;
472130610Smlaier	    }
473130610Smlaier	    *compilebuf = EOS;
474171168Smlaier	}
475130610Smlaier	return (globextend(patbuf, pglob));
476223637Sbz    }
477171168Smlaier    else if (!(flags & GLOB_NOSORT) && (pglob->gl_pathc != oldpathc))
478171168Smlaier	qsort((char *) (pglob->gl_pathv + pglob->gl_offs + oldpathc),
479130610Smlaier	      pglob->gl_pathc - oldpathc, sizeof(char *),
480240233Sglebius	      (int (*) __P((const void *, const void *))) compare);
481244210Sglebius    return (0);
482171168Smlaier}
483171168Smlaier
484244210Sglebiusstatic int
485240233Sglebiusglob1(pattern, pglob, no_match)
486171168Smlaier    Char *pattern;
487223637Sbz    glob_t *pglob;
488223637Sbz    int     no_match;
489240233Sglebius{
490240233Sglebius    Char pathbuf[GLOBBUFLEN + 1];
491130610Smlaier
492130610Smlaier    /*
493240233Sglebius     * a null pathname is invalid -- POSIX 1003.1 sect. 2.4.
494130610Smlaier     */
495130610Smlaier    if (*pattern == EOS)
496130610Smlaier	return (0);
497130610Smlaier    return (glob2(pathbuf, pathbuf, pattern, pglob, no_match));
498130610Smlaier}
499130610Smlaier
500240233Sglebius/*
501240233Sglebius * functions glob2 and glob3 are mutually recursive; there is one level
502130610Smlaier * of recursion for each segment in the pattern that contains one or
503130610Smlaier * more meta characters.
504130610Smlaier */
505130610Smlaierstatic int
506130610Smlaierglob2(pathbuf, pathend, pattern, pglob, no_match)
507173825Smlaier    Char *pathbuf, *pathend, *pattern;
508173825Smlaier    glob_t *pglob;
509223637Sbz    int     no_match;
510223637Sbz{
511223637Sbz    struct stat sbuf;
512173825Smlaier    int anymeta;
513173825Smlaier    Char *p, *q;
514173825Smlaier
515173825Smlaier    /*
516173825Smlaier     * loop over pattern segments until end of pattern or until segment with
517130610Smlaier     * meta character found.
518130610Smlaier     */
519130610Smlaier    anymeta = 0;
520130610Smlaier    for (;;) {
521130610Smlaier	if (*pattern == EOS) {	/* end of pattern? */
522130610Smlaier	    *pathend = EOS;
523130610Smlaier
524130610Smlaier	    if (Lstat(pathbuf, &sbuf))
525130610Smlaier		return (0);
526130610Smlaier
527130610Smlaier	    if (((pglob->gl_flags & GLOB_MARK) &&
528130610Smlaier		 pathend[-1] != SEP) &&
529130610Smlaier		(S_ISDIR(sbuf.st_mode)
530130610Smlaier#ifdef S_IFLNK
531130610Smlaier		 || (S_ISLNK(sbuf.st_mode) &&
532130610Smlaier		     (Stat(pathbuf, &sbuf) == 0) &&
533130610Smlaier		     S_ISDIR(sbuf.st_mode))
534130610Smlaier#endif
535130610Smlaier		 )) {
536130610Smlaier		*pathend++ = SEP;
537145836Smlaier		*pathend = EOS;
538130610Smlaier	    }
539130610Smlaier	    ++pglob->gl_matchc;
540130610Smlaier	    return (globextend(pathbuf, pglob));
541171168Smlaier	}
542130610Smlaier
543130610Smlaier	/* find end of next segment, copy tentatively to pathend */
544171168Smlaier	q = pathend;
545130610Smlaier	p = pattern;
546130610Smlaier	while (*p != EOS && *p != SEP) {
547130610Smlaier	    if (ismeta(*p))
548130610Smlaier		anymeta = 1;
549130610Smlaier	    *q++ = *p++;
550130610Smlaier	}
551130610Smlaier
552130610Smlaier	if (!anymeta) {		/* no expansion, do next segment */
553130610Smlaier	    pathend = q;
554130610Smlaier	    pattern = p;
555130610Smlaier	    while (*pattern == SEP)
556130610Smlaier		*pathend++ = *pattern++;
557240233Sglebius	}
558130610Smlaier	else			/* need expansion, recurse */
559130610Smlaier	    return (glob3(pathbuf, pathend, pattern, p, pglob, no_match));
560240233Sglebius    }
561130610Smlaier    /* NOTREACHED */
562130610Smlaier}
563130610Smlaier
564130610Smlaier
565130610Smlaierstatic int
566223637Sbzglob3(pathbuf, pathend, pattern, restpattern, pglob, no_match)
567223637Sbz    Char *pathbuf, *pathend, *pattern, *restpattern;
568130610Smlaier    glob_t *pglob;
569130610Smlaier    int     no_match;
570240233Sglebius{
571223637Sbz    DIR    *dirp;
572130610Smlaier    struct dirent *dp;
573130610Smlaier    int     err;
574223637Sbz    Char m_not = (pglob->gl_flags & GLOB_ALTNOT) ? M_ALTNOT : M_NOT;
575130613Smlaier    char cpathbuf[GLOBBUFLEN], *ptr;;
576130610Smlaier
577240233Sglebius    *pathend = EOS;
578240233Sglebius    errno = 0;
579130610Smlaier
580130610Smlaier    if (!(dirp = Opendir(pathbuf))) {
581223637Sbz	/* todo: don't call for ENOENT or ENOTDIR? */
582130610Smlaier	for (ptr = cpathbuf; (*ptr++ = (char) *pathbuf++) != EOS;)
583223637Sbz	    continue;
584223637Sbz	if ((pglob->gl_errfunc && (*pglob->gl_errfunc) (cpathbuf, errno)) ||
585223637Sbz	    (pglob->gl_flags & GLOB_ERR))
586130610Smlaier	    return (GLOB_ABEND);
587130610Smlaier	else
588130610Smlaier	    return (0);
589223637Sbz    }
590130610Smlaier
591130610Smlaier    err = 0;
592130610Smlaier
593130610Smlaier    /* search directory for matching names */
594130610Smlaier    while ((dp = readdir(dirp)) != NULL) {
595171168Smlaier	register unsigned char *sc;
596130610Smlaier	register Char *dc;
597171168Smlaier
598130610Smlaier	/* initial DOT must be matched literally */
599130610Smlaier	if (dp->d_name[0] == DOT && *pattern != DOT)
600130610Smlaier	    continue;
601130610Smlaier	for (sc = (unsigned char *) dp->d_name, dc = pathend;
602130610Smlaier	     (*dc++ = *sc++) != '\0';)
603130610Smlaier	    continue;
604130610Smlaier	if (match(pathend, pattern, restpattern, (int) m_not) == no_match) {
605130610Smlaier	    *pathend = EOS;
606130610Smlaier	    continue;
607130610Smlaier	}
608240233Sglebius	err = glob2(pathbuf, --dc, restpattern, pglob, no_match);
609130610Smlaier	if (err)
610130610Smlaier	    break;
611240233Sglebius    }
612240233Sglebius    /* todo: check error from readdir? */
613130610Smlaier    (void) closedir(dirp);
614240233Sglebius    return (err);
615240233Sglebius}
616240233Sglebius
617240233Sglebius
618130610Smlaier/*
619130610Smlaier * Extend the gl_pathv member of a glob_t structure to accomodate a new item,
620130610Smlaier * add the new item, and update gl_pathc.
621130610Smlaier *
622130610Smlaier * This assumes the BSD realloc, which only copies the block when its size
623240233Sglebius * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
624240233Sglebius * behavior.
625240233Sglebius *
626240233Sglebius * Return 0 if new item added, error code if memory couldn't be allocated.
627240233Sglebius *
628130610Smlaier * Invariant of the glob_t structure:
629130610Smlaier *	Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
630130610Smlaier *	 gl_pathv points to (gl_offs + gl_pathc + 1) items.
631130610Smlaier */
632240233Sglebiusstatic int
633130610Smlaierglobextend(path, pglob)
634130610Smlaier    Char *path;
635130610Smlaier    glob_t *pglob;
636130610Smlaier{
637130610Smlaier    register char **pathv;
638130610Smlaier    register int i;
639223637Sbz    unsigned int newsize;
640130610Smlaier    char   *copy;
641171168Smlaier    Char *p;
642223637Sbz
643223637Sbz    newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
644223637Sbz    pathv = (char **) (pglob->gl_pathv ?
645240233Sglebius		       xrealloc((ptr_t) pglob->gl_pathv, (size_t) newsize) :
646130610Smlaier		       xmalloc((size_t) newsize));
647223637Sbz    if (pathv == NULL)
648223637Sbz	return (GLOB_NOSPACE);
649240233Sglebius
650130610Smlaier    if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
651240233Sglebius	/* first time around -- clear initial gl_offs items */
652223637Sbz	pathv += pglob->gl_offs;
653223637Sbz	for (i = pglob->gl_offs; --i >= 0;)
654223637Sbz	    *--pathv = NULL;
655223637Sbz    }
656223637Sbz    pglob->gl_pathv = pathv;
657223637Sbz
658223637Sbz    for (p = path; *p++;)
659223637Sbz	continue;
660223637Sbz    if ((copy = (char *) xmalloc((size_t) (p - path))) != NULL) {
661223637Sbz	register char *dc = copy;
662223637Sbz	register Char *sc = path;
663223637Sbz
664223637Sbz	while ((*dc++ = *sc++) != '\0')
665223637Sbz	    continue;
666223637Sbz	pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
667223637Sbz    }
668223637Sbz    pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
669223637Sbz    return ((copy == NULL) ? GLOB_NOSPACE : 0);
670130610Smlaier}
671223637Sbz
672223637Sbz
673223637Sbz/*
674223637Sbz * pattern matching function for filenames.  Each occurrence of the *
675223637Sbz * pattern causes a recursion level.
676130610Smlaier */
677223637Sbzstatic  int
678223637Sbzmatch(name, pat, patend, m_not)
679223637Sbz    register Char *name, *pat, *patend;
680223637Sbz    int m_not;
681223637Sbz{
682223637Sbz    int ok, negate_range;
683223637Sbz    Char c, k;
684223637Sbz
685223637Sbz    while (pat < patend) {
686130610Smlaier	c = *pat++;
687145836Smlaier	switch (c & M_MASK) {
688145836Smlaier	case M_ALL:
689240233Sglebius	    if (pat == patend)
690171168Smlaier		return (1);
691145836Smlaier	    do
692171168Smlaier		if (match(name, pat, patend, m_not))
693240233Sglebius		    return (1);
694130610Smlaier	    while (*name++ != EOS);
695223637Sbz	    return (0);
696223637Sbz	case M_ONE:
697171168Smlaier	    if (*name++ == EOS)
698130610Smlaier		return (0);
699240233Sglebius	    break;
700240233Sglebius	case M_SET:
701240233Sglebius	    ok = 0;
702240233Sglebius	    if ((k = *name++) == EOS)
703240233Sglebius		return (0);
704240233Sglebius	    if ((negate_range = ((*pat & M_MASK) == m_not)) != 0)
705130610Smlaier		++pat;
706130610Smlaier	    while (((c = *pat++) & M_MASK) != M_END) {
707130610Smlaier		if ((*pat & M_MASK) == M_RNG) {
708130610Smlaier		    if (globcharcoll(CHAR(c), CHAR(k)) <= 0 &&
709240233Sglebius			globcharcoll(CHAR(k), CHAR(pat[1])) <= 0)
710171168Smlaier			ok = 1;
711130610Smlaier		    pat += 2;
712130610Smlaier		}
713130610Smlaier		else if (c == k)
714130610Smlaier		    ok = 1;
715130610Smlaier	    }
716130610Smlaier	    if (ok == negate_range)
717130610Smlaier		return (0);
718130610Smlaier	    break;
719130610Smlaier	default:
720130610Smlaier	    k = *name++;
721130610Smlaier	    if (samecase(k) != samecase(c))
722130610Smlaier		return (0);
723130610Smlaier	    break;
724130610Smlaier	}
725130610Smlaier    }
726130610Smlaier    return (*name == EOS);
727130610Smlaier}
728171168Smlaier
729171168Smlaier/* free allocated data belonging to a glob_t structure */
730171168Smlaiervoid
731171168Smlaierglobfree(pglob)
732171168Smlaier    glob_t *pglob;
733223637Sbz{
734171168Smlaier    register int i;
735171168Smlaier    register char **pp;
736171168Smlaier
737171168Smlaier    if (pglob->gl_pathv != NULL) {
738171168Smlaier	pp = pglob->gl_pathv + pglob->gl_offs;
739171168Smlaier	for (i = pglob->gl_pathc; i--; ++pp)
740171168Smlaier	    if (*pp)
741171168Smlaier		xfree((ptr_t) *pp), *pp = NULL;
742171168Smlaier	xfree((ptr_t) pglob->gl_pathv), pglob->gl_pathv = NULL;
743171168Smlaier    }
744171168Smlaier}
745171168Smlaier