1240075Sdes/* $OpenBSD: glob.c,v 1.38 2011/09/22 06:27:29 djm Exp $ */ 298937Sdes/* 398937Sdes * Copyright (c) 1989, 1993 498937Sdes * The Regents of the University of California. All rights reserved. 598937Sdes * 698937Sdes * This code is derived from software contributed to Berkeley by 798937Sdes * Guido van Rossum. 898937Sdes * 998937Sdes * Redistribution and use in source and binary forms, with or without 1098937Sdes * modification, are permitted provided that the following conditions 1198937Sdes * are met: 1298937Sdes * 1. Redistributions of source code must retain the above copyright 1398937Sdes * notice, this list of conditions and the following disclaimer. 1498937Sdes * 2. Redistributions in binary form must reproduce the above copyright 1598937Sdes * notice, this list of conditions and the following disclaimer in the 1698937Sdes * documentation and/or other materials provided with the distribution. 17124208Sdes * 3. Neither the name of the University nor the names of its contributors 1898937Sdes * may be used to endorse or promote products derived from this software 1998937Sdes * without specific prior written permission. 2098937Sdes * 2198937Sdes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2298937Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2398937Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2498937Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2598937Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2698937Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2798937Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2898937Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2998937Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3098937Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3198937Sdes * SUCH DAMAGE. 3298937Sdes */ 3398937Sdes 34157016Sdes/* OPENBSD ORIGINAL: lib/libc/gen/glob.c */ 35157016Sdes 3698937Sdes/* 3798937Sdes * glob(3) -- a superset of the one defined in POSIX 1003.2. 3898937Sdes * 3998937Sdes * The [!...] convention to negate a range is supported (SysV, Posix, ksh). 4098937Sdes * 4198937Sdes * Optional extra services, controlled by flags not defined by POSIX: 4298937Sdes * 4398937Sdes * GLOB_QUOTE: 4498937Sdes * Escaping convention: \ inhibits any special meaning the following 4598937Sdes * character might have (except \ at end of string is retained). 4698937Sdes * GLOB_MAGCHAR: 4798937Sdes * Set in gl_flags if pattern contained a globbing character. 4898937Sdes * GLOB_NOMAGIC: 4998937Sdes * Same as GLOB_NOCHECK, but it will only append pattern if it did 5098937Sdes * not contain any magic characters. [Used in csh style globbing] 5198937Sdes * GLOB_ALTDIRFUNC: 5298937Sdes * Use alternately specified directory access functions. 5398937Sdes * GLOB_TILDE: 5498937Sdes * expand ~user/foo to the /home/dir/of/user/foo 5598937Sdes * GLOB_BRACE: 5698937Sdes * expand {1,2}{a,b} to 1a 1b 2a 2b 5798937Sdes * gl_matchc: 5898937Sdes * Number of matches in the current invocation of glob. 5998937Sdes */ 6098937Sdes 61221420Sdes#include "includes.h" 62296781Sdes#include "glob.h" 6398937Sdes 64221420Sdes#include <sys/types.h> 65221420Sdes#include <sys/stat.h> 66221420Sdes 67221420Sdes#include <dirent.h> 68221420Sdes#include <ctype.h> 69221420Sdes#include <errno.h> 70240075Sdes#include <limits.h> 71221420Sdes#include <pwd.h> 72221420Sdes#include <stdlib.h> 73221420Sdes#include <string.h> 74221420Sdes#include <unistd.h> 75221420Sdes 76221420Sdes#if !defined(HAVE_GLOB) || !defined(GLOB_HAS_ALTDIRFUNC) || \ 77221420Sdes !defined(GLOB_HAS_GL_MATCHC) || !defined(GLOB_HAS_GL_STATV) || \ 78221420Sdes !defined(HAVE_DECL_GLOB_NOMATCH) || HAVE_DECL_GLOB_NOMATCH == 0 || \ 79221420Sdes defined(BROKEN_GLOB) 80221420Sdes 81221420Sdes#include "charclass.h" 82221420Sdes 8398937Sdes#define DOLLAR '$' 8498937Sdes#define DOT '.' 8598937Sdes#define EOS '\0' 8698937Sdes#define LBRACKET '[' 8798937Sdes#define NOT '!' 8898937Sdes#define QUESTION '?' 8998937Sdes#define QUOTE '\\' 9098937Sdes#define RANGE '-' 9198937Sdes#define RBRACKET ']' 9298937Sdes#define SEP '/' 9398937Sdes#define STAR '*' 9498937Sdes#define TILDE '~' 9598937Sdes#define UNDERSCORE '_' 9698937Sdes#define LBRACE '{' 9798937Sdes#define RBRACE '}' 9898937Sdes#define SLASH '/' 9998937Sdes#define COMMA ',' 10098937Sdes 10198937Sdes#ifndef DEBUG 10298937Sdes 10398937Sdes#define M_QUOTE 0x8000 10498937Sdes#define M_PROTECT 0x4000 10598937Sdes#define M_MASK 0xffff 10698937Sdes#define M_ASCII 0x00ff 10798937Sdes 10898937Sdestypedef u_short Char; 10998937Sdes 11098937Sdes#else 11198937Sdes 11298937Sdes#define M_QUOTE 0x80 11398937Sdes#define M_PROTECT 0x40 11498937Sdes#define M_MASK 0xff 11598937Sdes#define M_ASCII 0x7f 11698937Sdes 11798937Sdestypedef char Char; 11898937Sdes 11998937Sdes#endif 12098937Sdes 12198937Sdes 12298937Sdes#define CHAR(c) ((Char)((c)&M_ASCII)) 12398937Sdes#define META(c) ((Char)((c)|M_QUOTE)) 12498937Sdes#define M_ALL META('*') 12598937Sdes#define M_END META(']') 12698937Sdes#define M_NOT META('!') 12798937Sdes#define M_ONE META('?') 12898937Sdes#define M_RNG META('-') 12998937Sdes#define M_SET META('[') 130221420Sdes#define M_CLASS META(':') 13198937Sdes#define ismeta(c) (((c)&M_QUOTE) != 0) 13298937Sdes 133221420Sdes#define GLOB_LIMIT_MALLOC 65536 134221420Sdes#define GLOB_LIMIT_STAT 128 135221420Sdes#define GLOB_LIMIT_READDIR 16384 13698937Sdes 137240075Sdes/* Limit of recursion during matching attempts. */ 138240075Sdes#define GLOB_LIMIT_RECUR 64 139240075Sdes 140221420Sdesstruct glob_lim { 141221420Sdes size_t glim_malloc; 142221420Sdes size_t glim_stat; 143221420Sdes size_t glim_readdir; 144221420Sdes}; 145221420Sdes 146240075Sdesstruct glob_path_stat { 147240075Sdes char *gps_path; 148240075Sdes struct stat *gps_stat; 149240075Sdes}; 150240075Sdes 151106121Sdesstatic int compare(const void *, const void *); 152240075Sdesstatic int compare_gps(const void *, const void *); 153106121Sdesstatic int g_Ctoc(const Char *, char *, u_int); 154106121Sdesstatic int g_lstat(Char *, struct stat *, glob_t *); 155106121Sdesstatic DIR *g_opendir(Char *, glob_t *); 156221420Sdesstatic Char *g_strchr(const Char *, int); 157221420Sdesstatic int g_strncmp(const Char *, const char *, size_t); 158106121Sdesstatic int g_stat(Char *, struct stat *, glob_t *); 159221420Sdesstatic int glob0(const Char *, glob_t *, struct glob_lim *); 160221420Sdesstatic int glob1(Char *, Char *, glob_t *, struct glob_lim *); 161106121Sdesstatic int glob2(Char *, Char *, Char *, Char *, Char *, Char *, 162221420Sdes glob_t *, struct glob_lim *); 163181111Sdesstatic int glob3(Char *, Char *, Char *, Char *, Char *, 164221420Sdes Char *, Char *, glob_t *, struct glob_lim *); 165221420Sdesstatic int globextend(const Char *, glob_t *, struct glob_lim *, 166221420Sdes struct stat *); 16798937Sdesstatic const Char * 168106121Sdes globtilde(const Char *, Char *, size_t, glob_t *); 169221420Sdesstatic int globexp1(const Char *, glob_t *, struct glob_lim *); 170221420Sdesstatic int globexp2(const Char *, const Char *, glob_t *, 171221420Sdes struct glob_lim *); 172240075Sdesstatic int match(Char *, Char *, Char *, int); 17398937Sdes#ifdef DEBUG 174106121Sdesstatic void qprintf(const char *, Char *); 17598937Sdes#endif 17698937Sdes 17798937Sdesint 178157016Sdesglob(const char *pattern, int flags, int (*errfunc)(const char *, int), 179157016Sdes glob_t *pglob) 18098937Sdes{ 18198937Sdes const u_char *patnext; 18298937Sdes int c; 18398937Sdes Char *bufnext, *bufend, patbuf[MAXPATHLEN]; 184221420Sdes struct glob_lim limit = { 0, 0, 0 }; 18598937Sdes 186240075Sdes if (strnlen(pattern, PATH_MAX) == PATH_MAX) 187240075Sdes return(GLOB_NOMATCH); 188240075Sdes 18998937Sdes patnext = (u_char *) pattern; 19098937Sdes if (!(flags & GLOB_APPEND)) { 19198937Sdes pglob->gl_pathc = 0; 19298937Sdes pglob->gl_pathv = NULL; 193221420Sdes pglob->gl_statv = NULL; 19498937Sdes if (!(flags & GLOB_DOOFFS)) 19598937Sdes pglob->gl_offs = 0; 19698937Sdes } 19798937Sdes pglob->gl_flags = flags & ~GLOB_MAGCHAR; 19898937Sdes pglob->gl_errfunc = errfunc; 19998937Sdes pglob->gl_matchc = 0; 20098937Sdes 201221420Sdes if (pglob->gl_offs < 0 || pglob->gl_pathc < 0 || 202221420Sdes pglob->gl_offs >= INT_MAX || pglob->gl_pathc >= INT_MAX || 203221420Sdes pglob->gl_pathc >= INT_MAX - pglob->gl_offs - 1) 204221420Sdes return GLOB_NOSPACE; 205221420Sdes 20698937Sdes bufnext = patbuf; 20798937Sdes bufend = bufnext + MAXPATHLEN - 1; 20898937Sdes if (flags & GLOB_NOESCAPE) 20998937Sdes while (bufnext < bufend && (c = *patnext++) != EOS) 21098937Sdes *bufnext++ = c; 21198937Sdes else { 21298937Sdes /* Protect the quoted characters. */ 21398937Sdes while (bufnext < bufend && (c = *patnext++) != EOS) 21498937Sdes if (c == QUOTE) { 21598937Sdes if ((c = *patnext++) == EOS) { 21698937Sdes c = QUOTE; 21798937Sdes --patnext; 21898937Sdes } 21998937Sdes *bufnext++ = c | M_PROTECT; 22098937Sdes } else 22198937Sdes *bufnext++ = c; 22298937Sdes } 22398937Sdes *bufnext = EOS; 22498937Sdes 22598937Sdes if (flags & GLOB_BRACE) 226221420Sdes return globexp1(patbuf, pglob, &limit); 22798937Sdes else 228221420Sdes return glob0(patbuf, pglob, &limit); 22998937Sdes} 23098937Sdes 23198937Sdes/* 23298937Sdes * Expand recursively a glob {} pattern. When there is no more expansion 23398937Sdes * invoke the standard globbing routine to glob the rest of the magic 23498937Sdes * characters 23598937Sdes */ 23698937Sdesstatic int 237221420Sdesglobexp1(const Char *pattern, glob_t *pglob, struct glob_lim *limitp) 23898937Sdes{ 23998937Sdes const Char* ptr = pattern; 24098937Sdes 24198937Sdes /* Protect a single {}, for find(1), like csh */ 24298937Sdes if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS) 243221420Sdes return glob0(pattern, pglob, limitp); 24498937Sdes 245221420Sdes if ((ptr = (const Char *) g_strchr(ptr, LBRACE)) != NULL) 246221420Sdes return globexp2(ptr, pattern, pglob, limitp); 24798937Sdes 248221420Sdes return glob0(pattern, pglob, limitp); 24998937Sdes} 25098937Sdes 25198937Sdes 25298937Sdes/* 25398937Sdes * Recursive brace globbing helper. Tries to expand a single brace. 25498937Sdes * If it succeeds then it invokes globexp1 with the new pattern. 25598937Sdes * If it fails then it tries to glob the rest of the pattern and returns. 25698937Sdes */ 25798937Sdesstatic int 258221420Sdesglobexp2(const Char *ptr, const Char *pattern, glob_t *pglob, 259221420Sdes struct glob_lim *limitp) 26098937Sdes{ 261221420Sdes int i, rv; 26298937Sdes Char *lm, *ls; 26398937Sdes const Char *pe, *pm, *pl; 26498937Sdes Char patbuf[MAXPATHLEN]; 26598937Sdes 26698937Sdes /* copy part up to the brace */ 26798937Sdes for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++) 26898937Sdes ; 26998937Sdes *lm = EOS; 27098937Sdes ls = lm; 27198937Sdes 27298937Sdes /* Find the balanced brace */ 27398937Sdes for (i = 0, pe = ++ptr; *pe; pe++) 27498937Sdes if (*pe == LBRACKET) { 27598937Sdes /* Ignore everything between [] */ 27698937Sdes for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++) 27798937Sdes ; 27898937Sdes if (*pe == EOS) { 27998937Sdes /* 28098937Sdes * We could not find a matching RBRACKET. 28198937Sdes * Ignore and just look for RBRACE 28298937Sdes */ 28398937Sdes pe = pm; 28498937Sdes } 28598937Sdes } else if (*pe == LBRACE) 28698937Sdes i++; 28798937Sdes else if (*pe == RBRACE) { 28898937Sdes if (i == 0) 28998937Sdes break; 29098937Sdes i--; 29198937Sdes } 29298937Sdes 29398937Sdes /* Non matching braces; just glob the pattern */ 294221420Sdes if (i != 0 || *pe == EOS) 295221420Sdes return glob0(patbuf, pglob, limitp); 29698937Sdes 29798937Sdes for (i = 0, pl = pm = ptr; pm <= pe; pm++) { 29898937Sdes switch (*pm) { 29998937Sdes case LBRACKET: 30098937Sdes /* Ignore everything between [] */ 30198937Sdes for (pl = pm++; *pm != RBRACKET && *pm != EOS; pm++) 30298937Sdes ; 30398937Sdes if (*pm == EOS) { 30498937Sdes /* 30598937Sdes * We could not find a matching RBRACKET. 30698937Sdes * Ignore and just look for RBRACE 30798937Sdes */ 30898937Sdes pm = pl; 30998937Sdes } 31098937Sdes break; 31198937Sdes 31298937Sdes case LBRACE: 31398937Sdes i++; 31498937Sdes break; 31598937Sdes 31698937Sdes case RBRACE: 31798937Sdes if (i) { 31898937Sdes i--; 31998937Sdes break; 32098937Sdes } 32198937Sdes /* FALLTHROUGH */ 32298937Sdes case COMMA: 32398937Sdes if (i && *pm == COMMA) 32498937Sdes break; 32598937Sdes else { 32698937Sdes /* Append the current string */ 32798937Sdes for (lm = ls; (pl < pm); *lm++ = *pl++) 32898937Sdes ; 32998937Sdes 33098937Sdes /* 33198937Sdes * Append the rest of the pattern after the 33298937Sdes * closing brace 33398937Sdes */ 33498937Sdes for (pl = pe + 1; (*lm++ = *pl++) != EOS; ) 33598937Sdes ; 33698937Sdes 33798937Sdes /* Expand the current pattern */ 33898937Sdes#ifdef DEBUG 33998937Sdes qprintf("globexp2:", patbuf); 34098937Sdes#endif 341221420Sdes rv = globexp1(patbuf, pglob, limitp); 342221420Sdes if (rv && rv != GLOB_NOMATCH) 343221420Sdes return rv; 34498937Sdes 34598937Sdes /* move after the comma, to the next string */ 34698937Sdes pl = pm + 1; 34798937Sdes } 34898937Sdes break; 34998937Sdes 35098937Sdes default: 35198937Sdes break; 35298937Sdes } 35398937Sdes } 35498937Sdes return 0; 35598937Sdes} 35698937Sdes 35798937Sdes 35898937Sdes 35998937Sdes/* 36098937Sdes * expand tilde from the passwd file. 36198937Sdes */ 36298937Sdesstatic const Char * 363157016Sdesglobtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t *pglob) 36498937Sdes{ 36598937Sdes struct passwd *pwd; 36698937Sdes char *h; 36798937Sdes const Char *p; 36898937Sdes Char *b, *eb; 36998937Sdes 37098937Sdes if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE)) 37198937Sdes return pattern; 37298937Sdes 37398937Sdes /* Copy up to the end of the string or / */ 37498937Sdes eb = &patbuf[patbuf_len - 1]; 37598937Sdes for (p = pattern + 1, h = (char *) patbuf; 37698937Sdes h < (char *)eb && *p && *p != SLASH; *h++ = *p++) 37798937Sdes ; 37898937Sdes 37998937Sdes *h = EOS; 38098937Sdes 38198937Sdes#if 0 38298937Sdes if (h == (char *)eb) 38398937Sdes return what; 38498937Sdes#endif 38598937Sdes 38698937Sdes if (((char *) patbuf)[0] == EOS) { 38798937Sdes /* 38898937Sdes * handle a plain ~ or ~/ by expanding $HOME 38998937Sdes * first and then trying the password file 39098937Sdes */ 39198937Sdes#if 0 39298937Sdes if (issetugid() != 0 || (h = getenv("HOME")) == NULL) { 39398937Sdes#endif 39498937Sdes if ((getuid() != geteuid()) || (h = getenv("HOME")) == NULL) { 39598937Sdes if ((pwd = getpwuid(getuid())) == NULL) 39698937Sdes return pattern; 39798937Sdes else 39898937Sdes h = pwd->pw_dir; 39998937Sdes } 40098937Sdes } else { 40198937Sdes /* 40298937Sdes * Expand a ~user 40398937Sdes */ 40498937Sdes if ((pwd = getpwnam((char*) patbuf)) == NULL) 40598937Sdes return pattern; 40698937Sdes else 40798937Sdes h = pwd->pw_dir; 40898937Sdes } 40998937Sdes 41098937Sdes /* Copy the home directory */ 41198937Sdes for (b = patbuf; b < eb && *h; *b++ = *h++) 41298937Sdes ; 41398937Sdes 41498937Sdes /* Append the rest of the pattern */ 41598937Sdes while (b < eb && (*b++ = *p++) != EOS) 41698937Sdes ; 41798937Sdes *b = EOS; 41898937Sdes 41998937Sdes return patbuf; 42098937Sdes} 42198937Sdes 422221420Sdesstatic int 423221420Sdesg_strncmp(const Char *s1, const char *s2, size_t n) 424221420Sdes{ 425221420Sdes int rv = 0; 42698937Sdes 427221420Sdes while (n--) { 428221420Sdes rv = *(Char *)s1 - *(const unsigned char *)s2++; 429221420Sdes if (rv) 430221420Sdes break; 431221420Sdes if (*s1++ == '\0') 432221420Sdes break; 433221420Sdes } 434221420Sdes return rv; 435221420Sdes} 436221420Sdes 437221420Sdesstatic int 438221420Sdesg_charclass(const Char **patternp, Char **bufnextp) 439221420Sdes{ 440221420Sdes const Char *pattern = *patternp + 1; 441221420Sdes Char *bufnext = *bufnextp; 442221420Sdes const Char *colon; 443221420Sdes struct cclass *cc; 444221420Sdes size_t len; 445221420Sdes 446221420Sdes if ((colon = g_strchr(pattern, ':')) == NULL || colon[1] != ']') 447221420Sdes return 1; /* not a character class */ 448221420Sdes 449221420Sdes len = (size_t)(colon - pattern); 450221420Sdes for (cc = cclasses; cc->name != NULL; cc++) { 451221420Sdes if (!g_strncmp(pattern, cc->name, len) && cc->name[len] == '\0') 452221420Sdes break; 453221420Sdes } 454221420Sdes if (cc->name == NULL) 455221420Sdes return -1; /* invalid character class */ 456221420Sdes *bufnext++ = M_CLASS; 457221420Sdes *bufnext++ = (Char)(cc - &cclasses[0]); 458221420Sdes *bufnextp = bufnext; 459221420Sdes *patternp += len + 3; 460221420Sdes 461221420Sdes return 0; 462221420Sdes} 463221420Sdes 46498937Sdes/* 46598937Sdes * The main glob() routine: compiles the pattern (optionally processing 46698937Sdes * quotes), calls glob1() to do the real pattern matching, and finally 46798937Sdes * sorts the list (unless unsorted operation is requested). Returns 0 46898937Sdes * if things went well, nonzero if errors occurred. It is not an error 46998937Sdes * to find no matches. 47098937Sdes */ 47198937Sdesstatic int 472221420Sdesglob0(const Char *pattern, glob_t *pglob, struct glob_lim *limitp) 47398937Sdes{ 47498937Sdes const Char *qpatnext; 47598937Sdes int c, err, oldpathc; 47698937Sdes Char *bufnext, patbuf[MAXPATHLEN]; 47798937Sdes 47898937Sdes qpatnext = globtilde(pattern, patbuf, MAXPATHLEN, pglob); 47998937Sdes oldpathc = pglob->gl_pathc; 48098937Sdes bufnext = patbuf; 48198937Sdes 48298937Sdes /* We don't need to check for buffer overflow any more. */ 48398937Sdes while ((c = *qpatnext++) != EOS) { 48498937Sdes switch (c) { 48598937Sdes case LBRACKET: 48698937Sdes c = *qpatnext; 48798937Sdes if (c == NOT) 48898937Sdes ++qpatnext; 48998937Sdes if (*qpatnext == EOS || 490221420Sdes g_strchr(qpatnext+1, RBRACKET) == NULL) { 49198937Sdes *bufnext++ = LBRACKET; 49298937Sdes if (c == NOT) 49398937Sdes --qpatnext; 49498937Sdes break; 49598937Sdes } 49698937Sdes *bufnext++ = M_SET; 49798937Sdes if (c == NOT) 49898937Sdes *bufnext++ = M_NOT; 49998937Sdes c = *qpatnext++; 50098937Sdes do { 501221420Sdes if (c == LBRACKET && *qpatnext == ':') { 502221420Sdes do { 503221420Sdes err = g_charclass(&qpatnext, 504221420Sdes &bufnext); 505221420Sdes if (err) 506221420Sdes break; 507221420Sdes c = *qpatnext++; 508221420Sdes } while (c == LBRACKET && *qpatnext == ':'); 509221420Sdes if (err == -1 && 510221420Sdes !(pglob->gl_flags & GLOB_NOCHECK)) 511221420Sdes return GLOB_NOMATCH; 512221420Sdes if (c == RBRACKET) 513221420Sdes break; 514221420Sdes } 51598937Sdes *bufnext++ = CHAR(c); 51698937Sdes if (*qpatnext == RANGE && 51798937Sdes (c = qpatnext[1]) != RBRACKET) { 51898937Sdes *bufnext++ = M_RNG; 51998937Sdes *bufnext++ = CHAR(c); 52098937Sdes qpatnext += 2; 52198937Sdes } 52298937Sdes } while ((c = *qpatnext++) != RBRACKET); 52398937Sdes pglob->gl_flags |= GLOB_MAGCHAR; 52498937Sdes *bufnext++ = M_END; 52598937Sdes break; 52698937Sdes case QUESTION: 52798937Sdes pglob->gl_flags |= GLOB_MAGCHAR; 52898937Sdes *bufnext++ = M_ONE; 52998937Sdes break; 53098937Sdes case STAR: 53198937Sdes pglob->gl_flags |= GLOB_MAGCHAR; 53298937Sdes /* collapse adjacent stars to one, 53398937Sdes * to avoid exponential behavior 53498937Sdes */ 53598937Sdes if (bufnext == patbuf || bufnext[-1] != M_ALL) 53698937Sdes *bufnext++ = M_ALL; 53798937Sdes break; 53898937Sdes default: 53998937Sdes *bufnext++ = CHAR(c); 54098937Sdes break; 54198937Sdes } 54298937Sdes } 54398937Sdes *bufnext = EOS; 54498937Sdes#ifdef DEBUG 54598937Sdes qprintf("glob0:", patbuf); 54698937Sdes#endif 54798937Sdes 548221420Sdes if ((err = glob1(patbuf, patbuf+MAXPATHLEN-1, pglob, limitp)) != 0) 54998937Sdes return(err); 55098937Sdes 55198937Sdes /* 55298937Sdes * If there was no match we are going to append the pattern 55398937Sdes * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified 55498937Sdes * and the pattern did not contain any magic characters 55598937Sdes * GLOB_NOMAGIC is there just for compatibility with csh. 55698937Sdes */ 55798937Sdes if (pglob->gl_pathc == oldpathc) { 55898937Sdes if ((pglob->gl_flags & GLOB_NOCHECK) || 55998937Sdes ((pglob->gl_flags & GLOB_NOMAGIC) && 56098937Sdes !(pglob->gl_flags & GLOB_MAGCHAR))) 561221420Sdes return(globextend(pattern, pglob, limitp, NULL)); 56298937Sdes else 56398937Sdes return(GLOB_NOMATCH); 56498937Sdes } 565240075Sdes if (!(pglob->gl_flags & GLOB_NOSORT)) { 566240075Sdes if ((pglob->gl_flags & GLOB_KEEPSTAT)) { 567240075Sdes /* Keep the paths and stat info synced during sort */ 568240075Sdes struct glob_path_stat *path_stat; 569240075Sdes int i; 570240075Sdes int n = pglob->gl_pathc - oldpathc; 571240075Sdes int o = pglob->gl_offs + oldpathc; 572240075Sdes 573240075Sdes if ((path_stat = calloc(n, sizeof(*path_stat))) == NULL) 574240075Sdes return GLOB_NOSPACE; 575240075Sdes for (i = 0; i < n; i++) { 576240075Sdes path_stat[i].gps_path = pglob->gl_pathv[o + i]; 577240075Sdes path_stat[i].gps_stat = pglob->gl_statv[o + i]; 578240075Sdes } 579240075Sdes qsort(path_stat, n, sizeof(*path_stat), compare_gps); 580240075Sdes for (i = 0; i < n; i++) { 581240075Sdes pglob->gl_pathv[o + i] = path_stat[i].gps_path; 582240075Sdes pglob->gl_statv[o + i] = path_stat[i].gps_stat; 583240075Sdes } 584240075Sdes free(path_stat); 585240075Sdes } else { 586240075Sdes qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc, 587240075Sdes pglob->gl_pathc - oldpathc, sizeof(char *), 588240075Sdes compare); 589240075Sdes } 590240075Sdes } 59198937Sdes return(0); 59298937Sdes} 59398937Sdes 59498937Sdesstatic int 595157016Sdescompare(const void *p, const void *q) 59698937Sdes{ 59798937Sdes return(strcmp(*(char **)p, *(char **)q)); 59898937Sdes} 59998937Sdes 60098937Sdesstatic int 601240075Sdescompare_gps(const void *_p, const void *_q) 602240075Sdes{ 603240075Sdes const struct glob_path_stat *p = (const struct glob_path_stat *)_p; 604240075Sdes const struct glob_path_stat *q = (const struct glob_path_stat *)_q; 605240075Sdes 606240075Sdes return(strcmp(p->gps_path, q->gps_path)); 607240075Sdes} 608240075Sdes 609240075Sdesstatic int 610221420Sdesglob1(Char *pattern, Char *pattern_last, glob_t *pglob, struct glob_lim *limitp) 61198937Sdes{ 61298937Sdes Char pathbuf[MAXPATHLEN]; 61398937Sdes 61498937Sdes /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */ 61598937Sdes if (*pattern == EOS) 61698937Sdes return(0); 61798937Sdes return(glob2(pathbuf, pathbuf+MAXPATHLEN-1, 61898937Sdes pathbuf, pathbuf+MAXPATHLEN-1, 61998937Sdes pattern, pattern_last, pglob, limitp)); 62098937Sdes} 62198937Sdes 62298937Sdes/* 62398937Sdes * The functions glob2 and glob3 are mutually recursive; there is one level 62498937Sdes * of recursion for each segment in the pattern that contains one or more 62598937Sdes * meta characters. 62698937Sdes */ 62798937Sdesstatic int 628157016Sdesglob2(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last, 629221420Sdes Char *pattern, Char *pattern_last, glob_t *pglob, struct glob_lim *limitp) 63098937Sdes{ 63198937Sdes struct stat sb; 63298937Sdes Char *p, *q; 63398937Sdes int anymeta; 63498937Sdes 63598937Sdes /* 63698937Sdes * Loop over pattern segments until end of pattern or until 63798937Sdes * segment with meta character found. 63898937Sdes */ 63998937Sdes for (anymeta = 0;;) { 64098937Sdes if (*pattern == EOS) { /* End of pattern? */ 64198937Sdes *pathend = EOS; 64298937Sdes if (g_lstat(pathbuf, &sb, pglob)) 64398937Sdes return(0); 64498937Sdes 645221420Sdes if ((pglob->gl_flags & GLOB_LIMIT) && 646221420Sdes limitp->glim_stat++ >= GLOB_LIMIT_STAT) { 647221420Sdes errno = 0; 648221420Sdes *pathend++ = SEP; 649221420Sdes *pathend = EOS; 650221420Sdes return(GLOB_NOSPACE); 651221420Sdes } 652221420Sdes 65398937Sdes if (((pglob->gl_flags & GLOB_MARK) && 65498937Sdes pathend[-1] != SEP) && (S_ISDIR(sb.st_mode) || 65598937Sdes (S_ISLNK(sb.st_mode) && 65698937Sdes (g_stat(pathbuf, &sb, pglob) == 0) && 65798937Sdes S_ISDIR(sb.st_mode)))) { 65898937Sdes if (pathend+1 > pathend_last) 65998937Sdes return (1); 66098937Sdes *pathend++ = SEP; 66198937Sdes *pathend = EOS; 66298937Sdes } 66398937Sdes ++pglob->gl_matchc; 664221420Sdes return(globextend(pathbuf, pglob, limitp, &sb)); 66598937Sdes } 66698937Sdes 66798937Sdes /* Find end of next segment, copy tentatively to pathend. */ 66898937Sdes q = pathend; 66998937Sdes p = pattern; 67098937Sdes while (*p != EOS && *p != SEP) { 67198937Sdes if (ismeta(*p)) 67298937Sdes anymeta = 1; 67398937Sdes if (q+1 > pathend_last) 67498937Sdes return (1); 67598937Sdes *q++ = *p++; 67698937Sdes } 67798937Sdes 67898937Sdes if (!anymeta) { /* No expansion, do next segment. */ 67998937Sdes pathend = q; 68098937Sdes pattern = p; 68198937Sdes while (*pattern == SEP) { 68298937Sdes if (pathend+1 > pathend_last) 68398937Sdes return (1); 68498937Sdes *pathend++ = *pattern++; 68598937Sdes } 68698937Sdes } else 68798937Sdes /* Need expansion, recurse. */ 68898937Sdes return(glob3(pathbuf, pathbuf_last, pathend, 689181111Sdes pathend_last, pattern, p, pattern_last, 690181111Sdes pglob, limitp)); 69198937Sdes } 69298937Sdes /* NOTREACHED */ 69398937Sdes} 69498937Sdes 69598937Sdesstatic int 696157016Sdesglob3(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last, 697181111Sdes Char *pattern, Char *restpattern, Char *restpattern_last, glob_t *pglob, 698221420Sdes struct glob_lim *limitp) 69998937Sdes{ 700157016Sdes struct dirent *dp; 70198937Sdes DIR *dirp; 70298937Sdes int err; 70398937Sdes char buf[MAXPATHLEN]; 70498937Sdes 70598937Sdes /* 70698937Sdes * The readdirfunc declaration can't be prototyped, because it is 70798937Sdes * assigned, below, to two functions which are prototyped in glob.h 70898937Sdes * and dirent.h as taking pointers to differently typed opaque 70998937Sdes * structures. 71098937Sdes */ 711124208Sdes struct dirent *(*readdirfunc)(void *); 71298937Sdes 71398937Sdes if (pathend > pathend_last) 71498937Sdes return (1); 71598937Sdes *pathend = EOS; 71698937Sdes errno = 0; 71798937Sdes 71898937Sdes if ((dirp = g_opendir(pathbuf, pglob)) == NULL) { 71998937Sdes /* TODO: don't call for ENOENT or ENOTDIR? */ 72098937Sdes if (pglob->gl_errfunc) { 72198937Sdes if (g_Ctoc(pathbuf, buf, sizeof(buf))) 72298937Sdes return(GLOB_ABORTED); 72398937Sdes if (pglob->gl_errfunc(buf, errno) || 72498937Sdes pglob->gl_flags & GLOB_ERR) 72598937Sdes return(GLOB_ABORTED); 72698937Sdes } 72798937Sdes return(0); 72898937Sdes } 72998937Sdes 73098937Sdes err = 0; 73198937Sdes 73298937Sdes /* Search directory for matching names. */ 73398937Sdes if (pglob->gl_flags & GLOB_ALTDIRFUNC) 73498937Sdes readdirfunc = pglob->gl_readdir; 73598937Sdes else 736124208Sdes readdirfunc = (struct dirent *(*)(void *))readdir; 73798937Sdes while ((dp = (*readdirfunc)(dirp))) { 738157016Sdes u_char *sc; 739157016Sdes Char *dc; 74098937Sdes 741221420Sdes if ((pglob->gl_flags & GLOB_LIMIT) && 742221420Sdes limitp->glim_readdir++ >= GLOB_LIMIT_READDIR) { 743221420Sdes errno = 0; 744221420Sdes *pathend++ = SEP; 745221420Sdes *pathend = EOS; 746240075Sdes err = GLOB_NOSPACE; 747240075Sdes break; 748221420Sdes } 749221420Sdes 75098937Sdes /* Initial DOT must be matched literally. */ 75198937Sdes if (dp->d_name[0] == DOT && *pattern != DOT) 75298937Sdes continue; 75398937Sdes dc = pathend; 75498937Sdes sc = (u_char *) dp->d_name; 75598937Sdes while (dc < pathend_last && (*dc++ = *sc++) != EOS) 75698937Sdes ; 75798937Sdes if (dc >= pathend_last) { 75898937Sdes *dc = EOS; 75998937Sdes err = 1; 76098937Sdes break; 76198937Sdes } 76298937Sdes 763240075Sdes if (!match(pathend, pattern, restpattern, GLOB_LIMIT_RECUR)) { 76498937Sdes *pathend = EOS; 76598937Sdes continue; 76698937Sdes } 76798937Sdes err = glob2(pathbuf, pathbuf_last, --dc, pathend_last, 76898937Sdes restpattern, restpattern_last, pglob, limitp); 76998937Sdes if (err) 77098937Sdes break; 77198937Sdes } 77298937Sdes 77398937Sdes if (pglob->gl_flags & GLOB_ALTDIRFUNC) 77498937Sdes (*pglob->gl_closedir)(dirp); 77598937Sdes else 77698937Sdes closedir(dirp); 77798937Sdes return(err); 77898937Sdes} 77998937Sdes 78098937Sdes 78198937Sdes/* 782106121Sdes * Extend the gl_pathv member of a glob_t structure to accommodate a new item, 78398937Sdes * add the new item, and update gl_pathc. 78498937Sdes * 78598937Sdes * This assumes the BSD realloc, which only copies the block when its size 78698937Sdes * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic 78798937Sdes * behavior. 78898937Sdes * 78998937Sdes * Return 0 if new item added, error code if memory couldn't be allocated. 79098937Sdes * 79198937Sdes * Invariant of the glob_t structure: 79298937Sdes * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and 79398937Sdes * gl_pathv points to (gl_offs + gl_pathc + 1) items. 79498937Sdes */ 79598937Sdesstatic int 796221420Sdesglobextend(const Char *path, glob_t *pglob, struct glob_lim *limitp, 797221420Sdes struct stat *sb) 79898937Sdes{ 799157016Sdes char **pathv; 800221420Sdes ssize_t i; 801221420Sdes size_t newn, len; 802221420Sdes char *copy = NULL; 80398937Sdes const Char *p; 804221420Sdes struct stat **statv; 80598937Sdes 806221420Sdes newn = 2 + pglob->gl_pathc + pglob->gl_offs; 807221420Sdes if (pglob->gl_offs >= INT_MAX || 808221420Sdes pglob->gl_pathc >= INT_MAX || 809221420Sdes newn >= INT_MAX || 810221420Sdes SIZE_MAX / sizeof(*pathv) <= newn || 811221420Sdes SIZE_MAX / sizeof(*statv) <= newn) { 812221420Sdes nospace: 813221420Sdes for (i = pglob->gl_offs; i < (ssize_t)(newn - 2); i++) { 814221420Sdes if (pglob->gl_pathv && pglob->gl_pathv[i]) 815221420Sdes free(pglob->gl_pathv[i]); 816221420Sdes if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0 && 817221420Sdes pglob->gl_pathv && pglob->gl_pathv[i]) 818221420Sdes free(pglob->gl_statv[i]); 819221420Sdes } 82098937Sdes if (pglob->gl_pathv) { 82198937Sdes free(pglob->gl_pathv); 82298937Sdes pglob->gl_pathv = NULL; 82398937Sdes } 824221420Sdes if (pglob->gl_statv) { 825221420Sdes free(pglob->gl_statv); 826221420Sdes pglob->gl_statv = NULL; 827221420Sdes } 82898937Sdes return(GLOB_NOSPACE); 82998937Sdes } 83098937Sdes 831221420Sdes pathv = realloc(pglob->gl_pathv, newn * sizeof(*pathv)); 832221420Sdes if (pathv == NULL) 833221420Sdes goto nospace; 83498937Sdes if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) { 83598937Sdes /* first time around -- clear initial gl_offs items */ 83698937Sdes pathv += pglob->gl_offs; 83798937Sdes for (i = pglob->gl_offs; --i >= 0; ) 83898937Sdes *--pathv = NULL; 83998937Sdes } 84098937Sdes pglob->gl_pathv = pathv; 84198937Sdes 842221420Sdes if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0) { 843221420Sdes statv = realloc(pglob->gl_statv, newn * sizeof(*statv)); 844221420Sdes if (statv == NULL) 845221420Sdes goto nospace; 846221420Sdes if (pglob->gl_statv == NULL && pglob->gl_offs > 0) { 847221420Sdes /* first time around -- clear initial gl_offs items */ 848221420Sdes statv += pglob->gl_offs; 849221420Sdes for (i = pglob->gl_offs; --i >= 0; ) 850221420Sdes *--statv = NULL; 851221420Sdes } 852221420Sdes pglob->gl_statv = statv; 853221420Sdes if (sb == NULL) 854221420Sdes statv[pglob->gl_offs + pglob->gl_pathc] = NULL; 855221420Sdes else { 856221420Sdes limitp->glim_malloc += sizeof(**statv); 857221420Sdes if ((pglob->gl_flags & GLOB_LIMIT) && 858221420Sdes limitp->glim_malloc >= GLOB_LIMIT_MALLOC) { 859221420Sdes errno = 0; 860221420Sdes return(GLOB_NOSPACE); 861221420Sdes } 862221420Sdes if ((statv[pglob->gl_offs + pglob->gl_pathc] = 863221420Sdes malloc(sizeof(**statv))) == NULL) 864221420Sdes goto copy_error; 865221420Sdes memcpy(statv[pglob->gl_offs + pglob->gl_pathc], sb, 866221420Sdes sizeof(*sb)); 867221420Sdes } 868221420Sdes statv[pglob->gl_offs + pglob->gl_pathc + 1] = NULL; 869221420Sdes } 870221420Sdes 87198937Sdes for (p = path; *p++;) 87298937Sdes ; 87398937Sdes len = (size_t)(p - path); 874221420Sdes limitp->glim_malloc += len; 87598937Sdes if ((copy = malloc(len)) != NULL) { 87698937Sdes if (g_Ctoc(path, copy, len)) { 87798937Sdes free(copy); 87898937Sdes return(GLOB_NOSPACE); 87998937Sdes } 88098937Sdes pathv[pglob->gl_offs + pglob->gl_pathc++] = copy; 88198937Sdes } 88298937Sdes pathv[pglob->gl_offs + pglob->gl_pathc] = NULL; 88398937Sdes 88498937Sdes if ((pglob->gl_flags & GLOB_LIMIT) && 885221420Sdes (newn * sizeof(*pathv)) + limitp->glim_malloc > 886221420Sdes GLOB_LIMIT_MALLOC) { 88798937Sdes errno = 0; 88898937Sdes return(GLOB_NOSPACE); 88998937Sdes } 890221420Sdes copy_error: 89198937Sdes return(copy == NULL ? GLOB_NOSPACE : 0); 89298937Sdes} 89398937Sdes 89498937Sdes 89598937Sdes/* 89698937Sdes * pattern matching function for filenames. Each occurrence of the * 89798937Sdes * pattern causes a recursion level. 89898937Sdes */ 89998937Sdesstatic int 900240075Sdesmatch(Char *name, Char *pat, Char *patend, int recur) 90198937Sdes{ 90298937Sdes int ok, negate_range; 90398937Sdes Char c, k; 90498937Sdes 905240075Sdes if (recur-- == 0) 906240075Sdes return(GLOB_NOSPACE); 907240075Sdes 90898937Sdes while (pat < patend) { 90998937Sdes c = *pat++; 91098937Sdes switch (c & M_MASK) { 91198937Sdes case M_ALL: 912240075Sdes while (pat < patend && (*pat & M_MASK) == M_ALL) 913240075Sdes pat++; /* eat consecutive '*' */ 91498937Sdes if (pat == patend) 91598937Sdes return(1); 916157016Sdes do { 917240075Sdes if (match(name, pat, patend, recur)) 91898937Sdes return(1); 919157016Sdes } while (*name++ != EOS); 92098937Sdes return(0); 92198937Sdes case M_ONE: 92298937Sdes if (*name++ == EOS) 92398937Sdes return(0); 92498937Sdes break; 92598937Sdes case M_SET: 92698937Sdes ok = 0; 92798937Sdes if ((k = *name++) == EOS) 92898937Sdes return(0); 92998937Sdes if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS) 93098937Sdes ++pat; 931221420Sdes while (((c = *pat++) & M_MASK) != M_END) { 932221420Sdes if ((c & M_MASK) == M_CLASS) { 933221420Sdes Char idx = *pat & M_MASK; 934221420Sdes if (idx < NCCLASSES && 935221420Sdes cclasses[idx].isctype(k)) 936221420Sdes ok = 1; 937221420Sdes ++pat; 938221420Sdes } 93998937Sdes if ((*pat & M_MASK) == M_RNG) { 94098937Sdes if (c <= k && k <= pat[1]) 94198937Sdes ok = 1; 94298937Sdes pat += 2; 94398937Sdes } else if (c == k) 94498937Sdes ok = 1; 945221420Sdes } 94698937Sdes if (ok == negate_range) 94798937Sdes return(0); 94898937Sdes break; 94998937Sdes default: 95098937Sdes if (*name++ != c) 95198937Sdes return(0); 95298937Sdes break; 95398937Sdes } 95498937Sdes } 95598937Sdes return(*name == EOS); 95698937Sdes} 95798937Sdes 95898937Sdes/* Free allocated data belonging to a glob_t structure. */ 95998937Sdesvoid 960157016Sdesglobfree(glob_t *pglob) 96198937Sdes{ 962157016Sdes int i; 963157016Sdes char **pp; 96498937Sdes 96598937Sdes if (pglob->gl_pathv != NULL) { 96698937Sdes pp = pglob->gl_pathv + pglob->gl_offs; 96798937Sdes for (i = pglob->gl_pathc; i--; ++pp) 96898937Sdes if (*pp) 96998937Sdes free(*pp); 97098937Sdes free(pglob->gl_pathv); 97198937Sdes pglob->gl_pathv = NULL; 97298937Sdes } 973221420Sdes if (pglob->gl_statv != NULL) { 974221420Sdes for (i = 0; i < pglob->gl_pathc; i++) { 975221420Sdes if (pglob->gl_statv[i] != NULL) 976221420Sdes free(pglob->gl_statv[i]); 977221420Sdes } 978221420Sdes free(pglob->gl_statv); 979221420Sdes pglob->gl_statv = NULL; 980221420Sdes } 98198937Sdes} 98298937Sdes 98398937Sdesstatic DIR * 984157016Sdesg_opendir(Char *str, glob_t *pglob) 98598937Sdes{ 98698937Sdes char buf[MAXPATHLEN]; 98798937Sdes 98898937Sdes if (!*str) 989106121Sdes strlcpy(buf, ".", sizeof buf); 99098937Sdes else { 99198937Sdes if (g_Ctoc(str, buf, sizeof(buf))) 99298937Sdes return(NULL); 99398937Sdes } 99498937Sdes 99598937Sdes if (pglob->gl_flags & GLOB_ALTDIRFUNC) 99698937Sdes return((*pglob->gl_opendir)(buf)); 99798937Sdes 99898937Sdes return(opendir(buf)); 99998937Sdes} 100098937Sdes 100198937Sdesstatic int 1002157016Sdesg_lstat(Char *fn, struct stat *sb, glob_t *pglob) 100398937Sdes{ 100498937Sdes char buf[MAXPATHLEN]; 100598937Sdes 100698937Sdes if (g_Ctoc(fn, buf, sizeof(buf))) 100798937Sdes return(-1); 100898937Sdes if (pglob->gl_flags & GLOB_ALTDIRFUNC) 100998937Sdes return((*pglob->gl_lstat)(buf, sb)); 101098937Sdes return(lstat(buf, sb)); 101198937Sdes} 101298937Sdes 101398937Sdesstatic int 1014157016Sdesg_stat(Char *fn, struct stat *sb, glob_t *pglob) 101598937Sdes{ 101698937Sdes char buf[MAXPATHLEN]; 101798937Sdes 101898937Sdes if (g_Ctoc(fn, buf, sizeof(buf))) 101998937Sdes return(-1); 102098937Sdes if (pglob->gl_flags & GLOB_ALTDIRFUNC) 102198937Sdes return((*pglob->gl_stat)(buf, sb)); 102298937Sdes return(stat(buf, sb)); 102398937Sdes} 102498937Sdes 102598937Sdesstatic Char * 1026221420Sdesg_strchr(const Char *str, int ch) 102798937Sdes{ 102898937Sdes do { 102998937Sdes if (*str == ch) 1030221420Sdes return ((Char *)str); 103198937Sdes } while (*str++); 103298937Sdes return (NULL); 103398937Sdes} 103498937Sdes 103598937Sdesstatic int 1036157016Sdesg_Ctoc(const Char *str, char *buf, u_int len) 103798937Sdes{ 103898937Sdes 103998937Sdes while (len--) { 104098937Sdes if ((*buf++ = *str++) == EOS) 104198937Sdes return (0); 104298937Sdes } 104398937Sdes return (1); 104498937Sdes} 104598937Sdes 104698937Sdes#ifdef DEBUG 104798937Sdesstatic void 1048157016Sdesqprintf(const char *str, Char *s) 104998937Sdes{ 1050157016Sdes Char *p; 105198937Sdes 105298937Sdes (void)printf("%s:\n", str); 105398937Sdes for (p = s; *p; p++) 105498937Sdes (void)printf("%c", CHAR(*p)); 105598937Sdes (void)printf("\n"); 105698937Sdes for (p = s; *p; p++) 105798937Sdes (void)printf("%c", *p & M_PROTECT ? '"' : ' '); 105898937Sdes (void)printf("\n"); 105998937Sdes for (p = s; *p; p++) 106098937Sdes (void)printf("%c", ismeta(*p) ? '_' : ' '); 106198937Sdes (void)printf("\n"); 106298937Sdes} 106398937Sdes#endif 106498937Sdes 106598937Sdes#endif /* !defined(HAVE_GLOB) || !defined(GLOB_HAS_ALTDIRFUNC) || 1066221420Sdes !defined(GLOB_HAS_GL_MATCHC) || !defined(GLOB_HAS_GL_STATV) */ 1067