glob.c revision 100616
159243Sobrien/*
259243Sobrien * Copyright (c) 1989 The Regents of the University of California.
359243Sobrien * All rights reserved.
459243Sobrien *
559243Sobrien * This code is derived from software contributed to Berkeley by
659243Sobrien * Guido van Rossum.
759243Sobrien *
859243Sobrien * Redistribution and use in source and binary forms, with or without
959243Sobrien * modification, are permitted provided that the following conditions
1059243Sobrien * are met:
1159243Sobrien * 1. Redistributions of source code must retain the above copyright
1259243Sobrien *    notice, this list of conditions and the following disclaimer.
1359243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1459243Sobrien *    notice, this list of conditions and the following disclaimer in the
1559243Sobrien *    documentation and/or other materials provided with the distribution.
16100616Smp * 3. Neither the name of the University nor the names of its contributors
1759243Sobrien *    may be used to endorse or promote products derived from this software
1859243Sobrien *    without specific prior written permission.
1959243Sobrien *
2059243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2159243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2259243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2359243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2459243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2559243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2659243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2759243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2859243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2959243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3059243Sobrien * SUCH DAMAGE.
3159243Sobrien */
3259243Sobrien#if defined(LIBC_SCCS) && !defined(lint)
3359243Sobrienstatic char sccsid[] = "@(#)glob.c	5.12 (Berkeley) 6/24/91";
3459243Sobrien#endif /* LIBC_SCCS and not lint */
3559243Sobrien/*
3659243Sobrien * Glob: the interface is a superset of the one defined in POSIX 1003.2,
3759243Sobrien * draft 9.
3859243Sobrien *
3959243Sobrien * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
4059243Sobrien *
4159243Sobrien * Optional extra services, controlled by flags not defined by POSIX:
4259243Sobrien *
4359243Sobrien * GLOB_QUOTE:
4459243Sobrien *	Escaping convention: \ inhibits any special meaning the following
4559243Sobrien *	character might have (except \ at end of string is retained).
4659243Sobrien * GLOB_MAGCHAR:
4759243Sobrien *	Set in gl_flags if pattern contained a globbing character.
4859243Sobrien * GLOB_ALTNOT:
4959243Sobrien *	Use ^ instead of ! for "not".
5059243Sobrien * gl_matchc:
5159243Sobrien *	Number of matches in the current invocation of glob.
5259243Sobrien */
5359243Sobrien
5459243Sobrien#ifdef notdef
5559243Sobrien#include <sys/types.h>
5659243Sobrien#include <sys/param.h>
5759243Sobrien#include <sys/stat.h>
5859243Sobrien#include <dirent.h>
5959243Sobrien#include <ctype.h>
6059243Sobrientypedef void * ptr_t;
6159243Sobrien#endif
6269408Sache#ifdef WINNT_NATIVE
6359243Sobrien	#pragma warning(disable:4244)
6469408Sache#endif /* WINNT_NATIVE */
6559243Sobrien
6659243Sobrien#define Char __Char
6759243Sobrien#include "sh.h"
6859243Sobrien#undef Char
6959243Sobrien#undef QUOTE
7059243Sobrien#undef TILDE
7159243Sobrien#undef META
7259243Sobrien#undef CHAR
7359243Sobrien#undef ismeta
7459243Sobrien#undef Strchr
7559243Sobrien
7659243Sobrien#include "glob.h"
7759243Sobrien
7859243Sobrien#ifndef S_ISDIR
7959243Sobrien#define S_ISDIR(a)	(((a) & S_IFMT) == S_IFDIR)
8059243Sobrien#endif
8159243Sobrien
8259243Sobrien#if !defined(S_ISLNK) && defined(S_IFLNK)
8359243Sobrien#define S_ISLNK(a)	(((a) & S_IFMT) == S_IFLNK)
8459243Sobrien#endif
8559243Sobrien
8659243Sobrien#if !defined(S_ISLNK) && !defined(lstat)
8759243Sobrien#define lstat stat
8859243Sobrien#endif
8959243Sobrien
9059243Sobrientypedef unsigned short Char;
9159243Sobrien
9259243Sobrienstatic	int	 glob1 		__P((Char *, glob_t *, int));
9359243Sobrienstatic	int	 glob2		__P((Char *, Char *, Char *, glob_t *, int));
9459243Sobrienstatic	int	 glob3		__P((Char *, Char *, Char *, Char *,
9559243Sobrien				     glob_t *, int));
9659243Sobrienstatic	int	 globextend	__P((Char *, glob_t *));
9759243Sobrienstatic	int	 match		__P((Char *, Char *, Char *, int));
9859243Sobrien#ifndef __clipper__
9959243Sobrienstatic	int	 compare	__P((const ptr_t, const ptr_t));
10059243Sobrien#endif
10159243Sobrienstatic 	DIR	*Opendir	__P((Char *));
10259243Sobrien#ifdef S_IFLNK
10359243Sobrienstatic	int	 Lstat		__P((Char *, struct stat *));
10459243Sobrien#endif
10559243Sobrienstatic	int	 Stat		__P((Char *, struct stat *sb));
10659243Sobrienstatic 	Char 	*Strchr		__P((Char *, int));
10759243Sobrien#ifdef DEBUG
10859243Sobrienstatic	void	 qprintf	__P((Char *));
10959243Sobrien#endif
11059243Sobrien
11159243Sobrien#define	DOLLAR		'$'
11259243Sobrien#define	DOT		'.'
11359243Sobrien#define	EOS		'\0'
11459243Sobrien#define	LBRACKET	'['
11559243Sobrien#define	NOT		'!'
11659243Sobrien#define ALTNOT		'^'
11759243Sobrien#define	QUESTION	'?'
11859243Sobrien#define	QUOTE		'\\'
11959243Sobrien#define	RANGE		'-'
12059243Sobrien#define	RBRACKET	']'
12159243Sobrien#define	SEP		'/'
12259243Sobrien#define	STAR		'*'
12359243Sobrien#define	TILDE		'~'
12459243Sobrien#define	UNDERSCORE	'_'
12559243Sobrien
12659243Sobrien#define	M_META		0x8000
12759243Sobrien#define M_PROTECT	0x4000
12859243Sobrien#define	M_MASK		0xffff
12959243Sobrien#define	M_ASCII		0x00ff
13059243Sobrien
13159243Sobrien#define	CHAR(c)		((c)&M_ASCII)
13259243Sobrien#define	META(c)		((c)|M_META)
13359243Sobrien#define	M_ALL		META('*')
13459243Sobrien#define	M_END		META(']')
13559243Sobrien#define	M_NOT		META('!')
13659243Sobrien#define	M_ALTNOT	META('^')
13759243Sobrien#define	M_ONE		META('?')
13859243Sobrien#define	M_RNG		META('-')
13959243Sobrien#define	M_SET		META('[')
14059243Sobrien#define	ismeta(c)	(((c)&M_META) != 0)
14159243Sobrien
14259243Sobrien#ifndef BUFSIZE
14359243Sobrien#define GLOBBUFLEN	MAXPATHLEN
14459243Sobrien#else
14559243Sobrien#define GLOBBUFLEN	BUFSIZE
14659243Sobrien#endif
14759243Sobrien
14859243Sobrienint
14959243Sobrienglobcharcoll(c1, c2)
15059243Sobrien    int c1, c2;
15159243Sobrien{
15259243Sobrien#if defined(NLS) && defined(LC_COLLATE) && !defined(NOSTRCOLL)
15359243Sobrien    char s1[2], s2[2];
15459243Sobrien
15559243Sobrien    if (c1 == c2)
15659243Sobrien	return (0);
15759415Sobrien    /*
15859415Sobrien     * From kevin lyda <kevin@suberic.net>:
15959415Sobrien     * strcoll does not guarantee case sorting, so we pre-process now:
16059415Sobrien     */
16159415Sobrien    if (islower(c1) && isupper(c2))
16259415Sobrien	return (1);
16359243Sobrien    s1[0] = c1;
16459243Sobrien    s2[0] = c2;
16559243Sobrien    s1[1] = s2[1] = '\0';
16659243Sobrien    return strcoll(s1, s2);
16759243Sobrien#else
16859243Sobrien    return (c1 - c2);
16959243Sobrien#endif
17059243Sobrien}
17159243Sobrien
17259243Sobrien/*
17359243Sobrien * Need to dodge two kernel bugs:
17459243Sobrien * opendir("") != opendir(".")
17559243Sobrien * NAMEI_BUG: on plain files trailing slashes are ignored in some kernels.
17659243Sobrien *            POSIX specifies that they should be ignored in directories.
17759243Sobrien */
17859243Sobrien
17959243Sobrienstatic DIR *
18059243SobrienOpendir(str)
18159243Sobrien    register Char *str;
18259243Sobrien{
18359243Sobrien    char    buf[GLOBBUFLEN];
18459243Sobrien    register char *dc = buf;
18559243Sobrien#if defined(hpux) || defined(__hpux)
18659243Sobrien    struct stat st;
18759243Sobrien#endif
18859243Sobrien
18959243Sobrien    if (!*str)
19059243Sobrien	return (opendir("."));
19159243Sobrien    while ((*dc++ = *str++) != '\0')
19259243Sobrien	continue;
19359243Sobrien#if defined(hpux) || defined(__hpux)
19459243Sobrien    /*
19559243Sobrien     * Opendir on some device files hangs, so avoid it
19659243Sobrien     */
19759243Sobrien    if (stat(buf, &st) == -1 || !S_ISDIR(st.st_mode))
19859243Sobrien	return NULL;
19959243Sobrien#endif
20059243Sobrien    return (opendir(buf));
20159243Sobrien}
20259243Sobrien
20359243Sobrien#ifdef S_IFLNK
20459243Sobrienstatic int
20559243SobrienLstat(fn, sb)
20659243Sobrien    register Char *fn;
20759243Sobrien    struct stat *sb;
20859243Sobrien{
20959243Sobrien    char    buf[GLOBBUFLEN];
21059243Sobrien    register char *dc = buf;
21159243Sobrien
21259243Sobrien    while ((*dc++ = *fn++) != '\0')
21359243Sobrien	continue;
21459243Sobrien# ifdef NAMEI_BUG
21559243Sobrien    {
21659243Sobrien	int     st;
21759243Sobrien
21859243Sobrien	st = lstat(buf, sb);
21959243Sobrien	if (*buf)
22059243Sobrien	    dc--;
22159243Sobrien	return (*--dc == '/' && !S_ISDIR(sb->st_mode) ? -1 : st);
22259243Sobrien    }
22359243Sobrien# else
22459243Sobrien    return (lstat(buf, sb));
22559243Sobrien# endif	/* NAMEI_BUG */
22659243Sobrien}
22759243Sobrien#else
22859243Sobrien#define Lstat Stat
22959243Sobrien#endif /* S_IFLNK */
23059243Sobrien
23159243Sobrienstatic int
23259243SobrienStat(fn, sb)
23359243Sobrien    register Char *fn;
23459243Sobrien    struct stat *sb;
23559243Sobrien{
23659243Sobrien    char    buf[GLOBBUFLEN];
23759243Sobrien    register char *dc = buf;
23859243Sobrien
23959243Sobrien    while ((*dc++ = *fn++) != '\0')
24059243Sobrien	continue;
24159243Sobrien#ifdef NAMEI_BUG
24259243Sobrien    {
24359243Sobrien	int     st;
24459243Sobrien
24559243Sobrien	st = stat(buf, sb);
24659243Sobrien	if (*buf)
24759243Sobrien	    dc--;
24859243Sobrien	return (*--dc == '/' && !S_ISDIR(sb->st_mode) ? -1 : st);
24959243Sobrien    }
25059243Sobrien#else
25159243Sobrien    return (stat(buf, sb));
25259243Sobrien#endif /* NAMEI_BUG */
25359243Sobrien}
25459243Sobrien
25559243Sobrienstatic Char *
25659243SobrienStrchr(str, ch)
25759243Sobrien    Char *str;
25859243Sobrien    int ch;
25959243Sobrien{
26059243Sobrien    do
26159243Sobrien	if (*str == ch)
26259243Sobrien	    return (str);
26359243Sobrien    while (*str++);
26459243Sobrien    return (NULL);
26559243Sobrien}
26659243Sobrien
26759243Sobrien#ifdef DEBUG
26859243Sobrienstatic void
26959243Sobrienqprintf(s)
27059243SobrienChar *s;
27159243Sobrien{
27259243Sobrien    Char *p;
27359243Sobrien
27459243Sobrien    for (p = s; *p; p++)
27559243Sobrien	printf("%c", *p & 0xff);
27659243Sobrien    printf("\n");
27759243Sobrien    for (p = s; *p; p++)
27859243Sobrien	printf("%c", *p & M_PROTECT ? '"' : ' ');
27959243Sobrien    printf("\n");
28059243Sobrien    for (p = s; *p; p++)
28159243Sobrien	printf("%c", *p & M_META ? '_' : ' ');
28259243Sobrien    printf("\n");
28359243Sobrien}
28459243Sobrien#endif /* DEBUG */
28559243Sobrien
28659243Sobrienstatic int
28759243Sobriencompare(p, q)
28859243Sobrien    const ptr_t  p, q;
28959243Sobrien{
29059243Sobrien#if defined(NLS) && !defined(NOSTRCOLL)
29159243Sobrien    errno = 0;  /* strcoll sets errno, another brain-damage */
29259243Sobrien
29359243Sobrien    return (strcoll(*(char **) p, *(char **) q));
29459243Sobrien#else
29559243Sobrien    return (strcmp(*(char **) p, *(char **) q));
29659243Sobrien#endif /* NLS && !NOSTRCOLL */
29759243Sobrien}
29859243Sobrien
29959243Sobrien/*
30059243Sobrien * The main glob() routine: compiles the pattern (optionally processing
30159243Sobrien * quotes), calls glob1() to do the real pattern matching, and finally
30259243Sobrien * sorts the list (unless unsorted operation is requested).  Returns 0
30359243Sobrien * if things went well, nonzero if errors occurred.  It is not an error
30459243Sobrien * to find no matches.
30559243Sobrien */
30659243Sobrienint
30759243Sobrienglob(pattern, flags, errfunc, pglob)
30859243Sobrien    const char *pattern;
30959243Sobrien    int     flags;
31059243Sobrien    int     (*errfunc) __P((const char *, int));
31159243Sobrien    glob_t *pglob;
31259243Sobrien{
31359243Sobrien    int     err, oldpathc;
31459243Sobrien    Char *bufnext, *bufend, *compilebuf, m_not;
31559243Sobrien    const unsigned char *compilepat, *patnext;
31659243Sobrien    int     c, not;
31759243Sobrien    Char patbuf[GLOBBUFLEN + 1], *qpatnext;
31859243Sobrien    int     no_match;
31959243Sobrien
32059243Sobrien    patnext = (unsigned char *) pattern;
32159243Sobrien    if (!(flags & GLOB_APPEND)) {
32259243Sobrien	pglob->gl_pathc = 0;
32359243Sobrien	pglob->gl_pathv = NULL;
32459243Sobrien	if (!(flags & GLOB_DOOFFS))
32559243Sobrien	    pglob->gl_offs = 0;
32659243Sobrien    }
32759243Sobrien    pglob->gl_flags = flags & ~GLOB_MAGCHAR;
32859243Sobrien    pglob->gl_errfunc = errfunc;
32959243Sobrien    oldpathc = pglob->gl_pathc;
33059243Sobrien    pglob->gl_matchc = 0;
33159243Sobrien
33259243Sobrien    if (pglob->gl_flags & GLOB_ALTNOT) {
33359243Sobrien	not = ALTNOT;
33459243Sobrien	m_not = M_ALTNOT;
33559243Sobrien    }
33659243Sobrien    else {
33759243Sobrien	not = NOT;
33859243Sobrien	m_not = M_NOT;
33959243Sobrien    }
34059243Sobrien
34159243Sobrien    bufnext = patbuf;
34259243Sobrien    bufend = bufnext + GLOBBUFLEN;
34359243Sobrien    compilebuf = bufnext;
34459243Sobrien    compilepat = patnext;
34559243Sobrien
34659243Sobrien    no_match = *patnext == not;
34759243Sobrien    if (no_match)
34859243Sobrien	patnext++;
34959243Sobrien
35059243Sobrien    if (flags & GLOB_QUOTE) {
35159243Sobrien	/* Protect the quoted characters */
35259243Sobrien	while (bufnext < bufend && (c = *patnext++) != EOS)
35359415Sobrien#ifdef DSPMBYTE
35459415Sobrien	    if (Ismbyte1(c) && *patnext != EOS)
35559415Sobrien	    {
35659415Sobrien	      *bufnext++ = (Char) c;
35759415Sobrien	      *bufnext++ = (Char) *patnext++;
35859415Sobrien	    }
35959415Sobrien	    else
36059415Sobrien#endif /* DSPMBYTE */
36159243Sobrien	    if (c == QUOTE) {
36259243Sobrien		if ((c = *patnext++) == EOS) {
36359243Sobrien		    c = QUOTE;
36459243Sobrien		    --patnext;
36559243Sobrien		}
36659243Sobrien		*bufnext++ = (Char) (c | M_PROTECT);
36759243Sobrien	    }
36859243Sobrien	    else
36959243Sobrien		*bufnext++ = (Char) c;
37059243Sobrien    }
37159243Sobrien    else
37259243Sobrien	while (bufnext < bufend && (c = *patnext++) != EOS)
37359243Sobrien	    *bufnext++ = (Char) c;
37459243Sobrien    *bufnext = EOS;
37559243Sobrien
37659243Sobrien    bufnext = patbuf;
37759243Sobrien    qpatnext = patbuf;
37859243Sobrien    /* we don't need to check for buffer overflow any more */
37959243Sobrien    while ((c = *qpatnext++) != EOS) {
38059415Sobrien#ifdef DSPMBYTE
38159415Sobrien	if (Ismbyte1(c) && *qpatnext != EOS)
38259415Sobrien	{
38359415Sobrien	  *bufnext++ = CHAR(c);
38459415Sobrien	  *bufnext++ = CHAR(*qpatnext++);
38559415Sobrien	}
38659415Sobrien	else
38759415Sobrien#endif /* DSPMBYTE */
38859243Sobrien	switch (c) {
38959243Sobrien	case LBRACKET:
39059243Sobrien	    c = *qpatnext;
39159243Sobrien	    if (c == not)
39259243Sobrien		++qpatnext;
39359243Sobrien	    if (*qpatnext == EOS ||
39459243Sobrien		Strchr(qpatnext + 1, RBRACKET) == NULL) {
39559243Sobrien		*bufnext++ = LBRACKET;
39659243Sobrien		if (c == not)
39759243Sobrien		    --qpatnext;
39859243Sobrien		break;
39959243Sobrien	    }
40059243Sobrien	    pglob->gl_flags |= GLOB_MAGCHAR;
40159243Sobrien	    *bufnext++ = M_SET;
40259243Sobrien	    if (c == not)
40359243Sobrien		*bufnext++ = m_not;
40459243Sobrien	    c = *qpatnext++;
40559243Sobrien	    do {
40659243Sobrien		*bufnext++ = CHAR(c);
40759243Sobrien		if (*qpatnext == RANGE &&
40859243Sobrien		    (c = qpatnext[1]) != RBRACKET) {
40959243Sobrien		    *bufnext++ = M_RNG;
41059243Sobrien		    *bufnext++ = CHAR(c);
41159243Sobrien		    qpatnext += 2;
41259243Sobrien		}
41359243Sobrien	    } while ((c = *qpatnext++) != RBRACKET);
41459243Sobrien	    *bufnext++ = M_END;
41559243Sobrien	    break;
41659243Sobrien	case QUESTION:
41759243Sobrien	    pglob->gl_flags |= GLOB_MAGCHAR;
41859243Sobrien	    *bufnext++ = M_ONE;
41959243Sobrien	    break;
42059243Sobrien	case STAR:
42159243Sobrien	    pglob->gl_flags |= GLOB_MAGCHAR;
42259243Sobrien	    /* collapse adjacent stars to one, to avoid
42359243Sobrien	     * exponential behavior
42459243Sobrien	     */
42559243Sobrien	    if (bufnext == patbuf || bufnext[-1] != M_ALL)
42659243Sobrien		*bufnext++ = M_ALL;
42759243Sobrien	    break;
42859243Sobrien	default:
42959243Sobrien	    *bufnext++ = CHAR(c);
43059243Sobrien	    break;
43159243Sobrien	}
43259243Sobrien    }
43359243Sobrien    *bufnext = EOS;
43459243Sobrien#ifdef DEBUG
43559243Sobrien    qprintf(patbuf);
43659243Sobrien#endif
43759243Sobrien
43859243Sobrien    if ((err = glob1(patbuf, pglob, no_match)) != 0)
43959243Sobrien	return (err);
44059243Sobrien
44159243Sobrien    /*
44259243Sobrien     * If there was no match we are going to append the pattern
44359243Sobrien     * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
44459243Sobrien     * and the pattern did not contain any magic characters
44559243Sobrien     * GLOB_NOMAGIC is there just for compatibility with csh.
44659243Sobrien     */
44759243Sobrien    if (pglob->gl_pathc == oldpathc &&
44859243Sobrien	((flags & GLOB_NOCHECK) ||
44959243Sobrien	 ((flags & GLOB_NOMAGIC) && !(pglob->gl_flags & GLOB_MAGCHAR)))) {
45059243Sobrien	if (!(flags & GLOB_QUOTE)) {
45159243Sobrien	    Char *dp = compilebuf;
45259243Sobrien	    const unsigned char *sp = compilepat;
45359243Sobrien
45459243Sobrien	    while ((*dp++ = *sp++) != '\0')
45559243Sobrien		continue;
45659243Sobrien	}
45759243Sobrien	else {
45859243Sobrien	    /*
45959243Sobrien	     * copy pattern, interpreting quotes; this is slightly different
46059243Sobrien	     * than the interpretation of quotes above -- which should prevail?
46159243Sobrien	     */
46259243Sobrien	    while (*compilepat != EOS) {
46359243Sobrien		if (*compilepat == QUOTE) {
46459243Sobrien		    if (*++compilepat == EOS)
46559243Sobrien			--compilepat;
46659243Sobrien		}
46759243Sobrien		*compilebuf++ = (unsigned char) *compilepat++;
46859243Sobrien	    }
46959243Sobrien	    *compilebuf = EOS;
47059243Sobrien	}
47159243Sobrien	return (globextend(patbuf, pglob));
47259243Sobrien    }
47359415Sobrien    else if (!(flags & GLOB_NOSORT) && (pglob->gl_pathc != oldpathc))
47459243Sobrien	qsort((char *) (pglob->gl_pathv + pglob->gl_offs + oldpathc),
47559243Sobrien	      pglob->gl_pathc - oldpathc, sizeof(char *),
47659243Sobrien	      (int (*) __P((const void *, const void *))) compare);
47759243Sobrien    return (0);
47859243Sobrien}
47959243Sobrien
48059243Sobrienstatic int
48159243Sobrienglob1(pattern, pglob, no_match)
48259243Sobrien    Char *pattern;
48359243Sobrien    glob_t *pglob;
48459243Sobrien    int     no_match;
48559243Sobrien{
48659243Sobrien    Char pathbuf[GLOBBUFLEN + 1];
48759243Sobrien
48859243Sobrien    /*
48959243Sobrien     * a null pathname is invalid -- POSIX 1003.1 sect. 2.4.
49059243Sobrien     */
49159243Sobrien    if (*pattern == EOS)
49259243Sobrien	return (0);
49359243Sobrien    return (glob2(pathbuf, pathbuf, pattern, pglob, no_match));
49459243Sobrien}
49559243Sobrien
49659243Sobrien/*
49759243Sobrien * functions glob2 and glob3 are mutually recursive; there is one level
49859243Sobrien * of recursion for each segment in the pattern that contains one or
49959243Sobrien * more meta characters.
50059243Sobrien */
50159243Sobrienstatic int
50259243Sobrienglob2(pathbuf, pathend, pattern, pglob, no_match)
50359243Sobrien    Char *pathbuf, *pathend, *pattern;
50459243Sobrien    glob_t *pglob;
50559243Sobrien    int     no_match;
50659243Sobrien{
50759243Sobrien    struct stat sbuf;
50859243Sobrien    int anymeta;
50959243Sobrien    Char *p, *q;
51059243Sobrien
51159243Sobrien    /*
51259243Sobrien     * loop over pattern segments until end of pattern or until segment with
51359243Sobrien     * meta character found.
51459243Sobrien     */
51559243Sobrien    anymeta = 0;
51659243Sobrien    for (;;) {
51759243Sobrien	if (*pattern == EOS) {	/* end of pattern? */
51859243Sobrien	    *pathend = EOS;
51959243Sobrien
52059243Sobrien	    if (Lstat(pathbuf, &sbuf))
52159243Sobrien		return (0);
52259243Sobrien
52359243Sobrien	    if (((pglob->gl_flags & GLOB_MARK) &&
52459243Sobrien		 pathend[-1] != SEP) &&
52559243Sobrien		(S_ISDIR(sbuf.st_mode)
52659243Sobrien#ifdef S_IFLNK
52759243Sobrien		 || (S_ISLNK(sbuf.st_mode) &&
52859243Sobrien		     (Stat(pathbuf, &sbuf) == 0) &&
52959243Sobrien		     S_ISDIR(sbuf.st_mode))
53059243Sobrien#endif
53159243Sobrien		 )) {
53259243Sobrien		*pathend++ = SEP;
53359243Sobrien		*pathend = EOS;
53459243Sobrien	    }
53559243Sobrien	    ++pglob->gl_matchc;
53659243Sobrien	    return (globextend(pathbuf, pglob));
53759243Sobrien	}
53859243Sobrien
53959243Sobrien	/* find end of next segment, copy tentatively to pathend */
54059243Sobrien	q = pathend;
54159243Sobrien	p = pattern;
54259243Sobrien	while (*p != EOS && *p != SEP) {
54359243Sobrien	    if (ismeta(*p))
54459243Sobrien		anymeta = 1;
54559243Sobrien	    *q++ = *p++;
54659243Sobrien	}
54759243Sobrien
54859243Sobrien	if (!anymeta) {		/* no expansion, do next segment */
54959243Sobrien	    pathend = q;
55059243Sobrien	    pattern = p;
55159243Sobrien	    while (*pattern == SEP)
55259243Sobrien		*pathend++ = *pattern++;
55359243Sobrien	}
55459243Sobrien	else			/* need expansion, recurse */
55559243Sobrien	    return (glob3(pathbuf, pathend, pattern, p, pglob, no_match));
55659243Sobrien    }
55759243Sobrien    /* NOTREACHED */
55859243Sobrien}
55959243Sobrien
56059243Sobrien
56159243Sobrienstatic int
56259243Sobrienglob3(pathbuf, pathend, pattern, restpattern, pglob, no_match)
56359243Sobrien    Char *pathbuf, *pathend, *pattern, *restpattern;
56459243Sobrien    glob_t *pglob;
56559243Sobrien    int     no_match;
56659243Sobrien{
56759243Sobrien    DIR    *dirp;
56859243Sobrien    struct dirent *dp;
56959243Sobrien    int     err;
57059243Sobrien    Char m_not = (pglob->gl_flags & GLOB_ALTNOT) ? M_ALTNOT : M_NOT;
57159243Sobrien    char cpathbuf[GLOBBUFLEN], *ptr;;
57259243Sobrien
57359243Sobrien    *pathend = EOS;
57459243Sobrien    errno = 0;
57559243Sobrien
57659243Sobrien    if (!(dirp = Opendir(pathbuf))) {
57759243Sobrien	/* todo: don't call for ENOENT or ENOTDIR? */
57859243Sobrien	for (ptr = cpathbuf; (*ptr++ = (char) *pathbuf++) != EOS;)
57959243Sobrien	    continue;
58059243Sobrien	if ((pglob->gl_errfunc && (*pglob->gl_errfunc) (cpathbuf, errno)) ||
58159243Sobrien	    (pglob->gl_flags & GLOB_ERR))
58259243Sobrien	    return (GLOB_ABEND);
58359243Sobrien	else
58459243Sobrien	    return (0);
58559243Sobrien    }
58659243Sobrien
58759243Sobrien    err = 0;
58859243Sobrien
58959243Sobrien    /* search directory for matching names */
59059243Sobrien    while ((dp = readdir(dirp)) != NULL) {
59159243Sobrien	register unsigned char *sc;
59259243Sobrien	register Char *dc;
59359243Sobrien
59459243Sobrien	/* initial DOT must be matched literally */
59559243Sobrien	if (dp->d_name[0] == DOT && *pattern != DOT)
59659243Sobrien	    continue;
59759243Sobrien	for (sc = (unsigned char *) dp->d_name, dc = pathend;
59859243Sobrien	     (*dc++ = *sc++) != '\0';)
59959243Sobrien	    continue;
60059243Sobrien	if (match(pathend, pattern, restpattern, (int) m_not) == no_match) {
60159243Sobrien	    *pathend = EOS;
60259243Sobrien	    continue;
60359243Sobrien	}
60459243Sobrien	err = glob2(pathbuf, --dc, restpattern, pglob, no_match);
60559243Sobrien	if (err)
60659243Sobrien	    break;
60759243Sobrien    }
60859243Sobrien    /* todo: check error from readdir? */
60959243Sobrien    (void) closedir(dirp);
61059243Sobrien    return (err);
61159243Sobrien}
61259243Sobrien
61359243Sobrien
61459243Sobrien/*
61559243Sobrien * Extend the gl_pathv member of a glob_t structure to accomodate a new item,
61659243Sobrien * add the new item, and update gl_pathc.
61759243Sobrien *
61859243Sobrien * This assumes the BSD realloc, which only copies the block when its size
61959243Sobrien * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
62059243Sobrien * behavior.
62159243Sobrien *
62259243Sobrien * Return 0 if new item added, error code if memory couldn't be allocated.
62359243Sobrien *
62459243Sobrien * Invariant of the glob_t structure:
62559243Sobrien *	Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
62659243Sobrien *	 gl_pathv points to (gl_offs + gl_pathc + 1) items.
62759243Sobrien */
62859243Sobrienstatic int
62959243Sobrienglobextend(path, pglob)
63059243Sobrien    Char *path;
63159243Sobrien    glob_t *pglob;
63259243Sobrien{
63359243Sobrien    register char **pathv;
63459243Sobrien    register int i;
63559243Sobrien    unsigned int newsize;
63659243Sobrien    char   *copy;
63759243Sobrien    Char *p;
63859243Sobrien
63959243Sobrien    newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
64059243Sobrien    pathv = (char **) (pglob->gl_pathv ?
64159243Sobrien		       xrealloc((ptr_t) pglob->gl_pathv, (size_t) newsize) :
64259243Sobrien		       xmalloc((size_t) newsize));
64359243Sobrien    if (pathv == NULL)
64459243Sobrien	return (GLOB_NOSPACE);
64559243Sobrien
64659243Sobrien    if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
64759243Sobrien	/* first time around -- clear initial gl_offs items */
64859243Sobrien	pathv += pglob->gl_offs;
64959243Sobrien	for (i = pglob->gl_offs; --i >= 0;)
65059243Sobrien	    *--pathv = NULL;
65159243Sobrien    }
65259243Sobrien    pglob->gl_pathv = pathv;
65359243Sobrien
65459243Sobrien    for (p = path; *p++;)
65559243Sobrien	continue;
65659243Sobrien    if ((copy = (char *) xmalloc((size_t) (p - path))) != NULL) {
65759243Sobrien	register char *dc = copy;
65859243Sobrien	register Char *sc = path;
65959243Sobrien
66059243Sobrien	while ((*dc++ = *sc++) != '\0')
66159243Sobrien	    continue;
66259243Sobrien	pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
66359243Sobrien    }
66459243Sobrien    pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
66559243Sobrien    return ((copy == NULL) ? GLOB_NOSPACE : 0);
66659243Sobrien}
66759243Sobrien
66859243Sobrien
66959243Sobrien/*
67059243Sobrien * pattern matching function for filenames.  Each occurrence of the *
67159243Sobrien * pattern causes a recursion level.
67259243Sobrien */
67359243Sobrienstatic  int
67459243Sobrienmatch(name, pat, patend, m_not)
67559243Sobrien    register Char *name, *pat, *patend;
67659243Sobrien    int m_not;
67759243Sobrien{
67859243Sobrien    int ok, negate_range;
67959243Sobrien    Char c, k;
68059243Sobrien
68159243Sobrien    while (pat < patend) {
68259243Sobrien	c = *pat++;
68359243Sobrien	switch (c & M_MASK) {
68459243Sobrien	case M_ALL:
68559243Sobrien	    if (pat == patend)
68659243Sobrien		return (1);
68759243Sobrien	    do
68859243Sobrien		if (match(name, pat, patend, m_not))
68959243Sobrien		    return (1);
69059243Sobrien	    while (*name++ != EOS);
69159243Sobrien	    return (0);
69259243Sobrien	case M_ONE:
69359243Sobrien	    if (*name++ == EOS)
69459243Sobrien		return (0);
69559243Sobrien	    break;
69659243Sobrien	case M_SET:
69759243Sobrien	    ok = 0;
69859243Sobrien	    if ((k = *name++) == EOS)
69959243Sobrien		return (0);
70059243Sobrien	    if ((negate_range = ((*pat & M_MASK) == m_not)) != 0)
70159243Sobrien		++pat;
70259243Sobrien	    while (((c = *pat++) & M_MASK) != M_END) {
70359243Sobrien		if ((*pat & M_MASK) == M_RNG) {
70459243Sobrien		    if (globcharcoll(CHAR(c), CHAR(k)) <= 0 &&
70559243Sobrien			globcharcoll(CHAR(k), CHAR(pat[1])) <= 0)
70659243Sobrien			ok = 1;
70759243Sobrien		    pat += 2;
70859243Sobrien		}
70959243Sobrien		else if (c == k)
71059243Sobrien		    ok = 1;
71159243Sobrien	    }
71259243Sobrien	    if (ok == negate_range)
71359243Sobrien		return (0);
71459243Sobrien	    break;
71559243Sobrien	default:
71659243Sobrien	    k = *name++;
71759243Sobrien	    if (samecase(k) != samecase(c))
71859243Sobrien		return (0);
71959243Sobrien	    break;
72059243Sobrien	}
72159243Sobrien    }
72259243Sobrien    return (*name == EOS);
72359243Sobrien}
72459243Sobrien
72559243Sobrien/* free allocated data belonging to a glob_t structure */
72659243Sobrienvoid
72759243Sobrienglobfree(pglob)
72859243Sobrien    glob_t *pglob;
72959243Sobrien{
73059243Sobrien    register int i;
73159243Sobrien    register char **pp;
73259243Sobrien
73359243Sobrien    if (pglob->gl_pathv != NULL) {
73459243Sobrien	pp = pglob->gl_pathv + pglob->gl_offs;
73559243Sobrien	for (i = pglob->gl_pathc; i--; ++pp)
73659243Sobrien	    if (*pp)
73759243Sobrien		xfree((ptr_t) *pp), *pp = NULL;
73859243Sobrien	xfree((ptr_t) pglob->gl_pathv), pglob->gl_pathv = NULL;
73959243Sobrien    }
74059243Sobrien}
741