1#include <glob.h> 2#include <fnmatch.h> 3#include <sys/stat.h> 4#include <dirent.h> 5#include <limits.h> 6#include <string.h> 7#include <stdlib.h> 8#include <errno.h> 9#include <stddef.h> 10#include "libc.h" 11 12struct match 13{ 14 struct match *next; 15 char name[1]; 16}; 17 18static int is_literal(const char *p, int useesc) 19{ 20 int bracket = 0; 21 for (; *p; p++) { 22 switch (*p) { 23 case '\\': 24 if (!useesc) break; 25 case '?': 26 case '*': 27 return 0; 28 case '[': 29 bracket = 1; 30 break; 31 case ']': 32 if (bracket) return 0; 33 break; 34 } 35 } 36 return 1; 37} 38 39static int append(struct match **tail, const char *name, size_t len, int mark) 40{ 41 struct match *new = malloc(sizeof(struct match) + len + 1); 42 if (!new) return -1; 43 (*tail)->next = new; 44 new->next = NULL; 45 strcpy(new->name, name); 46 if (mark) strcat(new->name, "/"); 47 *tail = new; 48 return 0; 49} 50 51static int match_in_dir(const char *d, const char *p, int flags, int (*errfunc)(const char *path, int err), struct match **tail) 52{ 53 DIR *dir; 54 struct dirent de_buf, *de; 55 char pat[strlen(p)+1]; 56 char *p2; 57 size_t l = strlen(d); 58 int literal; 59 int fnm_flags= ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0) 60 | ((!(flags & GLOB_PERIOD)) ? FNM_PERIOD : 0); 61 int error; 62 63 if ((p2 = strchr(p, '/'))) { 64 strcpy(pat, p); 65 pat[p2-p] = 0; 66 for (; *p2 == '/'; p2++); 67 p = pat; 68 } 69 literal = is_literal(p, !(flags & GLOB_NOESCAPE)); 70 if (*d == '/' && !*(d+1)) l = 0; 71 72 /* rely on opendir failing for nondirectory objects */ 73 dir = opendir(*d ? d : "."); 74 error = errno; 75 if (!dir) { 76 /* this is not an error -- we let opendir call stat for us */ 77 if (error == ENOTDIR) return 0; 78 if (error == EACCES && !*p) { 79 struct stat st; 80 if (!stat(d, &st) && S_ISDIR(st.st_mode)) { 81 if (append(tail, d, l, l)) 82 return GLOB_NOSPACE; 83 return 0; 84 } 85 } 86 if (errfunc(d, error) || (flags & GLOB_ERR)) 87 return GLOB_ABORTED; 88 return 0; 89 } 90 if (!*p) { 91 error = append(tail, d, l, l) ? GLOB_NOSPACE : 0; 92 closedir(dir); 93 return error; 94 } 95 while (!(error = readdir_r(dir, &de_buf, &de)) && de) { 96 char namebuf[l+de->d_reclen+2], *name = namebuf; 97 if (!literal && fnmatch(p, de->d_name, fnm_flags)) 98 continue; 99 if (literal && strcmp(p, de->d_name)) 100 continue; 101 if (p2 && de->d_type && !S_ISDIR(de->d_type<<12) && !S_ISLNK(de->d_type<<12)) 102 continue; 103 if (*d) { 104 memcpy(name, d, l); 105 name[l] = '/'; 106 strcpy(name+l+1, de->d_name); 107 } else { 108 name = de->d_name; 109 } 110 if (p2) { 111 if ((error = match_in_dir(name, p2, flags, errfunc, tail))) { 112 closedir(dir); 113 return error; 114 } 115 } else { 116 int mark = 0; 117 if (flags & GLOB_MARK) { 118 if (de->d_type && !S_ISLNK(de->d_type<<12)) 119 mark = S_ISDIR(de->d_type<<12); 120 else { 121 struct stat st; 122 stat(name, &st); 123 mark = S_ISDIR(st.st_mode); 124 } 125 } 126 if (append(tail, name, l+de->d_reclen+1, mark)) { 127 closedir(dir); 128 return GLOB_NOSPACE; 129 } 130 } 131 } 132 closedir(dir); 133 if (error && (errfunc(d, error) || (flags & GLOB_ERR))) 134 return GLOB_ABORTED; 135 return 0; 136} 137 138static int ignore_err(const char *path, int err) 139{ 140 return 0; 141} 142 143static void freelist(struct match *head) 144{ 145 struct match *match, *next; 146 for (match=head->next; match; match=next) { 147 next = match->next; 148 free(match); 149 } 150} 151 152static int sort(const void *a, const void *b) 153{ 154 return strcmp(*(const char **)a, *(const char **)b); 155} 156 157int glob(const char *restrict pat, int flags, int (*errfunc)(const char *path, int err), glob_t *restrict g) 158{ 159 const char *p=pat, *d; 160 struct match head = { .next = NULL }, *tail = &head; 161 size_t cnt, i; 162 size_t offs = (flags & GLOB_DOOFFS) ? g->gl_offs : 0; 163 int error = 0; 164 165 if (*p == '/') { 166 for (; *p == '/'; p++); 167 d = "/"; 168 } else { 169 d = ""; 170 } 171 172 if (strlen(p) > PATH_MAX) return GLOB_NOSPACE; 173 174 if (!errfunc) errfunc = ignore_err; 175 176 if (!(flags & GLOB_APPEND)) { 177 g->gl_offs = offs; 178 g->gl_pathc = 0; 179 g->gl_pathv = NULL; 180 } 181 182 if (*p) error = match_in_dir(d, p, flags, errfunc, &tail); 183 if (error == GLOB_NOSPACE) { 184 freelist(&head); 185 return error; 186 } 187 188 for (cnt=0, tail=head.next; tail; tail=tail->next, cnt++); 189 if (!cnt) { 190 if (flags & GLOB_NOCHECK) { 191 tail = &head; 192 if (append(&tail, pat, strlen(pat), 0)) 193 return GLOB_NOSPACE; 194 cnt++; 195 } else 196 return GLOB_NOMATCH; 197 } 198 199 if (flags & GLOB_APPEND) { 200 char **pathv = realloc(g->gl_pathv, (offs + g->gl_pathc + cnt + 1) * sizeof(char *)); 201 if (!pathv) { 202 freelist(&head); 203 return GLOB_NOSPACE; 204 } 205 g->gl_pathv = pathv; 206 offs += g->gl_pathc; 207 } else { 208 g->gl_pathv = malloc((offs + cnt + 1) * sizeof(char *)); 209 if (!g->gl_pathv) { 210 freelist(&head); 211 return GLOB_NOSPACE; 212 } 213 for (i=0; i<offs; i++) 214 g->gl_pathv[i] = NULL; 215 } 216 for (i=0, tail=head.next; i<cnt; tail=tail->next, i++) 217 g->gl_pathv[offs + i] = tail->name; 218 g->gl_pathv[offs + i] = NULL; 219 g->gl_pathc += cnt; 220 221 if (!(flags & GLOB_NOSORT)) 222 qsort(g->gl_pathv+offs, cnt, sizeof(char *), sort); 223 224 return error; 225} 226 227void globfree(glob_t *g) 228{ 229 size_t i; 230 for (i=0; i<g->gl_pathc; i++) 231 free(g->gl_pathv[g->gl_offs + i] - offsetof(struct match, name)); 232 free(g->gl_pathv); 233 g->gl_pathc = 0; 234 g->gl_pathv = NULL; 235} 236 237LFS64(glob); 238LFS64(globfree); 239