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