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