source: trunk/third/tcsh/glob.c @ 12039

Revision 12039, 16.9 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12038, which included commits to RCS files with non-trunk default branches.
Line 
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, 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#if defined(LIBC_SCCS) && !defined(lint)
37static char sccsid[] = "@(#)glob.c      5.12 (Berkeley) 6/24/91";
38#endif /* LIBC_SCCS and not lint */
39/*
40 * Glob: the interface is a superset of the one defined in POSIX 1003.2,
41 * draft 9.
42 *
43 * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
44 *
45 * Optional extra services, controlled by flags not defined by POSIX:
46 *
47 * GLOB_QUOTE:
48 *      Escaping convention: \ inhibits any special meaning the following
49 *      character might have (except \ at end of string is retained).
50 * GLOB_MAGCHAR:
51 *      Set in gl_flags if pattern contained a globbing character.
52 * GLOB_ALTNOT:
53 *      Use ^ instead of ! for "not".
54 * gl_matchc:
55 *      Number of matches in the current invocation of glob.
56 */
57
58#ifdef notdef
59#include <sys/types.h>
60#include <sys/param.h>
61#include <sys/stat.h>
62#include <dirent.h>
63#include <ctype.h>
64typedef void * ptr_t;
65#endif
66#ifdef WINNT
67        #pragma warning(disable:4244)
68#endif /* WINNT */
69
70#define Char __Char
71#include "sh.h"
72#undef Char
73#undef QUOTE
74#undef TILDE
75#undef META
76#undef CHAR
77#undef ismeta
78#undef Strchr
79
80#include "glob.h"
81
82#ifndef S_ISDIR
83#define S_ISDIR(a)      (((a) & S_IFMT) == S_IFDIR)
84#endif
85
86#if !defined(S_ISLNK) && defined(S_IFLNK)
87#define S_ISLNK(a)      (((a) & S_IFMT) == S_IFLNK)
88#endif
89
90#if !defined(S_ISLNK) && !defined(lstat)
91#define lstat stat
92#endif
93
94typedef unsigned short Char;
95
96static  int      glob1          __P((Char *, glob_t *, int));
97static  int      glob2          __P((Char *, Char *, Char *, glob_t *, int));
98static  int      glob3          __P((Char *, Char *, Char *, Char *,
99                                     glob_t *, int));
100static  int      globextend     __P((Char *, glob_t *));
101static  int      match          __P((Char *, Char *, Char *, int));
102#ifndef __clipper__
103static  int      compare        __P((const ptr_t, const ptr_t));
104#endif
105static  DIR     *Opendir        __P((Char *));
106#ifdef S_IFLNK
107static  int      Lstat          __P((Char *, struct stat *));
108#endif
109static  int      Stat           __P((Char *, struct stat *sb));
110static  Char    *Strchr         __P((Char *, int));
111#ifdef DEBUG
112static  void     qprintf        __P((Char *));
113#endif
114
115#define DOLLAR          '$'
116#define DOT             '.'
117#define EOS             '\0'
118#define LBRACKET        '['
119#define NOT             '!'
120#define ALTNOT          '^'
121#define QUESTION        '?'
122#define QUOTE           '\\'
123#define RANGE           '-'
124#define RBRACKET        ']'
125#define SEP             '/'
126#define STAR            '*'
127#define TILDE           '~'
128#define UNDERSCORE      '_'
129
130#define M_META          0x8000
131#define M_PROTECT       0x4000
132#define M_MASK          0xffff
133#define M_ASCII         0x00ff
134
135#define CHAR(c)         ((c)&M_ASCII)
136#define META(c)         ((c)|M_META)
137#define M_ALL           META('*')
138#define M_END           META(']')
139#define M_NOT           META('!')
140#define M_ALTNOT        META('^')
141#define M_ONE           META('?')
142#define M_RNG           META('-')
143#define M_SET           META('[')
144#define ismeta(c)       (((c)&M_META) != 0)
145
146int
147globcharcoll(c1, c2)
148    int c1, c2;
149{
150#if defined(NLS) && defined(LC_COLLATE) && !defined(NOSTRCOLL)
151    char s1[2], s2[2];
152
153    if (c1 == c2)
154        return (0);
155    s1[0] = c1;
156    s2[0] = c2;
157    s1[1] = s2[1] = '\0';
158    return strcoll(s1, s2);
159#else
160    return (c1 - c2);
161#endif
162}
163
164/*
165 * Need to dodge two kernel bugs:
166 * opendir("") != opendir(".")
167 * NAMEI_BUG: on plain files trailing slashes are ignored in some kernels.
168 *            POSIX specifies that they should be ignored in directories.
169 */
170
171static DIR *
172Opendir(str)
173    register Char *str;
174{
175    char    buf[MAXPATHLEN];
176    register char *dc = buf;
177
178    if (!*str)
179        return (opendir("."));
180    while ((*dc++ = *str++) != '\0')
181        continue;
182    return (opendir(buf));
183}
184
185#ifdef S_IFLNK
186static int
187Lstat(fn, sb)
188    register Char *fn;
189    struct stat *sb;
190{
191    char    buf[MAXPATHLEN];
192    register char *dc = buf;
193
194    while ((*dc++ = *fn++) != '\0')
195        continue;
196# ifdef NAMEI_BUG
197    {
198        int     st;
199
200        st = lstat(buf, sb);
201        if (*buf)
202            dc--;
203        return (*--dc == '/' && !S_ISDIR(sb->st_mode) ? -1 : st);
204    }
205# else
206    return (lstat(buf, sb));
207# endif /* NAMEI_BUG */
208}
209#else
210#define Lstat Stat
211#endif /* S_IFLNK */
212
213static int
214Stat(fn, sb)
215    register Char *fn;
216    struct stat *sb;
217{
218    char    buf[MAXPATHLEN];
219    register char *dc = buf;
220
221    while ((*dc++ = *fn++) != '\0')
222        continue;
223#ifdef NAMEI_BUG
224    {
225        int     st;
226
227        st = stat(buf, sb);
228        if (*buf)
229            dc--;
230        return (*--dc == '/' && !S_ISDIR(sb->st_mode) ? -1 : st);
231    }
232#else
233    return (stat(buf, sb));
234#endif /* NAMEI_BUG */
235}
236
237static Char *
238Strchr(str, ch)
239    Char *str;
240    int ch;
241{
242    do
243        if (*str == ch)
244            return (str);
245    while (*str++);
246    return (NULL);
247}
248
249#ifdef DEBUG
250static void
251qprintf(s)
252Char *s;
253{
254    Char *p;
255
256    for (p = s; *p; p++)
257        printf("%c", *p & 0xff);
258    printf("\n");
259    for (p = s; *p; p++)
260        printf("%c", *p & M_PROTECT ? '"' : ' ');
261    printf("\n");
262    for (p = s; *p; p++)
263        printf("%c", *p & M_META ? '_' : ' ');
264    printf("\n");
265}
266#endif /* DEBUG */
267
268static int
269compare(p, q)
270    const ptr_t  p, q;
271{
272#if defined(NLS) && !defined(NOSTRCOLL)
273    errno = 0;  /* strcoll sets errno, another brain-damage */
274 
275    return (strcoll(*(char **) p, *(char **) q));
276#else
277    return (strcmp(*(char **) p, *(char **) q));
278#endif /* NLS && !NOSTRCOLL */
279}
280
281/*
282 * The main glob() routine: compiles the pattern (optionally processing
283 * quotes), calls glob1() to do the real pattern matching, and finally
284 * sorts the list (unless unsorted operation is requested).  Returns 0
285 * if things went well, nonzero if errors occurred.  It is not an error
286 * to find no matches.
287 */
288int
289glob(pattern, flags, errfunc, pglob)
290    const char *pattern;
291    int     flags;
292    int     (*errfunc) __P((const char *, int));
293    glob_t *pglob;
294{
295    int     err, oldpathc;
296    Char *bufnext, *bufend, *compilebuf, m_not;
297    const unsigned char *compilepat, *patnext;
298    int     c, not;
299    Char patbuf[MAXPATHLEN + 1], *qpatnext;
300    int     no_match;
301
302    patnext = (unsigned char *) pattern;
303    if (!(flags & GLOB_APPEND)) {
304        pglob->gl_pathc = 0;
305        pglob->gl_pathv = NULL;
306        if (!(flags & GLOB_DOOFFS))
307            pglob->gl_offs = 0;
308    }
309    pglob->gl_flags = flags & ~GLOB_MAGCHAR;
310    pglob->gl_errfunc = errfunc;
311    oldpathc = pglob->gl_pathc;
312    pglob->gl_matchc = 0;
313
314    if (pglob->gl_flags & GLOB_ALTNOT) {
315        not = ALTNOT;
316        m_not = M_ALTNOT;
317    }
318    else {
319        not = NOT;
320        m_not = M_NOT;
321    }
322
323    bufnext = patbuf;
324    bufend = bufnext + MAXPATHLEN;
325    compilebuf = bufnext;
326    compilepat = patnext;
327
328    no_match = *patnext == not;
329    if (no_match)
330        patnext++;
331
332    if (flags & GLOB_QUOTE) {
333        /* Protect the quoted characters */
334        while (bufnext < bufend && (c = *patnext++) != EOS)
335            if (c == QUOTE) {
336                if ((c = *patnext++) == EOS) {
337                    c = QUOTE;
338                    --patnext;
339                }
340                *bufnext++ = (Char) (c | M_PROTECT);
341            }
342            else
343                *bufnext++ = (Char) c;
344    }
345    else
346        while (bufnext < bufend && (c = *patnext++) != EOS)
347            *bufnext++ = (Char) c;
348    *bufnext = EOS;
349
350    bufnext = patbuf;
351    qpatnext = patbuf;
352    /* we don't need to check for buffer overflow any more */
353    while ((c = *qpatnext++) != EOS) {
354        switch (c) {
355        case LBRACKET:
356            c = *qpatnext;
357            if (c == not)
358                ++qpatnext;
359            if (*qpatnext == EOS ||
360                Strchr(qpatnext + 1, RBRACKET) == NULL) {
361                *bufnext++ = LBRACKET;
362                if (c == not)
363                    --qpatnext;
364                break;
365            }
366            pglob->gl_flags |= GLOB_MAGCHAR;
367            *bufnext++ = M_SET;
368            if (c == not)
369                *bufnext++ = m_not;
370            c = *qpatnext++;
371            do {
372                *bufnext++ = CHAR(c);
373                if (*qpatnext == RANGE &&
374                    (c = qpatnext[1]) != RBRACKET) {
375                    *bufnext++ = M_RNG;
376                    *bufnext++ = CHAR(c);
377                    qpatnext += 2;
378                }
379            } while ((c = *qpatnext++) != RBRACKET);
380            *bufnext++ = M_END;
381            break;
382        case QUESTION:
383            pglob->gl_flags |= GLOB_MAGCHAR;
384            *bufnext++ = M_ONE;
385            break;
386        case STAR:
387            pglob->gl_flags |= GLOB_MAGCHAR;
388            /* collapse adjacent stars to one, to avoid
389             * exponential behavior
390             */
391            if (bufnext == patbuf || bufnext[-1] != M_ALL)
392                *bufnext++ = M_ALL;
393            break;
394        default:
395            *bufnext++ = CHAR(c);
396            break;
397        }
398    }
399    *bufnext = EOS;
400#ifdef DEBUG
401    qprintf(patbuf);
402#endif
403
404    if ((err = glob1(patbuf, pglob, no_match)) != 0)
405        return (err);
406
407    /*
408     * If there was no match we are going to append the pattern
409     * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
410     * and the pattern did not contain any magic characters
411     * GLOB_NOMAGIC is there just for compatibility with csh.
412     */
413    if (pglob->gl_pathc == oldpathc &&
414        ((flags & GLOB_NOCHECK) ||
415         ((flags & GLOB_NOMAGIC) && !(pglob->gl_flags & GLOB_MAGCHAR)))) {
416        if (!(flags & GLOB_QUOTE)) {
417            Char *dp = compilebuf;
418            const unsigned char *sp = compilepat;
419
420            while ((*dp++ = *sp++) != '\0')
421                continue;
422        }
423        else {
424            /*
425             * copy pattern, interpreting quotes; this is slightly different
426             * than the interpretation of quotes above -- which should prevail?
427             */
428            while (*compilepat != EOS) {
429                if (*compilepat == QUOTE) {
430                    if (*++compilepat == EOS)
431                        --compilepat;
432                }
433                *compilebuf++ = (unsigned char) *compilepat++;
434            }
435            *compilebuf = EOS;
436        }
437        return (globextend(patbuf, pglob));
438    }
439    else if (!(flags & GLOB_NOSORT))
440        qsort((char *) (pglob->gl_pathv + pglob->gl_offs + oldpathc),
441              pglob->gl_pathc - oldpathc, sizeof(char *),
442              (int (*) __P((const void *, const void *))) compare);
443    return (0);
444}
445
446static int
447glob1(pattern, pglob, no_match)
448    Char *pattern;
449    glob_t *pglob;
450    int     no_match;
451{
452    Char pathbuf[MAXPATHLEN + 1];
453
454    /*
455     * a null pathname is invalid -- POSIX 1003.1 sect. 2.4.
456     */
457    if (*pattern == EOS)
458        return (0);
459    return (glob2(pathbuf, pathbuf, pattern, pglob, no_match));
460}
461
462/*
463 * functions glob2 and glob3 are mutually recursive; there is one level
464 * of recursion for each segment in the pattern that contains one or
465 * more meta characters.
466 */
467static int
468glob2(pathbuf, pathend, pattern, pglob, no_match)
469    Char *pathbuf, *pathend, *pattern;
470    glob_t *pglob;
471    int     no_match;
472{
473    struct stat sbuf;
474    int anymeta;
475    Char *p, *q;
476
477    /*
478     * loop over pattern segments until end of pattern or until segment with
479     * meta character found.
480     */
481    anymeta = 0;
482    for (;;) {
483        if (*pattern == EOS) {  /* end of pattern? */
484            *pathend = EOS;
485
486            if (Lstat(pathbuf, &sbuf))
487                return (0);
488
489            if (((pglob->gl_flags & GLOB_MARK) &&
490                 pathend[-1] != SEP) &&
491                (S_ISDIR(sbuf.st_mode)
492#ifdef S_IFLNK
493                 || (S_ISLNK(sbuf.st_mode) &&
494                     (Stat(pathbuf, &sbuf) == 0) &&
495                     S_ISDIR(sbuf.st_mode))
496#endif
497                 )) {
498                *pathend++ = SEP;
499                *pathend = EOS;
500            }
501            ++pglob->gl_matchc;
502            return (globextend(pathbuf, pglob));
503        }
504
505        /* find end of next segment, copy tentatively to pathend */
506        q = pathend;
507        p = pattern;
508        while (*p != EOS && *p != SEP) {
509            if (ismeta(*p))
510                anymeta = 1;
511            *q++ = *p++;
512        }
513
514        if (!anymeta) {         /* no expansion, do next segment */
515            pathend = q;
516            pattern = p;
517            while (*pattern == SEP)
518                *pathend++ = *pattern++;
519        }
520        else                    /* need expansion, recurse */
521            return (glob3(pathbuf, pathend, pattern, p, pglob, no_match));
522    }
523    /* NOTREACHED */
524}
525
526
527static int
528glob3(pathbuf, pathend, pattern, restpattern, pglob, no_match)
529    Char *pathbuf, *pathend, *pattern, *restpattern;
530    glob_t *pglob;
531    int     no_match;
532{
533    extern int errno;
534    DIR    *dirp;
535    struct dirent *dp;
536    int     err;
537    Char m_not = (pglob->gl_flags & GLOB_ALTNOT) ? M_ALTNOT : M_NOT;
538    char cpathbuf[MAXPATHLEN], *ptr;;
539
540    *pathend = EOS;
541    errno = 0;
542
543    if (!(dirp = Opendir(pathbuf))) {
544        /* todo: don't call for ENOENT or ENOTDIR? */
545        for (ptr = cpathbuf; (*ptr++ = (char) *pathbuf++) != EOS;)
546            continue;
547        if ((pglob->gl_errfunc && (*pglob->gl_errfunc) (cpathbuf, errno)) ||
548            (pglob->gl_flags & GLOB_ERR))
549            return (GLOB_ABEND);
550        else
551            return (0);
552    }
553
554    err = 0;
555
556    /* search directory for matching names */
557    while ((dp = readdir(dirp)) != NULL) {
558        register unsigned char *sc;
559        register Char *dc;
560
561        /* initial DOT must be matched literally */
562        if (dp->d_name[0] == DOT && *pattern != DOT)
563            continue;
564        for (sc = (unsigned char *) dp->d_name, dc = pathend;
565             (*dc++ = *sc++) != '\0';)
566            continue;
567        if (match(pathend, pattern, restpattern, (int) m_not) == no_match) {
568            *pathend = EOS;
569            continue;
570        }
571        err = glob2(pathbuf, --dc, restpattern, pglob, no_match);
572        if (err)
573            break;
574    }
575    /* todo: check error from readdir? */
576    (void) closedir(dirp);
577    return (err);
578}
579
580
581/*
582 * Extend the gl_pathv member of a glob_t structure to accomodate a new item,
583 * add the new item, and update gl_pathc.
584 *
585 * This assumes the BSD realloc, which only copies the block when its size
586 * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
587 * behavior.
588 *
589 * Return 0 if new item added, error code if memory couldn't be allocated.
590 *
591 * Invariant of the glob_t structure:
592 *      Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
593 *       gl_pathv points to (gl_offs + gl_pathc + 1) items.
594 */
595static int
596globextend(path, pglob)
597    Char *path;
598    glob_t *pglob;
599{
600    register char **pathv;
601    register int i;
602    unsigned int newsize;
603    char   *copy;
604    Char *p;
605
606    newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
607    pathv = (char **) (pglob->gl_pathv ?
608                       xrealloc((ptr_t) pglob->gl_pathv, (size_t) newsize) :
609                       xmalloc((size_t) newsize));
610    if (pathv == NULL)
611        return (GLOB_NOSPACE);
612
613    if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
614        /* first time around -- clear initial gl_offs items */
615        pathv += pglob->gl_offs;
616        for (i = pglob->gl_offs; --i >= 0;)
617            *--pathv = NULL;
618    }
619    pglob->gl_pathv = pathv;
620
621    for (p = path; *p++;)
622        continue;
623    if ((copy = (char *) xmalloc((size_t) (p - path))) != NULL) {
624        register char *dc = copy;
625        register Char *sc = path;
626
627        while ((*dc++ = *sc++) != '\0')
628            continue;
629        pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
630    }
631    pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
632    return ((copy == NULL) ? GLOB_NOSPACE : 0);
633}
634
635
636/*
637 * pattern matching function for filenames.  Each occurrence of the *
638 * pattern causes a recursion level.
639 */
640static  int
641match(name, pat, patend, m_not)
642    register Char *name, *pat, *patend;
643    int m_not;
644{
645    int ok, negate_range;
646    Char c, k;
647
648    while (pat < patend) {
649        c = *pat++;
650        switch (c & M_MASK) {
651        case M_ALL:
652            if (pat == patend)
653                return (1);
654            do
655                if (match(name, pat, patend, m_not))
656                    return (1);
657            while (*name++ != EOS);
658            return (0);
659        case M_ONE:
660            if (*name++ == EOS)
661                return (0);
662            break;
663        case M_SET:
664            ok = 0;
665            if ((k = *name++) == EOS)
666                return (0);
667            if ((negate_range = ((*pat & M_MASK) == m_not)) != 0)
668                ++pat;
669            while (((c = *pat++) & M_MASK) != M_END) {
670                if ((*pat & M_MASK) == M_RNG) {
671                    if (globcharcoll(CHAR(c), CHAR(k)) <= 0 &&
672                        globcharcoll(CHAR(k), CHAR(pat[1])) <= 0)
673                        ok = 1;
674                    pat += 2;
675                }
676                else if (c == k)
677                    ok = 1;
678            }
679            if (ok == negate_range)
680                return (0);
681            break;
682        default:
683            k = *name++;
684            if (samecase(k) != samecase(c))
685                return (0);
686            break;
687        }
688    }
689    return (*name == EOS);
690}
691
692/* free allocated data belonging to a glob_t structure */
693void
694globfree(pglob)
695    glob_t *pglob;
696{
697    register int i;
698    register char **pp;
699
700    if (pglob->gl_pathv != NULL) {
701        pp = pglob->gl_pathv + pglob->gl_offs;
702        for (i = pglob->gl_pathc; i--; ++pp)
703            if (*pp)
704                xfree((ptr_t) *pp), *pp = NULL;
705        xfree((ptr_t) pglob->gl_pathv), pglob->gl_pathv = NULL;
706    }
707}
Note: See TracBrowser for help on using the repository browser.