1c80476e4SDavid E. O'Brien /* 2c80476e4SDavid E. O'Brien * Copyright (c) 1989 The Regents of the University of California. 3c80476e4SDavid E. O'Brien * All rights reserved. 4c80476e4SDavid E. O'Brien * 5c80476e4SDavid E. O'Brien * This code is derived from software contributed to Berkeley by 6c80476e4SDavid E. O'Brien * Guido van Rossum. 7c80476e4SDavid E. O'Brien * 8c80476e4SDavid E. O'Brien * Redistribution and use in source and binary forms, with or without 9c80476e4SDavid E. O'Brien * modification, are permitted provided that the following conditions 10c80476e4SDavid E. O'Brien * are met: 11c80476e4SDavid E. O'Brien * 1. Redistributions of source code must retain the above copyright 12c80476e4SDavid E. O'Brien * notice, this list of conditions and the following disclaimer. 13c80476e4SDavid E. O'Brien * 2. Redistributions in binary form must reproduce the above copyright 14c80476e4SDavid E. O'Brien * notice, this list of conditions and the following disclaimer in the 15c80476e4SDavid E. O'Brien * documentation and/or other materials provided with the distribution. 1629301572SMark Peek * 3. Neither the name of the University nor the names of its contributors 17c80476e4SDavid E. O'Brien * may be used to endorse or promote products derived from this software 18c80476e4SDavid E. O'Brien * without specific prior written permission. 19c80476e4SDavid E. O'Brien * 20c80476e4SDavid E. O'Brien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21c80476e4SDavid E. O'Brien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22c80476e4SDavid E. O'Brien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23c80476e4SDavid E. O'Brien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24c80476e4SDavid E. O'Brien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25c80476e4SDavid E. O'Brien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26c80476e4SDavid E. O'Brien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27c80476e4SDavid E. O'Brien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28c80476e4SDavid E. O'Brien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29c80476e4SDavid E. O'Brien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30c80476e4SDavid E. O'Brien * SUCH DAMAGE. 31c80476e4SDavid E. O'Brien */ 32c80476e4SDavid E. O'Brien #if defined(LIBC_SCCS) && !defined(lint) 33c80476e4SDavid E. O'Brien static char sccsid[] = "@(#)glob.c 5.12 (Berkeley) 6/24/91"; 34c80476e4SDavid E. O'Brien #endif /* LIBC_SCCS and not lint */ 35c80476e4SDavid E. O'Brien /* 36c80476e4SDavid E. O'Brien * Glob: the interface is a superset of the one defined in POSIX 1003.2, 37c80476e4SDavid E. O'Brien * draft 9. 38c80476e4SDavid E. O'Brien * 39c80476e4SDavid E. O'Brien * The [!...] convention to negate a range is supported (SysV, Posix, ksh). 40c80476e4SDavid E. O'Brien * 41c80476e4SDavid E. O'Brien * Optional extra services, controlled by flags not defined by POSIX: 42c80476e4SDavid E. O'Brien * 43c80476e4SDavid E. O'Brien * GLOB_QUOTE: 44c80476e4SDavid E. O'Brien * Escaping convention: \ inhibits any special meaning the following 45c80476e4SDavid E. O'Brien * character might have (except \ at end of string is retained). 46c80476e4SDavid E. O'Brien * GLOB_MAGCHAR: 47c80476e4SDavid E. O'Brien * Set in gl_flags if pattern contained a globbing character. 48c80476e4SDavid E. O'Brien * GLOB_ALTNOT: 49c80476e4SDavid E. O'Brien * Use ^ instead of ! for "not". 50c80476e4SDavid E. O'Brien * gl_matchc: 51c80476e4SDavid E. O'Brien * Number of matches in the current invocation of glob. 52c80476e4SDavid E. O'Brien */ 53c80476e4SDavid E. O'Brien 543b6eaa7bSAndrey A. Chernov #ifdef WINNT_NATIVE 55c80476e4SDavid E. O'Brien #pragma warning(disable:4244) 563b6eaa7bSAndrey A. Chernov #endif /* WINNT_NATIVE */ 57c80476e4SDavid E. O'Brien 58c80476e4SDavid E. O'Brien #define Char __Char 59c80476e4SDavid E. O'Brien #include "sh.h" 6023338178SMark Peek #include "glob.h" 6123338178SMark Peek 62c80476e4SDavid E. O'Brien #undef Char 63c80476e4SDavid E. O'Brien #undef QUOTE 64c80476e4SDavid E. O'Brien #undef TILDE 65c80476e4SDavid E. O'Brien #undef META 66c80476e4SDavid E. O'Brien #undef ismeta 67c80476e4SDavid E. O'Brien #undef Strchr 68c80476e4SDavid E. O'Brien 69c80476e4SDavid E. O'Brien #ifndef S_ISDIR 70c80476e4SDavid E. O'Brien #define S_ISDIR(a) (((a) & S_IFMT) == S_IFDIR) 71c80476e4SDavid E. O'Brien #endif 72c80476e4SDavid E. O'Brien 73c80476e4SDavid E. O'Brien #if !defined(S_ISLNK) && defined(S_IFLNK) 74c80476e4SDavid E. O'Brien #define S_ISLNK(a) (((a) & S_IFMT) == S_IFLNK) 75c80476e4SDavid E. O'Brien #endif 76c80476e4SDavid E. O'Brien 77c80476e4SDavid E. O'Brien #if !defined(S_ISLNK) && !defined(lstat) 78c80476e4SDavid E. O'Brien #define lstat stat 79c80476e4SDavid E. O'Brien #endif 80c80476e4SDavid E. O'Brien 81c80476e4SDavid E. O'Brien typedef unsigned short Char; 82c80476e4SDavid E. O'Brien 8345e5710bSMark Peek static int glob1 (Char *, glob_t *, int); 8445e5710bSMark Peek static int glob2 (struct strbuf *, const Char *, glob_t *, int); 8545e5710bSMark Peek static int glob3 (struct strbuf *, const Char *, const Char *, 8645e5710bSMark Peek glob_t *, int); 8745e5710bSMark Peek static void globextend (const char *, glob_t *); 8845e5710bSMark Peek static int match (const char *, const Char *, const Char *, 8945e5710bSMark Peek int); 9045e5710bSMark Peek static int compare (const void *, const void *); 9145e5710bSMark Peek static DIR *Opendir (const char *); 92c80476e4SDavid E. O'Brien #ifdef S_IFLNK 9345e5710bSMark Peek static int Lstat (const char *, struct stat *); 94c80476e4SDavid E. O'Brien #endif 9545e5710bSMark Peek static int Stat (const char *, struct stat *sb); 9645e5710bSMark Peek static Char *Strchr (Char *, int); 97c80476e4SDavid E. O'Brien #ifdef DEBUG 9845e5710bSMark Peek static void qprintf (const Char *); 99c80476e4SDavid E. O'Brien #endif 100c80476e4SDavid E. O'Brien 101c80476e4SDavid E. O'Brien #define DOLLAR '$' 102c80476e4SDavid E. O'Brien #define DOT '.' 103c80476e4SDavid E. O'Brien #define EOS '\0' 104c80476e4SDavid E. O'Brien #define LBRACKET '[' 105c80476e4SDavid E. O'Brien #define NOT '!' 106c80476e4SDavid E. O'Brien #define ALTNOT '^' 107c80476e4SDavid E. O'Brien #define QUESTION '?' 108c80476e4SDavid E. O'Brien #define QUOTE '\\' 109c80476e4SDavid E. O'Brien #define RANGE '-' 110c80476e4SDavid E. O'Brien #define RBRACKET ']' 111c80476e4SDavid E. O'Brien #define SEP '/' 112c80476e4SDavid E. O'Brien #define STAR '*' 113c80476e4SDavid E. O'Brien #define TILDE '~' 114c80476e4SDavid E. O'Brien #define UNDERSCORE '_' 115c80476e4SDavid E. O'Brien 116c80476e4SDavid E. O'Brien #define M_META 0x8000 117c80476e4SDavid E. O'Brien #define M_PROTECT 0x4000 118c80476e4SDavid E. O'Brien #define M_MASK 0xffff 119c80476e4SDavid E. O'Brien #define M_ASCII 0x00ff 120c80476e4SDavid E. O'Brien 12145e5710bSMark Peek #define LCHAR(c) ((c)&M_ASCII) 122c80476e4SDavid E. O'Brien #define META(c) ((c)|M_META) 123c80476e4SDavid E. O'Brien #define M_ALL META('*') 124c80476e4SDavid E. O'Brien #define M_END META(']') 125c80476e4SDavid E. O'Brien #define M_NOT META('!') 126c80476e4SDavid E. O'Brien #define M_ALTNOT META('^') 127c80476e4SDavid E. O'Brien #define M_ONE META('?') 128c80476e4SDavid E. O'Brien #define M_RNG META('-') 129c80476e4SDavid E. O'Brien #define M_SET META('[') 130c80476e4SDavid E. O'Brien #define ismeta(c) (((c)&M_META) != 0) 131c80476e4SDavid E. O'Brien 132c80476e4SDavid E. O'Brien int 13345e5710bSMark Peek globcharcoll(__Char c1, __Char c2, int cs) 134c80476e4SDavid E. O'Brien { 13545e5710bSMark Peek #if defined(NLS) && defined(LC_COLLATE) && defined(HAVE_STRCOLL) 13645e5710bSMark Peek # if defined(WIDE_STRINGS) 13723338178SMark Peek wchar_t s1[2], s2[2]; 13823338178SMark Peek 13923338178SMark Peek if (c1 == c2) 14023338178SMark Peek return (0); 14123338178SMark Peek if (cs) { 14223338178SMark Peek c1 = towlower(c1); 14323338178SMark Peek c2 = towlower(c2); 14423338178SMark Peek } else { 14523338178SMark Peek /* This should not be here, but I'll rather leave it in than engage in 14623338178SMark Peek a LC_COLLATE flamewar about a shell I don't use... */ 14723338178SMark Peek if (iswlower(c1) && iswupper(c2)) 14823338178SMark Peek return (1); 14923338178SMark Peek if (iswupper(c1) && iswlower(c2)) 15023338178SMark Peek return (-1); 15123338178SMark Peek } 15223338178SMark Peek s1[0] = c1; 15323338178SMark Peek s2[0] = c2; 15423338178SMark Peek s1[1] = s2[1] = '\0'; 15523338178SMark Peek return wcscoll(s1, s2); 15645e5710bSMark Peek # else /* not WIDE_STRINGS */ 157c80476e4SDavid E. O'Brien char s1[2], s2[2]; 158c80476e4SDavid E. O'Brien 159c80476e4SDavid E. O'Brien if (c1 == c2) 160c80476e4SDavid E. O'Brien return (0); 1618e66bd9eSDavid E. O'Brien /* 1628e66bd9eSDavid E. O'Brien * From kevin lyda <kevin@suberic.net>: 1638e66bd9eSDavid E. O'Brien * strcoll does not guarantee case sorting, so we pre-process now: 1648e66bd9eSDavid E. O'Brien */ 165b2d5d167SMark Peek if (cs) { 166b2d5d167SMark Peek c1 = islower(c1) ? c1 : tolower(c1); 167b2d5d167SMark Peek c2 = islower(c2) ? c2 : tolower(c2); 168b2d5d167SMark Peek } else { 1698e66bd9eSDavid E. O'Brien if (islower(c1) && isupper(c2)) 1708e66bd9eSDavid E. O'Brien return (1); 17123338178SMark Peek if (isupper(c1) && islower(c2)) 17223338178SMark Peek return (-1); 173b2d5d167SMark Peek } 174c80476e4SDavid E. O'Brien s1[0] = c1; 175c80476e4SDavid E. O'Brien s2[0] = c2; 176c80476e4SDavid E. O'Brien s1[1] = s2[1] = '\0'; 177c80476e4SDavid E. O'Brien return strcoll(s1, s2); 17823338178SMark Peek # endif 179c80476e4SDavid E. O'Brien #else 180c80476e4SDavid E. O'Brien return (c1 - c2); 181c80476e4SDavid E. O'Brien #endif 182c80476e4SDavid E. O'Brien } 183c80476e4SDavid E. O'Brien 184c80476e4SDavid E. O'Brien /* 185c80476e4SDavid E. O'Brien * Need to dodge two kernel bugs: 186c80476e4SDavid E. O'Brien * opendir("") != opendir(".") 187c80476e4SDavid E. O'Brien * NAMEI_BUG: on plain files trailing slashes are ignored in some kernels. 188c80476e4SDavid E. O'Brien * POSIX specifies that they should be ignored in directories. 189c80476e4SDavid E. O'Brien */ 190c80476e4SDavid E. O'Brien 191c80476e4SDavid E. O'Brien static DIR * 19245e5710bSMark Peek Opendir(const char *str) 193c80476e4SDavid E. O'Brien { 194c80476e4SDavid E. O'Brien #if defined(hpux) || defined(__hpux) 195c80476e4SDavid E. O'Brien struct stat st; 196c80476e4SDavid E. O'Brien #endif 197c80476e4SDavid E. O'Brien 198c80476e4SDavid E. O'Brien if (!*str) 199c80476e4SDavid E. O'Brien return (opendir(".")); 200c80476e4SDavid E. O'Brien #if defined(hpux) || defined(__hpux) 201c80476e4SDavid E. O'Brien /* 202c80476e4SDavid E. O'Brien * Opendir on some device files hangs, so avoid it 203c80476e4SDavid E. O'Brien */ 20445e5710bSMark Peek if (stat(str, &st) == -1 || !S_ISDIR(st.st_mode)) 205c80476e4SDavid E. O'Brien return NULL; 206c80476e4SDavid E. O'Brien #endif 20745e5710bSMark Peek return opendir(str); 208c80476e4SDavid E. O'Brien } 209c80476e4SDavid E. O'Brien 210c80476e4SDavid E. O'Brien #ifdef S_IFLNK 211c80476e4SDavid E. O'Brien static int 21245e5710bSMark Peek Lstat(const char *fn, struct stat *sb) 213c80476e4SDavid E. O'Brien { 214c80476e4SDavid E. O'Brien int st; 215c80476e4SDavid E. O'Brien 21645e5710bSMark Peek st = lstat(fn, sb); 21745e5710bSMark Peek # ifdef NAMEI_BUG 21845e5710bSMark Peek if (*fn != 0 && strend(fn)[-1] == '/' && !S_ISDIR(sb->st_mode)) 21945e5710bSMark Peek st = -1; 220c80476e4SDavid E. O'Brien # endif /* NAMEI_BUG */ 22145e5710bSMark Peek return st; 222c80476e4SDavid E. O'Brien } 223c80476e4SDavid E. O'Brien #else 224c80476e4SDavid E. O'Brien #define Lstat Stat 225c80476e4SDavid E. O'Brien #endif /* S_IFLNK */ 226c80476e4SDavid E. O'Brien 227c80476e4SDavid E. O'Brien static int 22845e5710bSMark Peek Stat(const char *fn, struct stat *sb) 229c80476e4SDavid E. O'Brien { 230c80476e4SDavid E. O'Brien int st; 231c80476e4SDavid E. O'Brien 23245e5710bSMark Peek st = stat(fn, sb); 23345e5710bSMark Peek #ifdef NAMEI_BUG 23445e5710bSMark Peek if (*fn != 0 && strend(fn)[-1] == '/' && !S_ISDIR(sb->st_mode)) 23545e5710bSMark Peek st = -1; 236c80476e4SDavid E. O'Brien #endif /* NAMEI_BUG */ 23745e5710bSMark Peek return st; 238c80476e4SDavid E. O'Brien } 239c80476e4SDavid E. O'Brien 240c80476e4SDavid E. O'Brien static Char * 24145e5710bSMark Peek Strchr(Char *str, int ch) 242c80476e4SDavid E. O'Brien { 243c80476e4SDavid E. O'Brien do 244c80476e4SDavid E. O'Brien if (*str == ch) 245c80476e4SDavid E. O'Brien return (str); 246c80476e4SDavid E. O'Brien while (*str++); 247c80476e4SDavid E. O'Brien return (NULL); 248c80476e4SDavid E. O'Brien } 249c80476e4SDavid E. O'Brien 250c80476e4SDavid E. O'Brien #ifdef DEBUG 251c80476e4SDavid E. O'Brien static void 25245e5710bSMark Peek qprintf(const Char *s) 253c80476e4SDavid E. O'Brien { 25445e5710bSMark Peek const Char *p; 255c80476e4SDavid E. O'Brien 256c80476e4SDavid E. O'Brien for (p = s; *p; p++) 257c80476e4SDavid E. O'Brien printf("%c", *p & 0xff); 258c80476e4SDavid E. O'Brien printf("\n"); 259c80476e4SDavid E. O'Brien for (p = s; *p; p++) 260c80476e4SDavid E. O'Brien printf("%c", *p & M_PROTECT ? '"' : ' '); 261c80476e4SDavid E. O'Brien printf("\n"); 262c80476e4SDavid E. O'Brien for (p = s; *p; p++) 263c80476e4SDavid E. O'Brien printf("%c", *p & M_META ? '_' : ' '); 264c80476e4SDavid E. O'Brien printf("\n"); 265c80476e4SDavid E. O'Brien } 266c80476e4SDavid E. O'Brien #endif /* DEBUG */ 267c80476e4SDavid E. O'Brien 268c80476e4SDavid E. O'Brien static int 26945e5710bSMark Peek compare(const void *p, const void *q) 270c80476e4SDavid E. O'Brien { 27145e5710bSMark Peek #if defined(NLS) && defined(HAVE_STRCOLL) 27245e5710bSMark Peek return (strcoll(*(char *const *) p, *(char *const *) q)); 273c80476e4SDavid E. O'Brien #else 27445e5710bSMark Peek return (strcmp(*(char *const *) p, *(char *const *) q)); 27545e5710bSMark Peek #endif /* NLS && HAVE_STRCOLL */ 276c80476e4SDavid E. O'Brien } 277c80476e4SDavid E. O'Brien 278c80476e4SDavid E. O'Brien /* 279c80476e4SDavid E. O'Brien * The main glob() routine: compiles the pattern (optionally processing 280c80476e4SDavid E. O'Brien * quotes), calls glob1() to do the real pattern matching, and finally 281c80476e4SDavid E. O'Brien * sorts the list (unless unsorted operation is requested). Returns 0 282c80476e4SDavid E. O'Brien * if things went well, nonzero if errors occurred. It is not an error 283c80476e4SDavid E. O'Brien * to find no matches. 284c80476e4SDavid E. O'Brien */ 285c80476e4SDavid E. O'Brien int 28645e5710bSMark Peek glob(const char *pattern, int flags, int (*errfunc) (const char *, int), 28745e5710bSMark Peek glob_t *pglob) 288c80476e4SDavid E. O'Brien { 289c80476e4SDavid E. O'Brien int err, oldpathc; 29045e5710bSMark Peek Char *bufnext, m_not; 29145e5710bSMark Peek const unsigned char *patnext; 292c80476e4SDavid E. O'Brien int c, not; 29345e5710bSMark Peek Char *qpatnext, *patbuf; 294c80476e4SDavid E. O'Brien int no_match; 295c80476e4SDavid E. O'Brien 29623338178SMark Peek patnext = (const unsigned char *) pattern; 297c80476e4SDavid E. O'Brien if (!(flags & GLOB_APPEND)) { 298c80476e4SDavid E. O'Brien pglob->gl_pathc = 0; 299c80476e4SDavid E. O'Brien pglob->gl_pathv = NULL; 300c80476e4SDavid E. O'Brien if (!(flags & GLOB_DOOFFS)) 301c80476e4SDavid E. O'Brien pglob->gl_offs = 0; 302c80476e4SDavid E. O'Brien } 303c80476e4SDavid E. O'Brien pglob->gl_flags = flags & ~GLOB_MAGCHAR; 304c80476e4SDavid E. O'Brien pglob->gl_errfunc = errfunc; 305c80476e4SDavid E. O'Brien oldpathc = pglob->gl_pathc; 306c80476e4SDavid E. O'Brien pglob->gl_matchc = 0; 307c80476e4SDavid E. O'Brien 308c80476e4SDavid E. O'Brien if (pglob->gl_flags & GLOB_ALTNOT) { 309c80476e4SDavid E. O'Brien not = ALTNOT; 310c80476e4SDavid E. O'Brien m_not = M_ALTNOT; 311c80476e4SDavid E. O'Brien } 312c80476e4SDavid E. O'Brien else { 313c80476e4SDavid E. O'Brien not = NOT; 314c80476e4SDavid E. O'Brien m_not = M_NOT; 315c80476e4SDavid E. O'Brien } 316c80476e4SDavid E. O'Brien 31745e5710bSMark Peek patbuf = xmalloc((strlen(pattern) + 1) * sizeof(*patbuf)); 318c80476e4SDavid E. O'Brien bufnext = patbuf; 319c80476e4SDavid E. O'Brien 320c80476e4SDavid E. O'Brien no_match = *patnext == not; 321c80476e4SDavid E. O'Brien if (no_match) 322c80476e4SDavid E. O'Brien patnext++; 323c80476e4SDavid E. O'Brien 324c80476e4SDavid E. O'Brien if (flags & GLOB_QUOTE) { 325c80476e4SDavid E. O'Brien /* Protect the quoted characters */ 32645e5710bSMark Peek while ((c = *patnext++) != EOS) { 32723338178SMark Peek #ifdef WIDE_STRINGS 32823338178SMark Peek int len; 32923338178SMark Peek 33023338178SMark Peek len = mblen((const char *)(patnext - 1), MB_LEN_MAX); 33123338178SMark Peek if (len == -1) 332a15e6f9aSMark Peek (void)mblen(NULL, 0); 333a15e6f9aSMark Peek else if (len > 1) { 3348e66bd9eSDavid E. O'Brien *bufnext++ = (Char) c; 33523338178SMark Peek while (--len != 0) 33623338178SMark Peek *bufnext++ = (Char) (*patnext++ | M_PROTECT); 33723338178SMark Peek } else 33823338178SMark Peek #endif /* WIDE_STRINGS */ 339c80476e4SDavid E. O'Brien if (c == QUOTE) { 340c80476e4SDavid E. O'Brien if ((c = *patnext++) == EOS) { 341c80476e4SDavid E. O'Brien c = QUOTE; 342c80476e4SDavid E. O'Brien --patnext; 343c80476e4SDavid E. O'Brien } 344c80476e4SDavid E. O'Brien *bufnext++ = (Char) (c | M_PROTECT); 345c80476e4SDavid E. O'Brien } 346c80476e4SDavid E. O'Brien else 347c80476e4SDavid E. O'Brien *bufnext++ = (Char) c; 348c80476e4SDavid E. O'Brien } 34923338178SMark Peek } 350c80476e4SDavid E. O'Brien else 35145e5710bSMark Peek while ((c = *patnext++) != EOS) 352c80476e4SDavid E. O'Brien *bufnext++ = (Char) c; 353c80476e4SDavid E. O'Brien *bufnext = EOS; 354c80476e4SDavid E. O'Brien 355c80476e4SDavid E. O'Brien bufnext = patbuf; 356c80476e4SDavid E. O'Brien qpatnext = patbuf; 357c80476e4SDavid E. O'Brien while ((c = *qpatnext++) != EOS) { 358c80476e4SDavid E. O'Brien switch (c) { 359c80476e4SDavid E. O'Brien case LBRACKET: 360c80476e4SDavid E. O'Brien c = *qpatnext; 361c80476e4SDavid E. O'Brien if (c == not) 362c80476e4SDavid E. O'Brien ++qpatnext; 363c80476e4SDavid E. O'Brien if (*qpatnext == EOS || 364c80476e4SDavid E. O'Brien Strchr(qpatnext + 1, RBRACKET) == NULL) { 365c80476e4SDavid E. O'Brien *bufnext++ = LBRACKET; 366c80476e4SDavid E. O'Brien if (c == not) 367c80476e4SDavid E. O'Brien --qpatnext; 368c80476e4SDavid E. O'Brien break; 369c80476e4SDavid E. O'Brien } 370c80476e4SDavid E. O'Brien pglob->gl_flags |= GLOB_MAGCHAR; 371c80476e4SDavid E. O'Brien *bufnext++ = M_SET; 372c80476e4SDavid E. O'Brien if (c == not) 373c80476e4SDavid E. O'Brien *bufnext++ = m_not; 374c80476e4SDavid E. O'Brien c = *qpatnext++; 375c80476e4SDavid E. O'Brien do { 37645e5710bSMark Peek *bufnext++ = LCHAR(c); 377c80476e4SDavid E. O'Brien if (*qpatnext == RANGE && 378c80476e4SDavid E. O'Brien (c = qpatnext[1]) != RBRACKET) { 379c80476e4SDavid E. O'Brien *bufnext++ = M_RNG; 38045e5710bSMark Peek *bufnext++ = LCHAR(c); 381c80476e4SDavid E. O'Brien qpatnext += 2; 382c80476e4SDavid E. O'Brien } 383c80476e4SDavid E. O'Brien } while ((c = *qpatnext++) != RBRACKET); 384c80476e4SDavid E. O'Brien *bufnext++ = M_END; 385c80476e4SDavid E. O'Brien break; 386c80476e4SDavid E. O'Brien case QUESTION: 387c80476e4SDavid E. O'Brien pglob->gl_flags |= GLOB_MAGCHAR; 388c80476e4SDavid E. O'Brien *bufnext++ = M_ONE; 389c80476e4SDavid E. O'Brien break; 390c80476e4SDavid E. O'Brien case STAR: 391c80476e4SDavid E. O'Brien pglob->gl_flags |= GLOB_MAGCHAR; 392c80476e4SDavid E. O'Brien /* collapse adjacent stars to one, to avoid 393c80476e4SDavid E. O'Brien * exponential behavior 394c80476e4SDavid E. O'Brien */ 395c80476e4SDavid E. O'Brien if (bufnext == patbuf || bufnext[-1] != M_ALL) 396c80476e4SDavid E. O'Brien *bufnext++ = M_ALL; 397c80476e4SDavid E. O'Brien break; 398c80476e4SDavid E. O'Brien default: 39945e5710bSMark Peek *bufnext++ = LCHAR(c); 400c80476e4SDavid E. O'Brien break; 401c80476e4SDavid E. O'Brien } 402c80476e4SDavid E. O'Brien } 403c80476e4SDavid E. O'Brien *bufnext = EOS; 404c80476e4SDavid E. O'Brien #ifdef DEBUG 405c80476e4SDavid E. O'Brien qprintf(patbuf); 406c80476e4SDavid E. O'Brien #endif 407c80476e4SDavid E. O'Brien 40845e5710bSMark Peek if ((err = glob1(patbuf, pglob, no_match)) != 0) { 40945e5710bSMark Peek xfree(patbuf); 410c80476e4SDavid E. O'Brien return (err); 41145e5710bSMark Peek } 412c80476e4SDavid E. O'Brien 413c80476e4SDavid E. O'Brien /* 414c80476e4SDavid E. O'Brien * If there was no match we are going to append the pattern 415c80476e4SDavid E. O'Brien * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified 416c80476e4SDavid E. O'Brien * and the pattern did not contain any magic characters 417c80476e4SDavid E. O'Brien * GLOB_NOMAGIC is there just for compatibility with csh. 418c80476e4SDavid E. O'Brien */ 419c80476e4SDavid E. O'Brien if (pglob->gl_pathc == oldpathc && 420c80476e4SDavid E. O'Brien ((flags & GLOB_NOCHECK) || 421c80476e4SDavid E. O'Brien ((flags & GLOB_NOMAGIC) && !(pglob->gl_flags & GLOB_MAGCHAR)))) { 42245e5710bSMark Peek if (!(flags & GLOB_QUOTE)) 42345e5710bSMark Peek globextend(pattern, pglob); 424c80476e4SDavid E. O'Brien else { 42545e5710bSMark Peek char *copy, *dest; 42645e5710bSMark Peek const char *src; 42745e5710bSMark Peek 42845e5710bSMark Peek /* copy pattern, interpreting quotes */ 42945e5710bSMark Peek copy = xmalloc(strlen(pattern) + 1); 43045e5710bSMark Peek dest = copy; 43145e5710bSMark Peek src = pattern; 43245e5710bSMark Peek while (*src != EOS) { 43345e5710bSMark Peek if (*src == QUOTE) { 43445e5710bSMark Peek if (*++src == EOS) 43545e5710bSMark Peek --src; 436c80476e4SDavid E. O'Brien } 43745e5710bSMark Peek *dest++ = *src++; 438c80476e4SDavid E. O'Brien } 43945e5710bSMark Peek *dest = EOS; 44045e5710bSMark Peek globextend(copy, pglob); 44145e5710bSMark Peek xfree(copy); 442c80476e4SDavid E. O'Brien } 44345e5710bSMark Peek xfree(patbuf); 44445e5710bSMark Peek return 0; 445c80476e4SDavid E. O'Brien } 4468e66bd9eSDavid E. O'Brien else if (!(flags & GLOB_NOSORT) && (pglob->gl_pathc != oldpathc)) 44745e5710bSMark Peek qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc, 44845e5710bSMark Peek pglob->gl_pathc - oldpathc, sizeof(char *), compare); 44945e5710bSMark Peek xfree(patbuf); 450c80476e4SDavid E. O'Brien return (0); 451c80476e4SDavid E. O'Brien } 452c80476e4SDavid E. O'Brien 453c80476e4SDavid E. O'Brien static int 45445e5710bSMark Peek glob1(Char *pattern, glob_t *pglob, int no_match) 455c80476e4SDavid E. O'Brien { 45645e5710bSMark Peek struct strbuf pathbuf = strbuf_INIT; 45745e5710bSMark Peek int err; 458c80476e4SDavid E. O'Brien 459c80476e4SDavid E. O'Brien /* 460c80476e4SDavid E. O'Brien * a null pathname is invalid -- POSIX 1003.1 sect. 2.4. 461c80476e4SDavid E. O'Brien */ 462c80476e4SDavid E. O'Brien if (*pattern == EOS) 463c80476e4SDavid E. O'Brien return (0); 46445e5710bSMark Peek err = glob2(&pathbuf, pattern, pglob, no_match); 46545e5710bSMark Peek xfree(pathbuf.s); 46645e5710bSMark Peek return err; 467c80476e4SDavid E. O'Brien } 468c80476e4SDavid E. O'Brien 469c80476e4SDavid E. O'Brien /* 470c80476e4SDavid E. O'Brien * functions glob2 and glob3 are mutually recursive; there is one level 471c80476e4SDavid E. O'Brien * of recursion for each segment in the pattern that contains one or 472c80476e4SDavid E. O'Brien * more meta characters. 473c80476e4SDavid E. O'Brien */ 474c80476e4SDavid E. O'Brien static int 47545e5710bSMark Peek glob2(struct strbuf *pathbuf, const Char *pattern, glob_t *pglob, int no_match) 476c80476e4SDavid E. O'Brien { 477c80476e4SDavid E. O'Brien struct stat sbuf; 478c80476e4SDavid E. O'Brien int anymeta; 47945e5710bSMark Peek const Char *p; 48045e5710bSMark Peek size_t orig_len; 481c80476e4SDavid E. O'Brien 482c80476e4SDavid E. O'Brien /* 483c80476e4SDavid E. O'Brien * loop over pattern segments until end of pattern or until segment with 484c80476e4SDavid E. O'Brien * meta character found. 485c80476e4SDavid E. O'Brien */ 486c80476e4SDavid E. O'Brien anymeta = 0; 487c80476e4SDavid E. O'Brien for (;;) { 488c80476e4SDavid E. O'Brien if (*pattern == EOS) { /* end of pattern? */ 48945e5710bSMark Peek strbuf_terminate(pathbuf); 490c80476e4SDavid E. O'Brien 49145e5710bSMark Peek if (Lstat(pathbuf->s, &sbuf)) 492c80476e4SDavid E. O'Brien return (0); 493c80476e4SDavid E. O'Brien 494c80476e4SDavid E. O'Brien if (((pglob->gl_flags & GLOB_MARK) && 49545e5710bSMark Peek pathbuf->s[pathbuf->len - 1] != SEP) && 496c80476e4SDavid E. O'Brien (S_ISDIR(sbuf.st_mode) 497c80476e4SDavid E. O'Brien #ifdef S_IFLNK 498c80476e4SDavid E. O'Brien || (S_ISLNK(sbuf.st_mode) && 49945e5710bSMark Peek (Stat(pathbuf->s, &sbuf) == 0) && 500c80476e4SDavid E. O'Brien S_ISDIR(sbuf.st_mode)) 501c80476e4SDavid E. O'Brien #endif 502c80476e4SDavid E. O'Brien )) { 50345e5710bSMark Peek strbuf_append1(pathbuf, SEP); 50445e5710bSMark Peek strbuf_terminate(pathbuf); 505c80476e4SDavid E. O'Brien } 506c80476e4SDavid E. O'Brien ++pglob->gl_matchc; 50745e5710bSMark Peek globextend(pathbuf->s, pglob); 50845e5710bSMark Peek return 0; 509c80476e4SDavid E. O'Brien } 510c80476e4SDavid E. O'Brien 51145e5710bSMark Peek /* find end of next segment, tentatively copy to pathbuf */ 512c80476e4SDavid E. O'Brien p = pattern; 51345e5710bSMark Peek orig_len = pathbuf->len; 514c80476e4SDavid E. O'Brien while (*p != EOS && *p != SEP) { 515c80476e4SDavid E. O'Brien if (ismeta(*p)) 516c80476e4SDavid E. O'Brien anymeta = 1; 51745e5710bSMark Peek strbuf_append1(pathbuf, *p++); 518c80476e4SDavid E. O'Brien } 519c80476e4SDavid E. O'Brien 520c80476e4SDavid E. O'Brien if (!anymeta) { /* no expansion, do next segment */ 521c80476e4SDavid E. O'Brien pattern = p; 522c80476e4SDavid E. O'Brien while (*pattern == SEP) 52345e5710bSMark Peek strbuf_append1(pathbuf, *pattern++); 524c80476e4SDavid E. O'Brien } 52545e5710bSMark Peek else { /* need expansion, recurse */ 52645e5710bSMark Peek pathbuf->len = orig_len; 52745e5710bSMark Peek return (glob3(pathbuf, pattern, p, pglob, no_match)); 52845e5710bSMark Peek } 529c80476e4SDavid E. O'Brien } 530c80476e4SDavid E. O'Brien /* NOTREACHED */ 531c80476e4SDavid E. O'Brien } 532c80476e4SDavid E. O'Brien 533c80476e4SDavid E. O'Brien 534c80476e4SDavid E. O'Brien static int 53545e5710bSMark Peek glob3(struct strbuf *pathbuf, const Char *pattern, const Char *restpattern, 53645e5710bSMark Peek glob_t *pglob, int no_match) 537c80476e4SDavid E. O'Brien { 538c80476e4SDavid E. O'Brien DIR *dirp; 539c80476e4SDavid E. O'Brien struct dirent *dp; 540c80476e4SDavid E. O'Brien int err; 541c80476e4SDavid E. O'Brien Char m_not = (pglob->gl_flags & GLOB_ALTNOT) ? M_ALTNOT : M_NOT; 54245e5710bSMark Peek size_t orig_len; 543c80476e4SDavid E. O'Brien 54445e5710bSMark Peek strbuf_terminate(pathbuf); 545c80476e4SDavid E. O'Brien errno = 0; 546c80476e4SDavid E. O'Brien 54745e5710bSMark Peek if (!(dirp = Opendir(pathbuf->s))) { 548c80476e4SDavid E. O'Brien /* todo: don't call for ENOENT or ENOTDIR? */ 54945e5710bSMark Peek if ((pglob->gl_errfunc && (*pglob->gl_errfunc) (pathbuf->s, errno)) || 550c80476e4SDavid E. O'Brien (pglob->gl_flags & GLOB_ERR)) 551c80476e4SDavid E. O'Brien return (GLOB_ABEND); 552c80476e4SDavid E. O'Brien else 553c80476e4SDavid E. O'Brien return (0); 554c80476e4SDavid E. O'Brien } 555c80476e4SDavid E. O'Brien 556c80476e4SDavid E. O'Brien err = 0; 557c80476e4SDavid E. O'Brien 55845e5710bSMark Peek orig_len = pathbuf->len; 559c80476e4SDavid E. O'Brien /* search directory for matching names */ 560c80476e4SDavid E. O'Brien while ((dp = readdir(dirp)) != NULL) { 561c80476e4SDavid E. O'Brien /* initial DOT must be matched literally */ 562c80476e4SDavid E. O'Brien if (dp->d_name[0] == DOT && *pattern != DOT) 563c80476e4SDavid E. O'Brien continue; 56445e5710bSMark Peek pathbuf->len = orig_len; 56545e5710bSMark Peek strbuf_append(pathbuf, dp->d_name); 56645e5710bSMark Peek strbuf_terminate(pathbuf); 56745e5710bSMark Peek if (match(pathbuf->s + orig_len, pattern, restpattern, (int) m_not) 56845e5710bSMark Peek == no_match) 569c80476e4SDavid E. O'Brien continue; 57045e5710bSMark Peek err = glob2(pathbuf, restpattern, pglob, no_match); 571c80476e4SDavid E. O'Brien if (err) 572c80476e4SDavid E. O'Brien break; 573c80476e4SDavid E. O'Brien } 574c80476e4SDavid E. O'Brien /* todo: check error from readdir? */ 57545e5710bSMark Peek closedir(dirp); 576c80476e4SDavid E. O'Brien return (err); 577c80476e4SDavid E. O'Brien } 578c80476e4SDavid E. O'Brien 579c80476e4SDavid E. O'Brien 580c80476e4SDavid E. O'Brien /* 581c80476e4SDavid E. O'Brien * Extend the gl_pathv member of a glob_t structure to accomodate a new item, 582c80476e4SDavid E. O'Brien * add the new item, and update gl_pathc. 583c80476e4SDavid E. O'Brien * 584c80476e4SDavid E. O'Brien * This assumes the BSD realloc, which only copies the block when its size 585c80476e4SDavid E. O'Brien * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic 586c80476e4SDavid E. O'Brien * behavior. 587c80476e4SDavid E. O'Brien * 588c80476e4SDavid E. O'Brien * Return 0 if new item added, error code if memory couldn't be allocated. 589c80476e4SDavid E. O'Brien * 590c80476e4SDavid E. O'Brien * Invariant of the glob_t structure: 591c80476e4SDavid E. O'Brien * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and 592c80476e4SDavid E. O'Brien * gl_pathv points to (gl_offs + gl_pathc + 1) items. 593c80476e4SDavid E. O'Brien */ 59445e5710bSMark Peek static void 59545e5710bSMark Peek globextend(const char *path, glob_t *pglob) 596c80476e4SDavid E. O'Brien { 59723338178SMark Peek char **pathv; 59823338178SMark Peek int i; 59945e5710bSMark Peek size_t newsize; 600c80476e4SDavid E. O'Brien 601c80476e4SDavid E. O'Brien newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs); 60245e5710bSMark Peek pathv = xrealloc(pglob->gl_pathv, newsize); 603c80476e4SDavid E. O'Brien 604c80476e4SDavid E. O'Brien if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) { 605c80476e4SDavid E. O'Brien /* first time around -- clear initial gl_offs items */ 606c80476e4SDavid E. O'Brien pathv += pglob->gl_offs; 607c80476e4SDavid E. O'Brien for (i = pglob->gl_offs; --i >= 0;) 608c80476e4SDavid E. O'Brien *--pathv = NULL; 609c80476e4SDavid E. O'Brien } 610c80476e4SDavid E. O'Brien pglob->gl_pathv = pathv; 611c80476e4SDavid E. O'Brien 61245e5710bSMark Peek pathv[pglob->gl_offs + pglob->gl_pathc++] = strsave(path); 613c80476e4SDavid E. O'Brien pathv[pglob->gl_offs + pglob->gl_pathc] = NULL; 614c80476e4SDavid E. O'Brien } 615c80476e4SDavid E. O'Brien 61623338178SMark Peek static size_t 61745e5710bSMark Peek One_Char_mbtowc(__Char *pwc, const Char *s, size_t n) 61823338178SMark Peek { 61923338178SMark Peek #ifdef WIDE_STRINGS 62023338178SMark Peek char buf[MB_LEN_MAX], *p; 62123338178SMark Peek 62223338178SMark Peek if (n > MB_LEN_MAX) 62323338178SMark Peek n = MB_LEN_MAX; 62423338178SMark Peek p = buf; 62545e5710bSMark Peek while (p < buf + n && (*p++ = LCHAR(*s++)) != 0) 62623338178SMark Peek ; 62723338178SMark Peek return one_mbtowc(pwc, buf, n); 62823338178SMark Peek #else 62945e5710bSMark Peek *pwc = *s & CHAR; 63045e5710bSMark Peek return 1; 63123338178SMark Peek #endif 63223338178SMark Peek } 63323338178SMark Peek 634c80476e4SDavid E. O'Brien /* 635c80476e4SDavid E. O'Brien * pattern matching function for filenames. Each occurrence of the * 636c80476e4SDavid E. O'Brien * pattern causes a recursion level. 637c80476e4SDavid E. O'Brien */ 638c80476e4SDavid E. O'Brien static int 63945e5710bSMark Peek match(const char *name, const Char *pat, const Char *patend, int m_not) 640c80476e4SDavid E. O'Brien { 641c80476e4SDavid E. O'Brien int ok, negate_range; 64245e5710bSMark Peek Char c; 643c80476e4SDavid E. O'Brien 644c80476e4SDavid E. O'Brien while (pat < patend) { 64523338178SMark Peek size_t lwk; 64645e5710bSMark Peek __Char wc, wk; 64723338178SMark Peek 64823338178SMark Peek c = *pat; /* Only for M_MASK bits */ 64945e5710bSMark Peek pat += One_Char_mbtowc(&wc, pat, MB_LEN_MAX); 65045e5710bSMark Peek lwk = one_mbtowc(&wk, name, MB_LEN_MAX); 651c80476e4SDavid E. O'Brien switch (c & M_MASK) { 652c80476e4SDavid E. O'Brien case M_ALL: 653c80476e4SDavid E. O'Brien if (pat == patend) 654c80476e4SDavid E. O'Brien return (1); 65523338178SMark Peek for (;;) { 656c80476e4SDavid E. O'Brien if (match(name, pat, patend, m_not)) 657c80476e4SDavid E. O'Brien return (1); 65823338178SMark Peek if (*name == EOS) 65923338178SMark Peek break; 66023338178SMark Peek name += lwk; 66145e5710bSMark Peek lwk = one_mbtowc(&wk, name, MB_LEN_MAX); 66223338178SMark Peek } 663c80476e4SDavid E. O'Brien return (0); 664c80476e4SDavid E. O'Brien case M_ONE: 66523338178SMark Peek if (*name == EOS) 666c80476e4SDavid E. O'Brien return (0); 66723338178SMark Peek name += lwk; 668c80476e4SDavid E. O'Brien break; 669c80476e4SDavid E. O'Brien case M_SET: 670c80476e4SDavid E. O'Brien ok = 0; 67123338178SMark Peek if (*name == EOS) 672c80476e4SDavid E. O'Brien return (0); 67323338178SMark Peek name += lwk; 674c80476e4SDavid E. O'Brien if ((negate_range = ((*pat & M_MASK) == m_not)) != 0) 675c80476e4SDavid E. O'Brien ++pat; 67623338178SMark Peek while ((*pat & M_MASK) != M_END) { 67745e5710bSMark Peek pat += One_Char_mbtowc(&wc, pat, MB_LEN_MAX); 678c80476e4SDavid E. O'Brien if ((*pat & M_MASK) == M_RNG) { 67945e5710bSMark Peek __Char wc2; 68023338178SMark Peek 68123338178SMark Peek pat++; 68245e5710bSMark Peek pat += One_Char_mbtowc(&wc2, pat, MB_LEN_MAX); 68323338178SMark Peek if (globcharcoll(wc, wk, 0) <= 0 && 68423338178SMark Peek globcharcoll(wk, wc2, 0) <= 0) 685c80476e4SDavid E. O'Brien ok = 1; 68623338178SMark Peek } else if (wc == wk) 687c80476e4SDavid E. O'Brien ok = 1; 688c80476e4SDavid E. O'Brien } 68945e5710bSMark Peek pat += One_Char_mbtowc(&wc, pat, MB_LEN_MAX); 690c80476e4SDavid E. O'Brien if (ok == negate_range) 691c80476e4SDavid E. O'Brien return (0); 692c80476e4SDavid E. O'Brien break; 693c80476e4SDavid E. O'Brien default: 69423338178SMark Peek name += lwk; 69523338178SMark Peek if (samecase(wk) != samecase(wc)) 696c80476e4SDavid E. O'Brien return (0); 697c80476e4SDavid E. O'Brien break; 698c80476e4SDavid E. O'Brien } 699c80476e4SDavid E. O'Brien } 700c80476e4SDavid E. O'Brien return (*name == EOS); 701c80476e4SDavid E. O'Brien } 702c80476e4SDavid E. O'Brien 703c80476e4SDavid E. O'Brien /* free allocated data belonging to a glob_t structure */ 704c80476e4SDavid E. O'Brien void 70545e5710bSMark Peek globfree(glob_t *pglob) 706c80476e4SDavid E. O'Brien { 70723338178SMark Peek int i; 70823338178SMark Peek char **pp; 709c80476e4SDavid E. O'Brien 710c80476e4SDavid E. O'Brien if (pglob->gl_pathv != NULL) { 711c80476e4SDavid E. O'Brien pp = pglob->gl_pathv + pglob->gl_offs; 712c80476e4SDavid E. O'Brien for (i = pglob->gl_pathc; i--; ++pp) 713c80476e4SDavid E. O'Brien if (*pp) 71445e5710bSMark Peek xfree(*pp), *pp = NULL; 71545e5710bSMark Peek xfree(pglob->gl_pathv), pglob->gl_pathv = NULL; 716c80476e4SDavid E. O'Brien } 717c80476e4SDavid E. O'Brien } 718