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(*__errno_location()).h> |
9 | #include <stddef.h> |
10 | #include "libc.h" |
11 | |
12 | struct match |
13 | { |
14 | struct match *next; |
15 | char name[1]; |
16 | }; |
17 | |
18 | static 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 | |
39 | static 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; |
| 16 | | Assuming 'new' is non-null | |
|
| |
43 | (*tail)->next = new; |
44 | new->next = NULL((void*)0); |
45 | strcpy(new->name, name); |
46 | if (mark) strcat(new->name, "/"); |
| |
| 19 | | String copy function overflows destination buffer |
|
47 | *tail = new; |
48 | return 0; |
49 | } |
50 | |
51 | static 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_NOESCAPE0x40) ? FNM_NOESCAPE0x2 : 0) |
| |
60 | | ((!(flags & GLOB_PERIOD0x80)) ? FNM_PERIOD0x4 : 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_NOESCAPE0x40)); |
70 | if (*d == '/' && !*(d+1)) l = 0; |
71 | |
72 | |
73 | dir = opendir(*d ? d : "."); |
| |
74 | error = errno(*__errno_location()); |
75 | if (!dir) { |
| 6 | | Assuming 'dir' is non-null | |
|
| |
76 | |
77 | if (error == ENOTDIR20) return 0; |
78 | if (error == EACCES13 && !*p) { |
79 | struct stat st; |
80 | if (!stat(d, &st) && S_ISDIR(st.st_mode)(((st.st_mode) & 0170000) == 0040000)) { |
81 | if (append(tail, d, l, l)) |
82 | return GLOB_NOSPACE1; |
83 | return 0; |
84 | } |
85 | } |
86 | if (errfunc(d, error) || (flags & GLOB_ERR0x01)) |
87 | return GLOB_ABORTED2; |
88 | return 0; |
89 | } |
90 | if (!*p) { |
| |
91 | error = append(tail, d, l, l) ? GLOB_NOSPACE1 : 0; |
92 | closedir(dir); |
93 | return error; |
94 | } |
95 | while (!(error = readdir_r(dir, &de_buf, &de)) && de) { |
| 9 | | Assuming 'error' is zero | |
|
| 10 | | Loop condition is true. Entering loop body | |
|
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)(((de->d_type<<12) & 0170000) == 0040000) && !S_ISLNK(de->d_type<<12)(((de->d_type<<12) & 0170000) == 0120000)) |
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_MARK0x02) { |
| |
118 | if (de->d_type && !S_ISLNK(de->d_type<<12)(((de->d_type<<12) & 0170000) == 0120000)) |
119 | mark = S_ISDIR(de->d_type<<12)(((de->d_type<<12) & 0170000) == 0040000); |
120 | else { |
121 | struct stat st; |
122 | stat(name, &st); |
123 | mark = S_ISDIR(st.st_mode)(((st.st_mode) & 0170000) == 0040000); |
124 | } |
125 | } |
126 | if (append(tail, name, l+de->d_reclen+1, mark)) { |
| |
127 | closedir(dir); |
128 | return GLOB_NOSPACE1; |
129 | } |
130 | } |
131 | } |
132 | closedir(dir); |
133 | if (error && (errfunc(d, error) || (flags & GLOB_ERR0x01))) |
134 | return GLOB_ABORTED2; |
135 | return 0; |
136 | } |
137 | |
138 | static int ignore_err(const char *path, int err) |
139 | { |
140 | return 0; |
141 | } |
142 | |
143 | static 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 | |
152 | static int sort(const void *a, const void *b) |
153 | { |
154 | return strcmp(*(const char **)a, *(const char **)b); |
155 | } |
156 | |
157 | int 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((void*)0) }, *tail = &head; |
161 | size_t cnt, i; |
162 | size_t offs = (flags & GLOB_DOOFFS0x08) ? 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_MAX4096) return GLOB_NOSPACE1; |
173 | |
174 | if (!errfunc) errfunc = ignore_err; |
175 | |
176 | if (!(flags & GLOB_APPEND0x20)) { |
177 | g->gl_offs = offs; |
178 | g->gl_pathc = 0; |
179 | g->gl_pathv = NULL((void*)0); |
180 | } |
181 | |
182 | if (*p) error = match_in_dir(d, p, flags, errfunc, &tail); |
183 | if (error == GLOB_NOSPACE1) { |
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_NOCHECK0x10) { |
191 | tail = &head; |
192 | if (append(&tail, pat, strlen(pat), 0)) |
193 | return GLOB_NOSPACE1; |
194 | cnt++; |
195 | } else |
196 | return GLOB_NOMATCH3; |
197 | } |
198 | |
199 | if (flags & GLOB_APPEND0x20) { |
200 | char **pathv = realloc(g->gl_pathv, (offs + g->gl_pathc + cnt + 1) * sizeof(char *)); |
201 | if (!pathv) { |
202 | freelist(&head); |
203 | return GLOB_NOSPACE1; |
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_NOSPACE1; |
212 | } |
213 | for (i=0; i<offs; i++) |
214 | g->gl_pathv[i] = NULL((void*)0); |
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((void*)0); |
219 | g->gl_pathc += cnt; |
220 | |
221 | if (!(flags & GLOB_NOSORT0x04)) |
222 | qsort(g->gl_pathv+offs, cnt, sizeof(char *), sort); |
223 | |
224 | return error; |
225 | } |
226 | |
227 | void 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)__builtin_offsetof(struct match, name)); |
232 | free(g->gl_pathv); |
233 | g->gl_pathc = 0; |
234 | g->gl_pathv = NULL((void*)0); |
235 | } |
236 | |
237 | LFS64(glob)extern __typeof(glob) glob64 __attribute__((weak, alias("glob" ))); |
238 | LFS64(globfree)extern __typeof(globfree) globfree64 __attribute__((weak, alias ("globfree"))); |