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" 6298937Sdes 63221420Sdes#include <sys/types.h> 64221420Sdes#include <sys/stat.h> 65221420Sdes 66221420Sdes#include <dirent.h> 67221420Sdes#include <ctype.h> 68221420Sdes#include <errno.h> 69240075Sdes#include <limits.h> 70221420Sdes#include <pwd.h> 71221420Sdes#include <stdlib.h> 72221420Sdes#include <string.h> 73221420Sdes#include <unistd.h> 74221420Sdes 75221420Sdes#if !defined(HAVE_GLOB) || !defined(GLOB_HAS_ALTDIRFUNC) || \ 76221420Sdes !defined(GLOB_HAS_GL_MATCHC) || !defined(GLOB_HAS_GL_STATV) || \ 77221420Sdes !defined(HAVE_DECL_GLOB_NOMATCH) || HAVE_DECL_GLOB_NOMATCH == 0 || \ 78221420Sdes defined(BROKEN_GLOB) 79221420Sdes 80221420Sdes#include "charclass.h" 81221420Sdes 8298937Sdes#define DOLLAR '$' 8398937Sdes#define DOT '.' 8498937Sdes#define EOS '\0' 8598937Sdes#define LBRACKET '[' 8698937Sdes#define NOT '!' 8798937Sdes#define QUESTION '?' 8898937Sdes#define QUOTE '\\' 8998937Sdes#define RANGE '-' 9098937Sdes#define RBRACKET ']' 9198937Sdes#define SEP '/' 9298937Sdes#define STAR '*' 9398937Sdes#define TILDE '~' 9498937Sdes#define UNDERSCORE '_' 9598937Sdes#define LBRACE '{' 9698937Sdes#define RBRACE '}' 9798937Sdes#define SLASH '/' 9898937Sdes#define COMMA ',' 9998937Sdes 10098937Sdes#ifndef DEBUG 10198937Sdes 10298937Sdes#define M_QUOTE 0x8000 10398937Sdes#define M_PROTECT 0x4000 10498937Sdes#define M_MASK 0xffff 10598937Sdes#define M_ASCII 0x00ff 10698937Sdes 10798937Sdestypedef u_short Char; 10898937Sdes 10998937Sdes#else 11098937Sdes 11198937Sdes#define M_QUOTE 0x80 11298937Sdes#define M_PROTECT 0x40 11398937Sdes#define M_MASK 0xff 11498937Sdes#define M_ASCII 0x7f 11598937Sdes 11698937Sdestypedef char Char; 11798937Sdes 11898937Sdes#endif 11998937Sdes 12098937Sdes 12198937Sdes#define CHAR(c) ((Char)((c)&M_ASCII)) 12298937Sdes#define META(c) ((Char)((c)|M_QUOTE)) 12398937Sdes#define M_ALL META('*') 12498937Sdes#define M_END META(']') 12598937Sdes#define M_NOT META('!') 12698937Sdes#define M_ONE META('?') 12798937Sdes#define M_RNG META('-') 12898937Sdes#define M_SET META('[') 129221420Sdes#define M_CLASS META(':') 13098937Sdes#define ismeta(c) (((c)&M_QUOTE) != 0) 13198937Sdes 132221420Sdes#define GLOB_LIMIT_MALLOC 65536 133221420Sdes#define GLOB_LIMIT_STAT 128 134221420Sdes#define GLOB_LIMIT_READDIR 16384 13598937Sdes 136240075Sdes/* Limit of recursion during matching attempts. */ 137240075Sdes#define GLOB_LIMIT_RECUR 64 138240075Sdes 139221420Sdesstruct glob_lim { 140221420Sdes size_t glim_malloc; 141221420Sdes size_t glim_stat; 142221420Sdes size_t glim_readdir; 143221420Sdes}; 144221420Sdes 145240075Sdesstruct glob_path_stat { 146240075Sdes char *gps_path; 147240075Sdes struct stat *gps_stat; 148240075Sdes}; 149240075Sdes 150106121Sdesstatic int compare(const void *, const void *); 151240075Sdesstatic int compare_gps(const void *, const void *); 152106121Sdesstatic int g_Ctoc(const Char *, char *, u_int); 153106121Sdesstatic int g_lstat(Char *, struct stat *, glob_t *); 154106121Sdesstatic DIR *g_opendir(Char *, glob_t *); 155221420Sdesstatic Char *g_strchr(const Char *, int); 156221420Sdesstatic int g_strncmp(const Char *, const char *, size_t); 157106121Sdesstatic int g_stat(Char *, struct stat *, glob_t *); 158221420Sdesstatic int glob0(const Char *, glob_t *, struct glob_lim *); 159221420Sdesstatic int glob1(Char *, Char *, glob_t *, struct glob_lim *); 160106121Sdesstatic int glob2(Char *, Char *, Char *, Char *, Char *, Char *, 161221420Sdes glob_t *, struct glob_lim *); 162181111Sdesstatic int glob3(Char *, Char *, Char *, Char *, Char *, 163221420Sdes Char *, Char *, glob_t *, struct glob_lim *); 164221420Sdesstatic int globextend(const Char *, glob_t *, struct glob_lim *, 165221420Sdes struct stat *); 16698937Sdesstatic const Char * 167106121Sdes globtilde(const Char *, Char *, size_t, glob_t *); 168221420Sdesstatic int globexp1(const Char *, glob_t *, struct glob_lim *); 169221420Sdesstatic int globexp2(const Char *, const Char *, glob_t *, 170221420Sdes struct glob_lim *); 171240075Sdesstatic int match(Char *, Char *, Char *, int); 17298937Sdes#ifdef DEBUG 173106121Sdesstatic void qprintf(const char *, Char *); 17498937Sdes#endif 17598937Sdes 17698937Sdesint 177157016Sdesglob(const char *pattern, int flags, int (*errfunc)(const char *, int), 178157016Sdes glob_t *pglob) 17998937Sdes{ 18098937Sdes const u_char *patnext; 18198937Sdes int c; 18298937Sdes Char *bufnext, *bufend, patbuf[MAXPATHLEN]; 183221420Sdes struct glob_lim limit = { 0, 0, 0 }; 18498937Sdes 185240075Sdes if (strnlen(pattern, PATH_MAX) == PATH_MAX) 186240075Sdes return(GLOB_NOMATCH); 187240075Sdes 18898937Sdes patnext = (u_char *) pattern; 18998937Sdes if (!(flags & GLOB_APPEND)) { 19098937Sdes pglob->gl_pathc = 0; 19198937Sdes pglob->gl_pathv = NULL; 192221420Sdes pglob->gl_statv = NULL; 19398937Sdes if (!(flags & GLOB_DOOFFS)) 19498937Sdes pglob->gl_offs = 0; 19598937Sdes } 19698937Sdes pglob->gl_flags = flags & ~GLOB_MAGCHAR; 19798937Sdes pglob->gl_errfunc = errfunc; 19898937Sdes pglob->gl_matchc = 0; 19998937Sdes 200221420Sdes if (pglob->gl_offs < 0 || pglob->gl_pathc < 0 || 201221420Sdes pglob->gl_offs >= INT_MAX || pglob->gl_pathc >= INT_MAX || 202221420Sdes pglob->gl_pathc >= INT_MAX - pglob->gl_offs - 1) 203221420Sdes return GLOB_NOSPACE; 204221420Sdes 20598937Sdes bufnext = patbuf; 20698937Sdes bufend = bufnext + MAXPATHLEN - 1; 20798937Sdes if (flags & GLOB_NOESCAPE) 20898937Sdes while (bufnext < bufend && (c = *patnext++) != EOS) 20998937Sdes *bufnext++ = c; 21098937Sdes else { 21198937Sdes /* Protect the quoted characters. */ 21298937Sdes while (bufnext < bufend && (c = *patnext++) != EOS) 21398937Sdes if (c == QUOTE) { 21498937Sdes if ((c = *patnext++) == EOS) { 21598937Sdes c = QUOTE; 21698937Sdes --patnext; 21798937Sdes } 21898937Sdes *bufnext++ = c | M_PROTECT; 21998937Sdes } else 22098937Sdes *bufnext++ = c; 22198937Sdes } 22298937Sdes *bufnext = EOS; 22398937Sdes 22498937Sdes if (flags & GLOB_BRACE) 225221420Sdes return globexp1(patbuf, pglob, &limit); 22698937Sdes else 227221420Sdes return glob0(patbuf, pglob, &limit); 22898937Sdes} 22998937Sdes 23098937Sdes/* 23198937Sdes * Expand recursively a glob {} pattern. When there is no more expansion 23298937Sdes * invoke the standard globbing routine to glob the rest of the magic 23398937Sdes * characters 23498937Sdes */ 23598937Sdesstatic int 236221420Sdesglobexp1(const Char *pattern, glob_t *pglob, struct glob_lim *limitp) 23798937Sdes{ 23898937Sdes const Char* ptr = pattern; 23998937Sdes 24098937Sdes /* Protect a single {}, for find(1), like csh */ 24198937Sdes if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS) 242221420Sdes return glob0(pattern, pglob, limitp); 24398937Sdes 244221420Sdes if ((ptr = (const Char *) g_strchr(ptr, LBRACE)) != NULL) 245221420Sdes return globexp2(ptr, pattern, pglob, limitp); 24698937Sdes 247221420Sdes return glob0(pattern, pglob, limitp); 24898937Sdes} 24998937Sdes 25098937Sdes 25198937Sdes/* 25298937Sdes * Recursive brace globbing helper. Tries to expand a single brace. 25398937Sdes * If it succeeds then it invokes globexp1 with the new pattern. 25498937Sdes * If it fails then it tries to glob the rest of the pattern and returns. 25598937Sdes */ 25698937Sdesstatic int 257221420Sdesglobexp2(const Char *ptr, const Char *pattern, glob_t *pglob, 258221420Sdes struct glob_lim *limitp) 25998937Sdes{ 260221420Sdes int i, rv; 26198937Sdes Char *lm, *ls; 26298937Sdes const Char *pe, *pm, *pl; 26398937Sdes Char patbuf[MAXPATHLEN]; 26498937Sdes 26598937Sdes /* copy part up to the brace */ 26698937Sdes for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++) 26798937Sdes ; 26898937Sdes *lm = EOS; 26998937Sdes ls = lm; 27098937Sdes 27198937Sdes /* Find the balanced brace */ 27298937Sdes for (i = 0, pe = ++ptr; *pe; pe++) 27398937Sdes if (*pe == LBRACKET) { 27498937Sdes /* Ignore everything between [] */ 27598937Sdes for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++) 27698937Sdes ; 27798937Sdes if (*pe == EOS) { 27898937Sdes /* 27998937Sdes * We could not find a matching RBRACKET. 28098937Sdes * Ignore and just look for RBRACE 28198937Sdes */ 28298937Sdes pe = pm; 28398937Sdes } 28498937Sdes } else if (*pe == LBRACE) 28598937Sdes i++; 28698937Sdes else if (*pe == RBRACE) { 28798937Sdes if (i == 0) 28898937Sdes break; 28998937Sdes i--; 29098937Sdes } 29198937Sdes 29298937Sdes /* Non matching braces; just glob the pattern */ 293221420Sdes if (i != 0 || *pe == EOS) 294221420Sdes return glob0(patbuf, pglob, limitp); 29598937Sdes 29698937Sdes for (i = 0, pl = pm = ptr; pm <= pe; pm++) { 29798937Sdes switch (*pm) { 29898937Sdes case LBRACKET: 29998937Sdes /* Ignore everything between [] */ 30098937Sdes for (pl = pm++; *pm != RBRACKET && *pm != EOS; pm++) 30198937Sdes ; 30298937Sdes if (*pm == EOS) { 30398937Sdes /* 30498937Sdes * We could not find a matching RBRACKET. 30598937Sdes * Ignore and just look for RBRACE 30698937Sdes */ 30798937Sdes pm = pl; 30898937Sdes } 30998937Sdes break; 31098937Sdes 31198937Sdes case LBRACE: 31298937Sdes i++; 31398937Sdes break; 31498937Sdes 31598937Sdes case RBRACE: 31698937Sdes if (i) { 31798937Sdes i--; 31898937Sdes break; 31998937Sdes } 32098937Sdes /* FALLTHROUGH */ 32198937Sdes case COMMA: 32298937Sdes if (i && *pm == COMMA) 32398937Sdes break; 32498937Sdes else { 32598937Sdes /* Append the current string */ 32698937Sdes for (lm = ls; (pl < pm); *lm++ = *pl++) 32798937Sdes ; 32898937Sdes 32998937Sdes /* 33098937Sdes * Append the rest of the pattern after the 33198937Sdes * closing brace 33298937Sdes */ 33398937Sdes for (pl = pe + 1; (*lm++ = *pl++) != EOS; ) 33498937Sdes ; 33598937Sdes 33698937Sdes /* Expand the current pattern */ 33798937Sdes#ifdef DEBUG 33898937Sdes qprintf("globexp2:", patbuf); 33998937Sdes#endif 340221420Sdes rv = globexp1(patbuf, pglob, limitp); 341221420Sdes if (rv && rv != GLOB_NOMATCH) 342221420Sdes return rv; 34398937Sdes 34498937Sdes /* move after the comma, to the next string */ 34598937Sdes pl = pm + 1; 34698937Sdes } 34798937Sdes break; 34898937Sdes 34998937Sdes default: 35098937Sdes break; 35198937Sdes } 35298937Sdes } 35398937Sdes return 0; 35498937Sdes} 35598937Sdes 35698937Sdes 35798937Sdes 35898937Sdes/* 35998937Sdes * expand tilde from the passwd file. 36098937Sdes */ 36198937Sdesstatic const Char * 362157016Sdesglobtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t *pglob) 36398937Sdes{ 36498937Sdes struct passwd *pwd; 36598937Sdes char *h; 36698937Sdes const Char *p; 36798937Sdes Char *b, *eb; 36898937Sdes 36998937Sdes if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE)) 37098937Sdes return pattern; 37198937Sdes 37298937Sdes /* Copy up to the end of the string or / */ 37398937Sdes eb = &patbuf[patbuf_len - 1]; 37498937Sdes for (p = pattern + 1, h = (char *) patbuf; 37598937Sdes h < (char *)eb && *p && *p != SLASH; *h++ = *p++) 37698937Sdes ; 37798937Sdes 37898937Sdes *h = EOS; 37998937Sdes 38098937Sdes#if 0 38198937Sdes if (h == (char *)eb) 38298937Sdes return what; 38398937Sdes#endif 38498937Sdes 38598937Sdes if (((char *) patbuf)[0] == EOS) { 38698937Sdes /* 38798937Sdes * handle a plain ~ or ~/ by expanding $HOME 38898937Sdes * first and then trying the password file 38998937Sdes */ 39098937Sdes#if 0 39198937Sdes if (issetugid() != 0 || (h = getenv("HOME")) == NULL) { 39298937Sdes#endif 39398937Sdes if ((getuid() != geteuid()) || (h = getenv("HOME")) == NULL) { 39498937Sdes if ((pwd = getpwuid(getuid())) == NULL) 39598937Sdes return pattern; 39698937Sdes else 39798937Sdes h = pwd->pw_dir; 39898937Sdes } 39998937Sdes } else { 40098937Sdes /* 40198937Sdes * Expand a ~user 40298937Sdes */ 40398937Sdes if ((pwd = getpwnam((char*) patbuf)) == NULL) 40498937Sdes return pattern; 40598937Sdes else 40698937Sdes h = pwd->pw_dir; 40798937Sdes } 40898937Sdes 40998937Sdes /* Copy the home directory */ 41098937Sdes for (b = patbuf; b < eb && *h; *b++ = *h++) 41198937Sdes ; 41298937Sdes 41398937Sdes /* Append the rest of the pattern */ 41498937Sdes while (b < eb && (*b++ = *p++) != EOS) 41598937Sdes ; 41698937Sdes *b = EOS; 41798937Sdes 41898937Sdes return patbuf; 41998937Sdes} 42098937Sdes 421221420Sdesstatic int 422221420Sdesg_strncmp(const Char *s1, const char *s2, size_t n) 423221420Sdes{ 424221420Sdes int rv = 0; 42598937Sdes 426221420Sdes while (n--) { 427221420Sdes rv = *(Char *)s1 - *(const unsigned char *)s2++; 428221420Sdes if (rv) 429221420Sdes break; 430221420Sdes if (*s1++ == '\0') 431221420Sdes break; 432221420Sdes } 433221420Sdes return rv; 434221420Sdes} 435221420Sdes 436221420Sdesstatic int 437221420Sdesg_charclass(const Char **patternp, Char **bufnextp) 438221420Sdes{ 439221420Sdes const Char *pattern = *patternp + 1; 440221420Sdes Char *bufnext = *bufnextp; 441221420Sdes const Char *colon; 442221420Sdes struct cclass *cc; 443221420Sdes size_t len; 444221420Sdes 445221420Sdes if ((colon = g_strchr(pattern, ':')) == NULL || colon[1] != ']') 446221420Sdes return 1; /* not a character class */ 447221420Sdes 448221420Sdes len = (size_t)(colon - pattern); 449221420Sdes for (cc = cclasses; cc->name != NULL; cc++) { 450221420Sdes if (!g_strncmp(pattern, cc->name, len) && cc->name[len] == '\0') 451221420Sdes break; 452221420Sdes } 453221420Sdes if (cc->name == NULL) 454221420Sdes return -1; /* invalid character class */ 455221420Sdes *bufnext++ = M_CLASS; 456221420Sdes *bufnext++ = (Char)(cc - &cclasses[0]); 457221420Sdes *bufnextp = bufnext; 458221420Sdes *patternp += len + 3; 459221420Sdes 460221420Sdes return 0; 461221420Sdes} 462221420Sdes 46398937Sdes/* 46498937Sdes * The main glob() routine: compiles the pattern (optionally processing 46598937Sdes * quotes), calls glob1() to do the real pattern matching, and finally 46698937Sdes * sorts the list (unless unsorted operation is requested). Returns 0 46798937Sdes * if things went well, nonzero if errors occurred. It is not an error 46898937Sdes * to find no matches. 46998937Sdes */ 47098937Sdesstatic int 471221420Sdesglob0(const Char *pattern, glob_t *pglob, struct glob_lim *limitp) 47298937Sdes{ 47398937Sdes const Char *qpatnext; 47498937Sdes int c, err, oldpathc; 47598937Sdes Char *bufnext, patbuf[MAXPATHLEN]; 47698937Sdes 47798937Sdes qpatnext = globtilde(pattern, patbuf, MAXPATHLEN, pglob); 47898937Sdes oldpathc = pglob->gl_pathc; 47998937Sdes bufnext = patbuf; 48098937Sdes 48198937Sdes /* We don't need to check for buffer overflow any more. */ 48298937Sdes while ((c = *qpatnext++) != EOS) { 48398937Sdes switch (c) { 48498937Sdes case LBRACKET: 48598937Sdes c = *qpatnext; 48698937Sdes if (c == NOT) 48798937Sdes ++qpatnext; 48898937Sdes if (*qpatnext == EOS || 489221420Sdes g_strchr(qpatnext+1, RBRACKET) == NULL) { 49098937Sdes *bufnext++ = LBRACKET; 49198937Sdes if (c == NOT) 49298937Sdes --qpatnext; 49398937Sdes break; 49498937Sdes } 49598937Sdes *bufnext++ = M_SET; 49698937Sdes if (c == NOT) 49798937Sdes *bufnext++ = M_NOT; 49898937Sdes c = *qpatnext++; 49998937Sdes do { 500221420Sdes if (c == LBRACKET && *qpatnext == ':') { 501221420Sdes do { 502221420Sdes err = g_charclass(&qpatnext, 503221420Sdes &bufnext); 504221420Sdes if (err) 505221420Sdes break; 506221420Sdes c = *qpatnext++; 507221420Sdes } while (c == LBRACKET && *qpatnext == ':'); 508221420Sdes if (err == -1 && 509221420Sdes !(pglob->gl_flags & GLOB_NOCHECK)) 510221420Sdes return GLOB_NOMATCH; 511221420Sdes if (c == RBRACKET) 512221420Sdes break; 513221420Sdes } 51498937Sdes *bufnext++ = CHAR(c); 51598937Sdes if (*qpatnext == RANGE && 51698937Sdes (c = qpatnext[1]) != RBRACKET) { 51798937Sdes *bufnext++ = M_RNG; 51898937Sdes *bufnext++ = CHAR(c); 51998937Sdes qpatnext += 2; 52098937Sdes } 52198937Sdes } while ((c = *qpatnext++) != RBRACKET); 52298937Sdes pglob->gl_flags |= GLOB_MAGCHAR; 52398937Sdes *bufnext++ = M_END; 52498937Sdes break; 52598937Sdes case QUESTION: 52698937Sdes pglob->gl_flags |= GLOB_MAGCHAR; 52798937Sdes *bufnext++ = M_ONE; 52898937Sdes break; 52998937Sdes case STAR: 53098937Sdes pglob->gl_flags |= GLOB_MAGCHAR; 53198937Sdes /* collapse adjacent stars to one, 53298937Sdes * to avoid exponential behavior 53398937Sdes */ 53498937Sdes if (bufnext == patbuf || bufnext[-1] != M_ALL) 53598937Sdes *bufnext++ = M_ALL; 53698937Sdes break; 53798937Sdes default: 53898937Sdes *bufnext++ = CHAR(c); 53998937Sdes break; 54098937Sdes } 54198937Sdes } 54298937Sdes *bufnext = EOS; 54398937Sdes#ifdef DEBUG 54498937Sdes qprintf("glob0:", patbuf); 54598937Sdes#endif 54698937Sdes 547221420Sdes if ((err = glob1(patbuf, patbuf+MAXPATHLEN-1, pglob, limitp)) != 0) 54898937Sdes return(err); 54998937Sdes 55098937Sdes /* 55198937Sdes * If there was no match we are going to append the pattern 55298937Sdes * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified 55398937Sdes * and the pattern did not contain any magic characters 55498937Sdes * GLOB_NOMAGIC is there just for compatibility with csh. 55598937Sdes */ 55698937Sdes if (pglob->gl_pathc == oldpathc) { 55798937Sdes if ((pglob->gl_flags & GLOB_NOCHECK) || 55898937Sdes ((pglob->gl_flags & GLOB_NOMAGIC) && 55998937Sdes !(pglob->gl_flags & GLOB_MAGCHAR))) 560221420Sdes return(globextend(pattern, pglob, limitp, NULL)); 56198937Sdes else 56298937Sdes return(GLOB_NOMATCH); 56398937Sdes } 564240075Sdes if (!(pglob->gl_flags & GLOB_NOSORT)) { 565240075Sdes if ((pglob->gl_flags & GLOB_KEEPSTAT)) { 566240075Sdes /* Keep the paths and stat info synced during sort */ 567240075Sdes struct glob_path_stat *path_stat; 568240075Sdes int i; 569240075Sdes int n = pglob->gl_pathc - oldpathc; 570240075Sdes int o = pglob->gl_offs + oldpathc; 571240075Sdes 572240075Sdes if ((path_stat = calloc(n, sizeof(*path_stat))) == NULL) 573240075Sdes return GLOB_NOSPACE; 574240075Sdes for (i = 0; i < n; i++) { 575240075Sdes path_stat[i].gps_path = pglob->gl_pathv[o + i]; 576240075Sdes path_stat[i].gps_stat = pglob->gl_statv[o + i]; 577240075Sdes } 578240075Sdes qsort(path_stat, n, sizeof(*path_stat), compare_gps); 579240075Sdes for (i = 0; i < n; i++) { 580240075Sdes pglob->gl_pathv[o + i] = path_stat[i].gps_path; 581240075Sdes pglob->gl_statv[o + i] = path_stat[i].gps_stat; 582240075Sdes } 583240075Sdes free(path_stat); 584240075Sdes } else { 585240075Sdes qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc, 586240075Sdes pglob->gl_pathc - oldpathc, sizeof(char *), 587240075Sdes compare); 588240075Sdes } 589240075Sdes } 59098937Sdes return(0); 59198937Sdes} 59298937Sdes 59398937Sdesstatic int 594157016Sdescompare(const void *p, const void *q) 59598937Sdes{ 59698937Sdes return(strcmp(*(char **)p, *(char **)q)); 59798937Sdes} 59898937Sdes 59998937Sdesstatic int 600240075Sdescompare_gps(const void *_p, const void *_q) 601240075Sdes{ 602240075Sdes const struct glob_path_stat *p = (const struct glob_path_stat *)_p; 603240075Sdes const struct glob_path_stat *q = (const struct glob_path_stat *)_q; 604240075Sdes 605240075Sdes return(strcmp(p->gps_path, q->gps_path)); 606240075Sdes} 607240075Sdes 608240075Sdesstatic int 609221420Sdesglob1(Char *pattern, Char *pattern_last, glob_t *pglob, struct glob_lim *limitp) 61098937Sdes{ 61198937Sdes Char pathbuf[MAXPATHLEN]; 61298937Sdes 61398937Sdes /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */ 61498937Sdes if (*pattern == EOS) 61598937Sdes return(0); 61698937Sdes return(glob2(pathbuf, pathbuf+MAXPATHLEN-1, 61798937Sdes pathbuf, pathbuf+MAXPATHLEN-1, 61898937Sdes pattern, pattern_last, pglob, limitp)); 61998937Sdes} 62098937Sdes 62198937Sdes/* 62298937Sdes * The functions glob2 and glob3 are mutually recursive; there is one level 62398937Sdes * of recursion for each segment in the pattern that contains one or more 62498937Sdes * meta characters. 62598937Sdes */ 62698937Sdesstatic int 627157016Sdesglob2(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last, 628221420Sdes Char *pattern, Char *pattern_last, glob_t *pglob, struct glob_lim *limitp) 62998937Sdes{ 63098937Sdes struct stat sb; 63198937Sdes Char *p, *q; 63298937Sdes int anymeta; 63398937Sdes 63498937Sdes /* 63598937Sdes * Loop over pattern segments until end of pattern or until 63698937Sdes * segment with meta character found. 63798937Sdes */ 63898937Sdes for (anymeta = 0;;) { 63998937Sdes if (*pattern == EOS) { /* End of pattern? */ 64098937Sdes *pathend = EOS; 64198937Sdes if (g_lstat(pathbuf, &sb, pglob)) 64298937Sdes return(0); 64398937Sdes 644221420Sdes if ((pglob->gl_flags & GLOB_LIMIT) && 645221420Sdes limitp->glim_stat++ >= GLOB_LIMIT_STAT) { 646221420Sdes errno = 0; 647221420Sdes *pathend++ = SEP; 648221420Sdes *pathend = EOS; 649221420Sdes return(GLOB_NOSPACE); 650221420Sdes } 651221420Sdes 65298937Sdes if (((pglob->gl_flags & GLOB_MARK) && 65398937Sdes pathend[-1] != SEP) && (S_ISDIR(sb.st_mode) || 65498937Sdes (S_ISLNK(sb.st_mode) && 65598937Sdes (g_stat(pathbuf, &sb, pglob) == 0) && 65698937Sdes S_ISDIR(sb.st_mode)))) { 65798937Sdes if (pathend+1 > pathend_last) 65898937Sdes return (1); 65998937Sdes *pathend++ = SEP; 66098937Sdes *pathend = EOS; 66198937Sdes } 66298937Sdes ++pglob->gl_matchc; 663221420Sdes return(globextend(pathbuf, pglob, limitp, &sb)); 66498937Sdes } 66598937Sdes 66698937Sdes /* Find end of next segment, copy tentatively to pathend. */ 66798937Sdes q = pathend; 66898937Sdes p = pattern; 66998937Sdes while (*p != EOS && *p != SEP) { 67098937Sdes if (ismeta(*p)) 67198937Sdes anymeta = 1; 67298937Sdes if (q+1 > pathend_last) 67398937Sdes return (1); 67498937Sdes *q++ = *p++; 67598937Sdes } 67698937Sdes 67798937Sdes if (!anymeta) { /* No expansion, do next segment. */ 67898937Sdes pathend = q; 67998937Sdes pattern = p; 68098937Sdes while (*pattern == SEP) { 68198937Sdes if (pathend+1 > pathend_last) 68298937Sdes return (1); 68398937Sdes *pathend++ = *pattern++; 68498937Sdes } 68598937Sdes } else 68698937Sdes /* Need expansion, recurse. */ 68798937Sdes return(glob3(pathbuf, pathbuf_last, pathend, 688181111Sdes pathend_last, pattern, p, pattern_last, 689181111Sdes pglob, limitp)); 69098937Sdes } 69198937Sdes /* NOTREACHED */ 69298937Sdes} 69398937Sdes 69498937Sdesstatic int 695157016Sdesglob3(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last, 696181111Sdes Char *pattern, Char *restpattern, Char *restpattern_last, glob_t *pglob, 697221420Sdes struct glob_lim *limitp) 69898937Sdes{ 699157016Sdes struct dirent *dp; 70098937Sdes DIR *dirp; 70198937Sdes int err; 70298937Sdes char buf[MAXPATHLEN]; 70398937Sdes 70498937Sdes /* 70598937Sdes * The readdirfunc declaration can't be prototyped, because it is 70698937Sdes * assigned, below, to two functions which are prototyped in glob.h 70798937Sdes * and dirent.h as taking pointers to differently typed opaque 70898937Sdes * structures. 70998937Sdes */ 710124208Sdes struct dirent *(*readdirfunc)(void *); 71198937Sdes 71298937Sdes if (pathend > pathend_last) 71398937Sdes return (1); 71498937Sdes *pathend = EOS; 71598937Sdes errno = 0; 71698937Sdes 71798937Sdes if ((dirp = g_opendir(pathbuf, pglob)) == NULL) { 71898937Sdes /* TODO: don't call for ENOENT or ENOTDIR? */ 71998937Sdes if (pglob->gl_errfunc) { 72098937Sdes if (g_Ctoc(pathbuf, buf, sizeof(buf))) 72198937Sdes return(GLOB_ABORTED); 72298937Sdes if (pglob->gl_errfunc(buf, errno) || 72398937Sdes pglob->gl_flags & GLOB_ERR) 72498937Sdes return(GLOB_ABORTED); 72598937Sdes } 72698937Sdes return(0); 72798937Sdes } 72898937Sdes 72998937Sdes err = 0; 73098937Sdes 73198937Sdes /* Search directory for matching names. */ 73298937Sdes if (pglob->gl_flags & GLOB_ALTDIRFUNC) 73398937Sdes readdirfunc = pglob->gl_readdir; 73498937Sdes else 735124208Sdes readdirfunc = (struct dirent *(*)(void *))readdir; 73698937Sdes while ((dp = (*readdirfunc)(dirp))) { 737157016Sdes u_char *sc; 738157016Sdes Char *dc; 73998937Sdes 740221420Sdes if ((pglob->gl_flags & GLOB_LIMIT) && 741221420Sdes limitp->glim_readdir++ >= GLOB_LIMIT_READDIR) { 742221420Sdes errno = 0; 743221420Sdes *pathend++ = SEP; 744221420Sdes *pathend = EOS; 745240075Sdes err = GLOB_NOSPACE; 746240075Sdes break; 747221420Sdes } 748221420Sdes 74998937Sdes /* Initial DOT must be matched literally. */ 75098937Sdes if (dp->d_name[0] == DOT && *pattern != DOT) 75198937Sdes continue; 75298937Sdes dc = pathend; 75398937Sdes sc = (u_char *) dp->d_name; 75498937Sdes while (dc < pathend_last && (*dc++ = *sc++) != EOS) 75598937Sdes ; 75698937Sdes if (dc >= pathend_last) { 75798937Sdes *dc = EOS; 75898937Sdes err = 1; 75998937Sdes break; 76098937Sdes } 76198937Sdes 762240075Sdes if (!match(pathend, pattern, restpattern, GLOB_LIMIT_RECUR)) { 76398937Sdes *pathend = EOS; 76498937Sdes continue; 76598937Sdes } 76698937Sdes err = glob2(pathbuf, pathbuf_last, --dc, pathend_last, 76798937Sdes restpattern, restpattern_last, pglob, limitp); 76898937Sdes if (err) 76998937Sdes break; 77098937Sdes } 77198937Sdes 77298937Sdes if (pglob->gl_flags & GLOB_ALTDIRFUNC) 77398937Sdes (*pglob->gl_closedir)(dirp); 77498937Sdes else 77598937Sdes closedir(dirp); 77698937Sdes return(err); 77798937Sdes} 77898937Sdes 77998937Sdes 78098937Sdes/* 781106121Sdes * Extend the gl_pathv member of a glob_t structure to accommodate a new item, 78298937Sdes * add the new item, and update gl_pathc. 78398937Sdes * 78498937Sdes * This assumes the BSD realloc, which only copies the block when its size 78598937Sdes * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic 78698937Sdes * behavior. 78798937Sdes * 78898937Sdes * Return 0 if new item added, error code if memory couldn't be allocated. 78998937Sdes * 79098937Sdes * Invariant of the glob_t structure: 79198937Sdes * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and 79298937Sdes * gl_pathv points to (gl_offs + gl_pathc + 1) items. 79398937Sdes */ 79498937Sdesstatic int 795221420Sdesglobextend(const Char *path, glob_t *pglob, struct glob_lim *limitp, 796221420Sdes struct stat *sb) 79798937Sdes{ 798157016Sdes char **pathv; 799221420Sdes ssize_t i; 800221420Sdes size_t newn, len; 801221420Sdes char *copy = NULL; 80298937Sdes const Char *p; 803221420Sdes struct stat **statv; 80498937Sdes 805221420Sdes newn = 2 + pglob->gl_pathc + pglob->gl_offs; 806221420Sdes if (pglob->gl_offs >= INT_MAX || 807221420Sdes pglob->gl_pathc >= INT_MAX || 808221420Sdes newn >= INT_MAX || 809221420Sdes SIZE_MAX / sizeof(*pathv) <= newn || 810221420Sdes SIZE_MAX / sizeof(*statv) <= newn) { 811221420Sdes nospace: 812221420Sdes for (i = pglob->gl_offs; i < (ssize_t)(newn - 2); i++) { 813221420Sdes if (pglob->gl_pathv && pglob->gl_pathv[i]) 814221420Sdes free(pglob->gl_pathv[i]); 815221420Sdes if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0 && 816221420Sdes pglob->gl_pathv && pglob->gl_pathv[i]) 817221420Sdes free(pglob->gl_statv[i]); 818221420Sdes } 81998937Sdes if (pglob->gl_pathv) { 82098937Sdes free(pglob->gl_pathv); 82198937Sdes pglob->gl_pathv = NULL; 82298937Sdes } 823221420Sdes if (pglob->gl_statv) { 824221420Sdes free(pglob->gl_statv); 825221420Sdes pglob->gl_statv = NULL; 826221420Sdes } 82798937Sdes return(GLOB_NOSPACE); 82898937Sdes } 82998937Sdes 830221420Sdes pathv = realloc(pglob->gl_pathv, newn * sizeof(*pathv)); 831221420Sdes if (pathv == NULL) 832221420Sdes goto nospace; 83398937Sdes if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) { 83498937Sdes /* first time around -- clear initial gl_offs items */ 83598937Sdes pathv += pglob->gl_offs; 83698937Sdes for (i = pglob->gl_offs; --i >= 0; ) 83798937Sdes *--pathv = NULL; 83898937Sdes } 83998937Sdes pglob->gl_pathv = pathv; 84098937Sdes 841221420Sdes if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0) { 842221420Sdes statv = realloc(pglob->gl_statv, newn * sizeof(*statv)); 843221420Sdes if (statv == NULL) 844221420Sdes goto nospace; 845221420Sdes if (pglob->gl_statv == NULL && pglob->gl_offs > 0) { 846221420Sdes /* first time around -- clear initial gl_offs items */ 847221420Sdes statv += pglob->gl_offs; 848221420Sdes for (i = pglob->gl_offs; --i >= 0; ) 849221420Sdes *--statv = NULL; 850221420Sdes } 851221420Sdes pglob->gl_statv = statv; 852221420Sdes if (sb == NULL) 853221420Sdes statv[pglob->gl_offs + pglob->gl_pathc] = NULL; 854221420Sdes else { 855221420Sdes limitp->glim_malloc += sizeof(**statv); 856221420Sdes if ((pglob->gl_flags & GLOB_LIMIT) && 857221420Sdes limitp->glim_malloc >= GLOB_LIMIT_MALLOC) { 858221420Sdes errno = 0; 859221420Sdes return(GLOB_NOSPACE); 860221420Sdes } 861221420Sdes if ((statv[pglob->gl_offs + pglob->gl_pathc] = 862221420Sdes malloc(sizeof(**statv))) == NULL) 863221420Sdes goto copy_error; 864221420Sdes memcpy(statv[pglob->gl_offs + pglob->gl_pathc], sb, 865221420Sdes sizeof(*sb)); 866221420Sdes } 867221420Sdes statv[pglob->gl_offs + pglob->gl_pathc + 1] = NULL; 868221420Sdes } 869221420Sdes 87098937Sdes for (p = path; *p++;) 87198937Sdes ; 87298937Sdes len = (size_t)(p - path); 873221420Sdes limitp->glim_malloc += len; 87498937Sdes if ((copy = malloc(len)) != NULL) { 87598937Sdes if (g_Ctoc(path, copy, len)) { 87698937Sdes free(copy); 87798937Sdes return(GLOB_NOSPACE); 87898937Sdes } 87998937Sdes pathv[pglob->gl_offs + pglob->gl_pathc++] = copy; 88098937Sdes } 88198937Sdes pathv[pglob->gl_offs + pglob->gl_pathc] = NULL; 88298937Sdes 88398937Sdes if ((pglob->gl_flags & GLOB_LIMIT) && 884221420Sdes (newn * sizeof(*pathv)) + limitp->glim_malloc > 885221420Sdes GLOB_LIMIT_MALLOC) { 88698937Sdes errno = 0; 88798937Sdes return(GLOB_NOSPACE); 88898937Sdes } 889221420Sdes copy_error: 89098937Sdes return(copy == NULL ? GLOB_NOSPACE : 0); 89198937Sdes} 89298937Sdes 89398937Sdes 89498937Sdes/* 89598937Sdes * pattern matching function for filenames. Each occurrence of the * 89698937Sdes * pattern causes a recursion level. 89798937Sdes */ 89898937Sdesstatic int 899240075Sdesmatch(Char *name, Char *pat, Char *patend, int recur) 90098937Sdes{ 90198937Sdes int ok, negate_range; 90298937Sdes Char c, k; 90398937Sdes 904240075Sdes if (recur-- == 0) 905240075Sdes return(GLOB_NOSPACE); 906240075Sdes 90798937Sdes while (pat < patend) { 90898937Sdes c = *pat++; 90998937Sdes switch (c & M_MASK) { 91098937Sdes case M_ALL: 911240075Sdes while (pat < patend && (*pat & M_MASK) == M_ALL) 912240075Sdes pat++; /* eat consecutive '*' */ 91398937Sdes if (pat == patend) 91498937Sdes return(1); 915157016Sdes do { 916240075Sdes if (match(name, pat, patend, recur)) 91798937Sdes return(1); 918157016Sdes } while (*name++ != EOS); 91998937Sdes return(0); 92098937Sdes case M_ONE: 92198937Sdes if (*name++ == EOS) 92298937Sdes return(0); 92398937Sdes break; 92498937Sdes case M_SET: 92598937Sdes ok = 0; 92698937Sdes if ((k = *name++) == EOS) 92798937Sdes return(0); 92898937Sdes if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS) 92998937Sdes ++pat; 930221420Sdes while (((c = *pat++) & M_MASK) != M_END) { 931221420Sdes if ((c & M_MASK) == M_CLASS) { 932221420Sdes Char idx = *pat & M_MASK; 933221420Sdes if (idx < NCCLASSES && 934221420Sdes cclasses[idx].isctype(k)) 935221420Sdes ok = 1; 936221420Sdes ++pat; 937221420Sdes } 93898937Sdes if ((*pat & M_MASK) == M_RNG) { 93998937Sdes if (c <= k && k <= pat[1]) 94098937Sdes ok = 1; 94198937Sdes pat += 2; 94298937Sdes } else if (c == k) 94398937Sdes ok = 1; 944221420Sdes } 94598937Sdes if (ok == negate_range) 94698937Sdes return(0); 94798937Sdes break; 94898937Sdes default: 94998937Sdes if (*name++ != c) 95098937Sdes return(0); 95198937Sdes break; 95298937Sdes } 95398937Sdes } 95498937Sdes return(*name == EOS); 95598937Sdes} 95698937Sdes 95798937Sdes/* Free allocated data belonging to a glob_t structure. */ 95898937Sdesvoid 959157016Sdesglobfree(glob_t *pglob) 96098937Sdes{ 961157016Sdes int i; 962157016Sdes char **pp; 96398937Sdes 96498937Sdes if (pglob->gl_pathv != NULL) { 96598937Sdes pp = pglob->gl_pathv + pglob->gl_offs; 96698937Sdes for (i = pglob->gl_pathc; i--; ++pp) 96798937Sdes if (*pp) 96898937Sdes free(*pp); 96998937Sdes free(pglob->gl_pathv); 97098937Sdes pglob->gl_pathv = NULL; 97198937Sdes } 972221420Sdes if (pglob->gl_statv != NULL) { 973221420Sdes for (i = 0; i < pglob->gl_pathc; i++) { 974221420Sdes if (pglob->gl_statv[i] != NULL) 975221420Sdes free(pglob->gl_statv[i]); 976221420Sdes } 977221420Sdes free(pglob->gl_statv); 978221420Sdes pglob->gl_statv = NULL; 979221420Sdes } 98098937Sdes} 98198937Sdes 98298937Sdesstatic DIR * 983157016Sdesg_opendir(Char *str, glob_t *pglob) 98498937Sdes{ 98598937Sdes char buf[MAXPATHLEN]; 98698937Sdes 98798937Sdes if (!*str) 988106121Sdes strlcpy(buf, ".", sizeof buf); 98998937Sdes else { 99098937Sdes if (g_Ctoc(str, buf, sizeof(buf))) 99198937Sdes return(NULL); 99298937Sdes } 99398937Sdes 99498937Sdes if (pglob->gl_flags & GLOB_ALTDIRFUNC) 99598937Sdes return((*pglob->gl_opendir)(buf)); 99698937Sdes 99798937Sdes return(opendir(buf)); 99898937Sdes} 99998937Sdes 100098937Sdesstatic int 1001157016Sdesg_lstat(Char *fn, struct stat *sb, glob_t *pglob) 100298937Sdes{ 100398937Sdes char buf[MAXPATHLEN]; 100498937Sdes 100598937Sdes if (g_Ctoc(fn, buf, sizeof(buf))) 100698937Sdes return(-1); 100798937Sdes if (pglob->gl_flags & GLOB_ALTDIRFUNC) 100898937Sdes return((*pglob->gl_lstat)(buf, sb)); 100998937Sdes return(lstat(buf, sb)); 101098937Sdes} 101198937Sdes 101298937Sdesstatic int 1013157016Sdesg_stat(Char *fn, struct stat *sb, glob_t *pglob) 101498937Sdes{ 101598937Sdes char buf[MAXPATHLEN]; 101698937Sdes 101798937Sdes if (g_Ctoc(fn, buf, sizeof(buf))) 101898937Sdes return(-1); 101998937Sdes if (pglob->gl_flags & GLOB_ALTDIRFUNC) 102098937Sdes return((*pglob->gl_stat)(buf, sb)); 102198937Sdes return(stat(buf, sb)); 102298937Sdes} 102398937Sdes 102498937Sdesstatic Char * 1025221420Sdesg_strchr(const Char *str, int ch) 102698937Sdes{ 102798937Sdes do { 102898937Sdes if (*str == ch) 1029221420Sdes return ((Char *)str); 103098937Sdes } while (*str++); 103198937Sdes return (NULL); 103298937Sdes} 103398937Sdes 103498937Sdesstatic int 1035157016Sdesg_Ctoc(const Char *str, char *buf, u_int len) 103698937Sdes{ 103798937Sdes 103898937Sdes while (len--) { 103998937Sdes if ((*buf++ = *str++) == EOS) 104098937Sdes return (0); 104198937Sdes } 104298937Sdes return (1); 104398937Sdes} 104498937Sdes 104598937Sdes#ifdef DEBUG 104698937Sdesstatic void 1047157016Sdesqprintf(const char *str, Char *s) 104898937Sdes{ 1049157016Sdes Char *p; 105098937Sdes 105198937Sdes (void)printf("%s:\n", str); 105298937Sdes for (p = s; *p; p++) 105398937Sdes (void)printf("%c", CHAR(*p)); 105498937Sdes (void)printf("\n"); 105598937Sdes for (p = s; *p; p++) 105698937Sdes (void)printf("%c", *p & M_PROTECT ? '"' : ' '); 105798937Sdes (void)printf("\n"); 105898937Sdes for (p = s; *p; p++) 105998937Sdes (void)printf("%c", ismeta(*p) ? '_' : ' '); 106098937Sdes (void)printf("\n"); 106198937Sdes} 106298937Sdes#endif 106398937Sdes 106498937Sdes#endif /* !defined(HAVE_GLOB) || !defined(GLOB_HAS_ALTDIRFUNC) || 1065221420Sdes !defined(GLOB_HAS_GL_MATCHC) || !defined(GLOB_HAS_GL_STATV) */ 1066