1570af302Sopenharmony_ci#define _BSD_SOURCE 2570af302Sopenharmony_ci#include <glob.h> 3570af302Sopenharmony_ci#include <fnmatch.h> 4570af302Sopenharmony_ci#include <sys/stat.h> 5570af302Sopenharmony_ci#include <dirent.h> 6570af302Sopenharmony_ci#include <limits.h> 7570af302Sopenharmony_ci#include <string.h> 8570af302Sopenharmony_ci#include <stdlib.h> 9570af302Sopenharmony_ci#include <errno.h> 10570af302Sopenharmony_ci#include <stddef.h> 11570af302Sopenharmony_ci#include <unistd.h> 12570af302Sopenharmony_ci#include <pwd.h> 13570af302Sopenharmony_ci 14570af302Sopenharmony_cistruct match 15570af302Sopenharmony_ci{ 16570af302Sopenharmony_ci struct match *next; 17570af302Sopenharmony_ci char name[]; 18570af302Sopenharmony_ci}; 19570af302Sopenharmony_ci 20570af302Sopenharmony_cistatic int append(struct match **tail, const char *name, size_t len, int mark) 21570af302Sopenharmony_ci{ 22570af302Sopenharmony_ci struct match *new = malloc(sizeof(struct match) + len + 2); 23570af302Sopenharmony_ci if (!new) return -1; 24570af302Sopenharmony_ci (*tail)->next = new; 25570af302Sopenharmony_ci new->next = NULL; 26570af302Sopenharmony_ci memcpy(new->name, name, len+1); 27570af302Sopenharmony_ci if (mark && len && name[len-1]!='/') { 28570af302Sopenharmony_ci new->name[len] = '/'; 29570af302Sopenharmony_ci new->name[len+1] = 0; 30570af302Sopenharmony_ci } 31570af302Sopenharmony_ci *tail = new; 32570af302Sopenharmony_ci return 0; 33570af302Sopenharmony_ci} 34570af302Sopenharmony_ci 35570af302Sopenharmony_cistatic int do_glob(char *buf, size_t pos, int type, char *pat, int flags, int (*errfunc)(const char *path, int err), struct match **tail) 36570af302Sopenharmony_ci{ 37570af302Sopenharmony_ci /* If GLOB_MARK is unused, we don't care about type. */ 38570af302Sopenharmony_ci if (!type && !(flags & GLOB_MARK)) type = DT_REG; 39570af302Sopenharmony_ci 40570af302Sopenharmony_ci /* Special-case the remaining pattern being all slashes, in 41570af302Sopenharmony_ci * which case we can use caller-passed type if it's a dir. */ 42570af302Sopenharmony_ci if (*pat && type!=DT_DIR) type = 0; 43570af302Sopenharmony_ci while (pos+1 < PATH_MAX && *pat=='/') buf[pos++] = *pat++; 44570af302Sopenharmony_ci 45570af302Sopenharmony_ci /* Consume maximal [escaped-]literal prefix of pattern, copying 46570af302Sopenharmony_ci * and un-escaping it to the running buffer as we go. */ 47570af302Sopenharmony_ci ptrdiff_t i=0, j=0; 48570af302Sopenharmony_ci int in_bracket = 0, overflow = 0; 49570af302Sopenharmony_ci for (; pat[i]!='*' && pat[i]!='?' && (!in_bracket || pat[i]!=']'); i++) { 50570af302Sopenharmony_ci if (!pat[i]) { 51570af302Sopenharmony_ci if (overflow) return 0; 52570af302Sopenharmony_ci pat += i; 53570af302Sopenharmony_ci pos += j; 54570af302Sopenharmony_ci i = j = 0; 55570af302Sopenharmony_ci break; 56570af302Sopenharmony_ci } else if (pat[i] == '[') { 57570af302Sopenharmony_ci in_bracket = 1; 58570af302Sopenharmony_ci } else if (pat[i] == '\\' && !(flags & GLOB_NOESCAPE)) { 59570af302Sopenharmony_ci /* Backslashes inside a bracket are (at least by 60570af302Sopenharmony_ci * our interpretation) non-special, so if next 61570af302Sopenharmony_ci * char is ']' we have a complete expression. */ 62570af302Sopenharmony_ci if (in_bracket && pat[i+1]==']') break; 63570af302Sopenharmony_ci /* Unpaired final backslash never matches. */ 64570af302Sopenharmony_ci if (!pat[i+1]) return 0; 65570af302Sopenharmony_ci i++; 66570af302Sopenharmony_ci } 67570af302Sopenharmony_ci if (pat[i] == '/') { 68570af302Sopenharmony_ci if (overflow) return 0; 69570af302Sopenharmony_ci in_bracket = 0; 70570af302Sopenharmony_ci pat += i+1; 71570af302Sopenharmony_ci i = -1; 72570af302Sopenharmony_ci pos += j+1; 73570af302Sopenharmony_ci j = -1; 74570af302Sopenharmony_ci } 75570af302Sopenharmony_ci /* Only store a character if it fits in the buffer, but if 76570af302Sopenharmony_ci * a potential bracket expression is open, the overflow 77570af302Sopenharmony_ci * must be remembered and handled later only if the bracket 78570af302Sopenharmony_ci * is unterminated (and thereby a literal), so as not to 79570af302Sopenharmony_ci * disallow long bracket expressions with short matches. */ 80570af302Sopenharmony_ci if (pos+(j+1) < PATH_MAX) { 81570af302Sopenharmony_ci buf[pos+j++] = pat[i]; 82570af302Sopenharmony_ci } else if (in_bracket) { 83570af302Sopenharmony_ci overflow = 1; 84570af302Sopenharmony_ci } else { 85570af302Sopenharmony_ci return 0; 86570af302Sopenharmony_ci } 87570af302Sopenharmony_ci /* If we consume any new components, the caller-passed type 88570af302Sopenharmony_ci * or dummy type from above is no longer valid. */ 89570af302Sopenharmony_ci type = 0; 90570af302Sopenharmony_ci } 91570af302Sopenharmony_ci buf[pos] = 0; 92570af302Sopenharmony_ci if (!*pat) { 93570af302Sopenharmony_ci /* If we consumed any components above, or if GLOB_MARK is 94570af302Sopenharmony_ci * requested and we don't yet know if the match is a dir, 95570af302Sopenharmony_ci * we must confirm the file exists and/or determine its type. 96570af302Sopenharmony_ci * 97570af302Sopenharmony_ci * If marking dirs, symlink type is inconclusive; we need the 98570af302Sopenharmony_ci * type for the symlink target, and therefore must try stat 99570af302Sopenharmony_ci * first unless type is known not to be a symlink. Otherwise, 100570af302Sopenharmony_ci * or if that fails, use lstat for determining existence to 101570af302Sopenharmony_ci * avoid false negatives in the case of broken symlinks. */ 102570af302Sopenharmony_ci struct stat st; 103570af302Sopenharmony_ci if ((flags & GLOB_MARK) && (!type||type==DT_LNK) && !stat(buf, &st)) { 104570af302Sopenharmony_ci if (S_ISDIR(st.st_mode)) type = DT_DIR; 105570af302Sopenharmony_ci else type = DT_REG; 106570af302Sopenharmony_ci } 107570af302Sopenharmony_ci if (!type && lstat(buf, &st)) { 108570af302Sopenharmony_ci if (errno!=ENOENT && (errfunc(buf, errno) || (flags & GLOB_ERR))) 109570af302Sopenharmony_ci return GLOB_ABORTED; 110570af302Sopenharmony_ci return 0; 111570af302Sopenharmony_ci } 112570af302Sopenharmony_ci if (append(tail, buf, pos, (flags & GLOB_MARK) && type==DT_DIR)) 113570af302Sopenharmony_ci return GLOB_NOSPACE; 114570af302Sopenharmony_ci return 0; 115570af302Sopenharmony_ci } 116570af302Sopenharmony_ci char *p2 = strchr(pat, '/'), saved_sep = '/'; 117570af302Sopenharmony_ci /* Check if the '/' was escaped and, if so, remove the escape char 118570af302Sopenharmony_ci * so that it will not be unpaired when passed to fnmatch. */ 119570af302Sopenharmony_ci if (p2 && !(flags & GLOB_NOESCAPE)) { 120570af302Sopenharmony_ci char *p; 121570af302Sopenharmony_ci for (p=p2; p>pat && p[-1]=='\\'; p--); 122570af302Sopenharmony_ci if ((p2-p)%2) { 123570af302Sopenharmony_ci p2--; 124570af302Sopenharmony_ci saved_sep = '\\'; 125570af302Sopenharmony_ci } 126570af302Sopenharmony_ci } 127570af302Sopenharmony_ci DIR *dir = opendir(pos ? buf : "."); 128570af302Sopenharmony_ci if (!dir) { 129570af302Sopenharmony_ci if (errfunc(buf, errno) || (flags & GLOB_ERR)) 130570af302Sopenharmony_ci return GLOB_ABORTED; 131570af302Sopenharmony_ci return 0; 132570af302Sopenharmony_ci } 133570af302Sopenharmony_ci int old_errno = errno; 134570af302Sopenharmony_ci struct dirent *de; 135570af302Sopenharmony_ci while (errno=0, de=readdir(dir)) { 136570af302Sopenharmony_ci /* Quickly skip non-directories when there's pattern left. */ 137570af302Sopenharmony_ci if (p2 && de->d_type && de->d_type!=DT_DIR && de->d_type!=DT_LNK) 138570af302Sopenharmony_ci continue; 139570af302Sopenharmony_ci 140570af302Sopenharmony_ci size_t l = strlen(de->d_name); 141570af302Sopenharmony_ci if (l >= PATH_MAX-pos) continue; 142570af302Sopenharmony_ci 143570af302Sopenharmony_ci if (p2) *p2 = 0; 144570af302Sopenharmony_ci 145570af302Sopenharmony_ci int fnm_flags= ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0) 146570af302Sopenharmony_ci | ((!(flags & GLOB_PERIOD)) ? FNM_PERIOD : 0); 147570af302Sopenharmony_ci 148570af302Sopenharmony_ci if (fnmatch(pat, de->d_name, fnm_flags)) 149570af302Sopenharmony_ci continue; 150570af302Sopenharmony_ci 151570af302Sopenharmony_ci /* With GLOB_PERIOD, don't allow matching . or .. unless 152570af302Sopenharmony_ci * fnmatch would match them with FNM_PERIOD rules in effect. */ 153570af302Sopenharmony_ci if (p2 && (flags & GLOB_PERIOD) && de->d_name[0]=='.' 154570af302Sopenharmony_ci && (!de->d_name[1] || de->d_name[1]=='.' && !de->d_name[2]) 155570af302Sopenharmony_ci && fnmatch(pat, de->d_name, fnm_flags | FNM_PERIOD)) 156570af302Sopenharmony_ci continue; 157570af302Sopenharmony_ci 158570af302Sopenharmony_ci memcpy(buf+pos, de->d_name, l+1); 159570af302Sopenharmony_ci if (p2) *p2 = saved_sep; 160570af302Sopenharmony_ci int r = do_glob(buf, pos+l, de->d_type, p2 ? p2 : "", flags, errfunc, tail); 161570af302Sopenharmony_ci if (r) { 162570af302Sopenharmony_ci closedir(dir); 163570af302Sopenharmony_ci return r; 164570af302Sopenharmony_ci } 165570af302Sopenharmony_ci } 166570af302Sopenharmony_ci int readerr = errno; 167570af302Sopenharmony_ci if (p2) *p2 = saved_sep; 168570af302Sopenharmony_ci closedir(dir); 169570af302Sopenharmony_ci if (readerr && (errfunc(buf, errno) || (flags & GLOB_ERR))) 170570af302Sopenharmony_ci return GLOB_ABORTED; 171570af302Sopenharmony_ci errno = old_errno; 172570af302Sopenharmony_ci return 0; 173570af302Sopenharmony_ci} 174570af302Sopenharmony_ci 175570af302Sopenharmony_cistatic int ignore_err(const char *path, int err) 176570af302Sopenharmony_ci{ 177570af302Sopenharmony_ci return 0; 178570af302Sopenharmony_ci} 179570af302Sopenharmony_ci 180570af302Sopenharmony_cistatic void freelist(struct match *head) 181570af302Sopenharmony_ci{ 182570af302Sopenharmony_ci struct match *match, *next; 183570af302Sopenharmony_ci for (match=head->next; match; match=next) { 184570af302Sopenharmony_ci next = match->next; 185570af302Sopenharmony_ci free(match); 186570af302Sopenharmony_ci } 187570af302Sopenharmony_ci} 188570af302Sopenharmony_ci 189570af302Sopenharmony_cistatic int sort(const void *a, const void *b) 190570af302Sopenharmony_ci{ 191570af302Sopenharmony_ci return strcmp(*(const char **)a, *(const char **)b); 192570af302Sopenharmony_ci} 193570af302Sopenharmony_ci 194570af302Sopenharmony_cistatic int expand_tilde(char **pat, char *buf, size_t *pos) 195570af302Sopenharmony_ci{ 196570af302Sopenharmony_ci char *p = *pat + 1; 197570af302Sopenharmony_ci size_t i = 0; 198570af302Sopenharmony_ci 199570af302Sopenharmony_ci char delim, *name_end = __strchrnul(p, '/'); 200570af302Sopenharmony_ci if ((delim = *name_end)) *name_end++ = 0; 201570af302Sopenharmony_ci *pat = name_end; 202570af302Sopenharmony_ci 203570af302Sopenharmony_ci char *home = *p ? NULL : getenv("HOME"); 204570af302Sopenharmony_ci if (!home) { 205570af302Sopenharmony_ci struct passwd pw, *res; 206570af302Sopenharmony_ci switch (*p ? getpwnam_r(p, &pw, buf, PATH_MAX, &res) 207570af302Sopenharmony_ci : getpwuid_r(getuid(), &pw, buf, PATH_MAX, &res)) { 208570af302Sopenharmony_ci case ENOMEM: 209570af302Sopenharmony_ci return GLOB_NOSPACE; 210570af302Sopenharmony_ci case 0: 211570af302Sopenharmony_ci if (!res) 212570af302Sopenharmony_ci default: 213570af302Sopenharmony_ci return GLOB_NOMATCH; 214570af302Sopenharmony_ci } 215570af302Sopenharmony_ci home = pw.pw_dir; 216570af302Sopenharmony_ci } 217570af302Sopenharmony_ci while (i < PATH_MAX - 2 && *home) 218570af302Sopenharmony_ci buf[i++] = *home++; 219570af302Sopenharmony_ci if (*home) 220570af302Sopenharmony_ci return GLOB_NOMATCH; 221570af302Sopenharmony_ci if ((buf[i] = delim)) 222570af302Sopenharmony_ci buf[++i] = 0; 223570af302Sopenharmony_ci *pos = i; 224570af302Sopenharmony_ci return 0; 225570af302Sopenharmony_ci} 226570af302Sopenharmony_ci 227570af302Sopenharmony_ciint glob(const char *restrict pat, int flags, int (*errfunc)(const char *path, int err), glob_t *restrict g) 228570af302Sopenharmony_ci{ 229570af302Sopenharmony_ci struct match head = { .next = NULL }, *tail = &head; 230570af302Sopenharmony_ci size_t cnt, i; 231570af302Sopenharmony_ci size_t offs = (flags & GLOB_DOOFFS) ? g->gl_offs : 0; 232570af302Sopenharmony_ci int error = 0; 233570af302Sopenharmony_ci char buf[PATH_MAX]; 234570af302Sopenharmony_ci 235570af302Sopenharmony_ci if (!errfunc) errfunc = ignore_err; 236570af302Sopenharmony_ci 237570af302Sopenharmony_ci if (!(flags & GLOB_APPEND)) { 238570af302Sopenharmony_ci g->gl_offs = offs; 239570af302Sopenharmony_ci g->gl_pathc = 0; 240570af302Sopenharmony_ci g->gl_pathv = NULL; 241570af302Sopenharmony_ci } 242570af302Sopenharmony_ci 243570af302Sopenharmony_ci if (*pat) { 244570af302Sopenharmony_ci char *p = strdup(pat); 245570af302Sopenharmony_ci if (!p) return GLOB_NOSPACE; 246570af302Sopenharmony_ci buf[0] = 0; 247570af302Sopenharmony_ci size_t pos = 0; 248570af302Sopenharmony_ci char *s = p; 249570af302Sopenharmony_ci if ((flags & (GLOB_TILDE | GLOB_TILDE_CHECK)) && *p == '~') 250570af302Sopenharmony_ci error = expand_tilde(&s, buf, &pos); 251570af302Sopenharmony_ci if (!error) 252570af302Sopenharmony_ci error = do_glob(buf, pos, 0, s, flags, errfunc, &tail); 253570af302Sopenharmony_ci free(p); 254570af302Sopenharmony_ci } 255570af302Sopenharmony_ci 256570af302Sopenharmony_ci if (error == GLOB_NOSPACE) { 257570af302Sopenharmony_ci freelist(&head); 258570af302Sopenharmony_ci return error; 259570af302Sopenharmony_ci } 260570af302Sopenharmony_ci 261570af302Sopenharmony_ci for (cnt=0, tail=head.next; tail; tail=tail->next, cnt++); 262570af302Sopenharmony_ci if (!cnt) { 263570af302Sopenharmony_ci if (flags & GLOB_NOCHECK) { 264570af302Sopenharmony_ci tail = &head; 265570af302Sopenharmony_ci if (append(&tail, pat, strlen(pat), 0)) 266570af302Sopenharmony_ci return GLOB_NOSPACE; 267570af302Sopenharmony_ci cnt++; 268570af302Sopenharmony_ci } else if (!error) 269570af302Sopenharmony_ci return GLOB_NOMATCH; 270570af302Sopenharmony_ci } 271570af302Sopenharmony_ci 272570af302Sopenharmony_ci if (flags & GLOB_APPEND) { 273570af302Sopenharmony_ci char **pathv = realloc(g->gl_pathv, (offs + g->gl_pathc + cnt + 1) * sizeof(char *)); 274570af302Sopenharmony_ci if (!pathv) { 275570af302Sopenharmony_ci freelist(&head); 276570af302Sopenharmony_ci return GLOB_NOSPACE; 277570af302Sopenharmony_ci } 278570af302Sopenharmony_ci g->gl_pathv = pathv; 279570af302Sopenharmony_ci offs += g->gl_pathc; 280570af302Sopenharmony_ci } else { 281570af302Sopenharmony_ci g->gl_pathv = malloc((offs + cnt + 1) * sizeof(char *)); 282570af302Sopenharmony_ci if (!g->gl_pathv) { 283570af302Sopenharmony_ci freelist(&head); 284570af302Sopenharmony_ci return GLOB_NOSPACE; 285570af302Sopenharmony_ci } 286570af302Sopenharmony_ci for (i=0; i<offs; i++) 287570af302Sopenharmony_ci g->gl_pathv[i] = NULL; 288570af302Sopenharmony_ci } 289570af302Sopenharmony_ci for (i=0, tail=head.next; i<cnt; tail=tail->next, i++) 290570af302Sopenharmony_ci g->gl_pathv[offs + i] = tail->name; 291570af302Sopenharmony_ci g->gl_pathv[offs + i] = NULL; 292570af302Sopenharmony_ci g->gl_pathc += cnt; 293570af302Sopenharmony_ci 294570af302Sopenharmony_ci if (!(flags & GLOB_NOSORT)) 295570af302Sopenharmony_ci qsort(g->gl_pathv+offs, cnt, sizeof(char *), sort); 296570af302Sopenharmony_ci 297570af302Sopenharmony_ci return error; 298570af302Sopenharmony_ci} 299570af302Sopenharmony_ci 300570af302Sopenharmony_civoid globfree(glob_t *g) 301570af302Sopenharmony_ci{ 302570af302Sopenharmony_ci size_t i; 303570af302Sopenharmony_ci for (i=0; i<g->gl_pathc; i++) 304570af302Sopenharmony_ci free(g->gl_pathv[g->gl_offs + i] - offsetof(struct match, name)); 305570af302Sopenharmony_ci free(g->gl_pathv); 306570af302Sopenharmony_ci g->gl_pathc = 0; 307570af302Sopenharmony_ci g->gl_pathv = NULL; 308570af302Sopenharmony_ci} 309570af302Sopenharmony_ci 310570af302Sopenharmony_ciweak_alias(glob, glob64); 311570af302Sopenharmony_ciweak_alias(globfree, globfree64); 312