Line | Count | Source |
1 | | // SPDX-License-Identifier: LGPL-2.1-or-later |
2 | | // SPDX-FileCopyrightText: (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. |
3 | | |
4 | | #include <common.h> |
5 | | #include <errno.h> |
6 | | #include <fs.h> |
7 | | #include <linux/stat.h> |
8 | | #include <malloc.h> |
9 | | #include <xfuncs.h> |
10 | | #include <fnmatch.h> |
11 | | #include <qsort.h> |
12 | | #include <glob.h> |
13 | | |
14 | | static int glob_in_dir (const char *pattern, const char *directory, |
15 | | int flags, |
16 | | int (*errfunc) (const char *, int), |
17 | | glob_t * pglob); |
18 | | static int prefix_array (const char *prefix, char **array, size_t n, |
19 | | int add_slash); |
20 | | |
21 | | /* Return nonzero if PATTERN contains any metacharacters. |
22 | | Metacharacters can be quoted with backslashes if QUOTE is nonzero. */ |
23 | | int glob_pattern_p(const char *pattern, int quote) |
24 | 0 | { |
25 | 0 | const char *p; |
26 | 0 | int open = 0; |
27 | |
|
28 | 0 | for (p = pattern; *p != '\0'; ++p) |
29 | 0 | switch (*p) { |
30 | 0 | case '?': |
31 | 0 | case '*': |
32 | 0 | return 1; |
33 | | |
34 | 0 | case '\\': |
35 | 0 | if (quote && p[1] != '\0') |
36 | 0 | ++p; |
37 | 0 | break; |
38 | | |
39 | 0 | case '[': |
40 | 0 | open = 1; |
41 | 0 | break; |
42 | | |
43 | 0 | case ']': |
44 | 0 | if (open) |
45 | 0 | return 1; |
46 | 0 | break; |
47 | 0 | } |
48 | | |
49 | 0 | return 0; |
50 | 0 | } |
51 | | |
52 | | #ifdef CONFIG_GLOB_SORT |
53 | | /* Do a collated comparison of A and B. */ |
54 | | static int collated_compare(const void *a, const void *b) |
55 | 0 | { |
56 | 0 | const char *const s1 = *(const char *const *)a; |
57 | 0 | const char *const s2 = *(const char *const *)b; |
58 | |
|
59 | 0 | if (s1 == s2) |
60 | 0 | return 0; |
61 | 0 | if (s1 == NULL) |
62 | 0 | return 1; |
63 | 0 | if (s2 == NULL) |
64 | 0 | return -1; |
65 | 0 | return strcmp(s1, s2); |
66 | 0 | } |
67 | | #endif |
68 | | |
69 | | /* Do glob searching for PATTERN, placing results in PGLOB. |
70 | | The bits defined above may be set in FLAGS. |
71 | | If a directory cannot be opened or read and ERRFUNC is not nil, |
72 | | it is called with the pathname that caused the error, and the |
73 | | `errno' value from the failing call; if it returns non-zero |
74 | | `glob' returns GLOB_ABEND; if it returns zero, the error is ignored. |
75 | | If memory cannot be allocated for PGLOB, GLOB_NOSPACE is returned. |
76 | | Otherwise, `glob' returns zero. */ |
77 | | int glob(const char *pattern, int flags, |
78 | | int (*errfunc) (const char *, int), glob_t *pglob) |
79 | 0 | { |
80 | 0 | const char *filename, *dirname = NULL; |
81 | 0 | size_t dirlen; |
82 | 0 | int status; |
83 | 0 | int oldcount; |
84 | |
|
85 | 0 | if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0) { |
86 | 0 | errno = EINVAL; |
87 | 0 | return -1; |
88 | 0 | } |
89 | | |
90 | | /* Find the filename. */ |
91 | 0 | filename = strrchr(pattern, '/'); |
92 | 0 | if (filename == NULL) { |
93 | 0 | filename = pattern; |
94 | 0 | dirname = strdup_const("."); |
95 | 0 | dirlen = 0; |
96 | 0 | } else if (filename == pattern) { |
97 | | /* "/pattern". */ |
98 | 0 | dirname = strdup_const("/"); |
99 | 0 | dirlen = 1; |
100 | 0 | ++filename; |
101 | 0 | } else { |
102 | 0 | char *buf; |
103 | |
|
104 | 0 | dirlen = filename - pattern; |
105 | 0 | buf = (char *)xmalloc(dirlen + 1); |
106 | 0 | memcpy(buf, pattern, dirlen); |
107 | 0 | buf[dirlen] = '\0'; |
108 | 0 | ++filename; |
109 | |
|
110 | 0 | dirname = buf; |
111 | 0 | } |
112 | |
|
113 | 0 | if (filename[0] == '\0' && dirlen > 1) { |
114 | | /* "pattern/". Expand "pattern", appending slashes. */ |
115 | 0 | int val = glob(dirname, flags | GLOB_MARK, errfunc, pglob); |
116 | 0 | if (val == 0) |
117 | 0 | pglob->gl_flags = |
118 | 0 | (pglob-> |
119 | 0 | gl_flags & ~GLOB_MARK) | (flags & GLOB_MARK); |
120 | 0 | status = val; |
121 | 0 | goto out; |
122 | 0 | } |
123 | | |
124 | 0 | if (!(flags & GLOB_APPEND)) { |
125 | 0 | pglob->gl_pathc = 0; |
126 | 0 | pglob->gl_pathv = NULL; |
127 | 0 | } |
128 | |
|
129 | 0 | oldcount = pglob->gl_pathc; |
130 | |
|
131 | 0 | if (glob_pattern_p(dirname, !(flags & GLOB_NOESCAPE))) { |
132 | | /* The directory name contains metacharacters, so we |
133 | | have to glob for the directory, and then glob for |
134 | | the pattern in each directory found. */ |
135 | 0 | glob_t dirs; |
136 | 0 | register int i; |
137 | 0 | status = glob(dirname, |
138 | 0 | ((flags & |
139 | 0 | (GLOB_ERR | GLOB_NOCHECK | GLOB_NOESCAPE)) | |
140 | 0 | GLOB_NOSORT), errfunc, &dirs); |
141 | 0 | if (status != 0) |
142 | 0 | goto out; |
143 | | |
144 | | /* We have successfully globbed the preceding directory name. |
145 | | For each name we found, call glob_in_dir on it and FILENAME, |
146 | | appending the results to PGLOB. */ |
147 | 0 | for (i = 0; i < dirs.gl_pathc; ++i) { |
148 | 0 | int oldcount1; |
149 | |
|
150 | 0 | oldcount1 = pglob->gl_pathc; |
151 | 0 | status = glob_in_dir(filename, dirs.gl_pathv[i], |
152 | 0 | (flags | GLOB_APPEND) & |
153 | 0 | ~GLOB_NOCHECK, errfunc, pglob); |
154 | 0 | if (status == GLOB_NOMATCH) { |
155 | | /* No matches in this directory. Try the next. */ |
156 | 0 | continue; |
157 | 0 | } |
158 | | |
159 | 0 | if (status != 0) { |
160 | 0 | globfree(pglob); |
161 | 0 | goto out; |
162 | 0 | } |
163 | | |
164 | | /* Stick the directory on the front of each name. */ |
165 | 0 | prefix_array(dirs.gl_pathv[i], |
166 | 0 | &pglob->gl_pathv[oldcount1], |
167 | 0 | pglob->gl_pathc - oldcount1, |
168 | 0 | flags & GLOB_MARK); |
169 | 0 | } |
170 | | |
171 | 0 | globfree(&dirs); |
172 | 0 | flags |= GLOB_MAGCHAR; |
173 | |
|
174 | 0 | if (pglob->gl_pathc == oldcount) { |
175 | | /* No matches. */ |
176 | |
|
177 | 0 | if (flags & GLOB_NOCHECK) { |
178 | 0 | size_t len = strlen(pattern) + 1; |
179 | 0 | char *patcopy = (char *)xmalloc(len); |
180 | 0 | memcpy(patcopy, pattern, len); |
181 | |
|
182 | 0 | pglob->gl_pathv |
183 | 0 | = (char **)xrealloc(pglob->gl_pathv, |
184 | 0 | (pglob->gl_pathc + |
185 | 0 | ((flags & GLOB_DOOFFS) ? |
186 | 0 | pglob->gl_offs : 0) + |
187 | 0 | 1 + 1) * |
188 | 0 | sizeof(char *)); |
189 | |
|
190 | 0 | if (flags & GLOB_DOOFFS) |
191 | 0 | while (pglob->gl_pathc < pglob->gl_offs) |
192 | 0 | pglob->gl_pathv[pglob-> |
193 | 0 | gl_pathc++] = |
194 | 0 | NULL; |
195 | |
|
196 | 0 | pglob->gl_pathv[pglob->gl_pathc++] = patcopy; |
197 | 0 | pglob->gl_pathv[pglob->gl_pathc] = NULL; |
198 | 0 | pglob->gl_flags = flags; |
199 | 0 | } else { |
200 | 0 | status = GLOB_NOMATCH; |
201 | 0 | goto out; |
202 | 0 | } |
203 | 0 | } |
204 | 0 | } else { |
205 | 0 | status = glob_in_dir(filename, dirname, flags, errfunc, pglob); |
206 | 0 | if (status != 0) |
207 | 0 | goto out; |
208 | | |
209 | 0 | if (dirlen > 0) { |
210 | | /* Stick the directory on the front of each name. */ |
211 | 0 | prefix_array(dirname, |
212 | 0 | &pglob->gl_pathv[oldcount], |
213 | 0 | pglob->gl_pathc - oldcount, |
214 | 0 | flags & GLOB_MARK); |
215 | 0 | } |
216 | 0 | } |
217 | | |
218 | 0 | if (flags & GLOB_MARK) { |
219 | | /* Append slashes to directory names. glob_in_dir has already |
220 | | allocated the extra character for us. */ |
221 | 0 | int i; |
222 | 0 | struct stat st; |
223 | 0 | for (i = oldcount; i < pglob->gl_pathc; ++i) |
224 | 0 | if (stat(pglob->gl_pathv[i], &st) == 0 && |
225 | 0 | S_ISDIR(st.st_mode)) |
226 | 0 | strcat(pglob->gl_pathv[i], "/"); |
227 | 0 | } |
228 | 0 | #ifdef CONFIG_GLOB_SORT |
229 | 0 | if (!(flags & GLOB_NOSORT)) |
230 | | /* Sort the vector. */ |
231 | 0 | qsort(&pglob->gl_pathv[oldcount], |
232 | 0 | pglob->gl_pathc - oldcount, |
233 | 0 | sizeof(char *), collated_compare); |
234 | 0 | #endif |
235 | 0 | status = 0; |
236 | 0 | out: |
237 | 0 | free_const(dirname); |
238 | |
|
239 | 0 | return status; |
240 | 0 | } |
241 | | |
242 | | /* Prepend DIRNAME to each of N members of ARRAY, replacing ARRAY's |
243 | | elements in place. Return nonzero if out of memory, zero if successful. |
244 | | A slash is inserted between DIRNAME and each elt of ARRAY, |
245 | | unless DIRNAME is just "/". Each old element of ARRAY is freed. |
246 | | If ADD_SLASH is non-zero, allocate one character more than |
247 | | necessary, so that a slash can be appended later. */ |
248 | | static int prefix_array(const char *dirname, char **array, size_t n, |
249 | | int add_slash) |
250 | 0 | { |
251 | 0 | register size_t i; |
252 | 0 | size_t dirlen = strlen(dirname); |
253 | |
|
254 | 0 | if (dirlen == 1 && dirname[0] == '/') |
255 | | /* DIRNAME is just "/", so normal prepending would get us "//foo". |
256 | | We want "/foo" instead, so don't prepend any chars from DIRNAME. */ |
257 | 0 | dirlen = 0; |
258 | |
|
259 | 0 | for (i = 0; i < n; ++i) { |
260 | 0 | size_t eltlen = strlen(array[i]) + 1; |
261 | 0 | char *new = |
262 | 0 | (char *)xmalloc(dirlen + 1 + eltlen + (add_slash ? 1 : 0)); |
263 | |
|
264 | 0 | memcpy(new, dirname, dirlen); |
265 | 0 | new[dirlen] = '/'; |
266 | 0 | memcpy(&new[dirlen + 1], array[i], eltlen); |
267 | 0 | free(array[i]); |
268 | 0 | array[i] = new; |
269 | 0 | } |
270 | |
|
271 | 0 | return 0; |
272 | 0 | } |
273 | | |
274 | | /* Like `glob', but PATTERN is a final pathname component, |
275 | | and matches are searched for in DIRECTORY. |
276 | | The GLOB_NOSORT bit in FLAGS is ignored. No sorting is ever done. |
277 | | The GLOB_APPEND flag is assumed to be set (always appends). */ |
278 | | static int glob_in_dir(const char *pattern, const char *directory, |
279 | | int flags, int (*errfunc) (const char *, int), glob_t *pglob) |
280 | 0 | { |
281 | 0 | void *stream = NULL; |
282 | |
|
283 | 0 | struct globlink { |
284 | 0 | struct globlink *next; |
285 | 0 | char *name; |
286 | 0 | }; |
287 | 0 | struct globlink *names = NULL; |
288 | 0 | size_t nfound = 0; |
289 | 0 | int meta; |
290 | |
|
291 | 0 | meta = glob_pattern_p(pattern, !(flags & GLOB_NOESCAPE)); |
292 | |
|
293 | 0 | if (meta) |
294 | 0 | flags |= GLOB_MAGCHAR; |
295 | |
|
296 | 0 | if (meta) |
297 | 0 | stream = opendir(directory); |
298 | |
|
299 | 0 | if (stream == NULL) { |
300 | 0 | if ((errfunc != NULL && (*errfunc) (directory, errno)) || |
301 | 0 | (flags & GLOB_ERR)) |
302 | 0 | return GLOB_ABORTED; |
303 | 0 | } |
304 | | |
305 | 0 | while (1) { |
306 | 0 | const char *name; |
307 | 0 | size_t len; |
308 | |
|
309 | 0 | struct dirent *d = readdir((DIR *) stream); |
310 | 0 | if (d == NULL) |
311 | 0 | break; |
312 | | // if (! (d->d_ino != 0)) |
313 | | // continue; |
314 | 0 | name = d->d_name; |
315 | |
|
316 | 0 | if ((!meta && strcmp(pattern, name) == 0) |
317 | 0 | || fnmatch(pattern, name, |
318 | 0 | (!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0) | |
319 | 0 | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0)) == 0) { |
320 | 0 | struct globlink *new = |
321 | 0 | (struct globlink *)xmalloc(sizeof(struct globlink)); |
322 | 0 | len = strlen(name); |
323 | 0 | new->name = xmalloc(len + ((flags & GLOB_MARK) ? 1 : 0) + 1); |
324 | 0 | memcpy(new->name, name, len); |
325 | 0 | new->name[len] = '\0'; |
326 | 0 | new->next = names; |
327 | 0 | names = new; |
328 | 0 | ++nfound; |
329 | 0 | if (!meta) |
330 | 0 | break; |
331 | 0 | } |
332 | 0 | } |
333 | |
|
334 | 0 | if (nfound == 0 && (flags & GLOB_NOCHECK)) { |
335 | 0 | size_t len = strlen(pattern); |
336 | 0 | nfound = 1; |
337 | 0 | names = (struct globlink *)xmalloc(sizeof(struct globlink)); |
338 | 0 | names->next = NULL; |
339 | 0 | names->name = |
340 | 0 | (char *)xmalloc(len + (flags & GLOB_MARK ? 1 : 0) + 1); |
341 | 0 | memcpy(names->name, pattern, len); |
342 | 0 | names->name[len] = '\0'; |
343 | 0 | } |
344 | |
|
345 | 0 | pglob->gl_pathv |
346 | 0 | = (char **)xrealloc(pglob->gl_pathv, |
347 | 0 | (pglob->gl_pathc + |
348 | 0 | ((flags & GLOB_DOOFFS) ? pglob->gl_offs : 0) + |
349 | 0 | nfound + 1) * sizeof(char *)); |
350 | |
|
351 | 0 | if (flags & GLOB_DOOFFS) |
352 | 0 | while (pglob->gl_pathc < pglob->gl_offs) |
353 | 0 | pglob->gl_pathv[pglob->gl_pathc++] = NULL; |
354 | |
|
355 | 0 | while (names) { |
356 | 0 | struct globlink *tmp; |
357 | |
|
358 | 0 | pglob->gl_pathv[pglob->gl_pathc++] = names->name; |
359 | 0 | tmp = names; |
360 | 0 | names = names->next; |
361 | 0 | free(tmp); |
362 | 0 | } |
363 | 0 | pglob->gl_pathv[pglob->gl_pathc] = NULL; |
364 | |
|
365 | 0 | pglob->gl_flags = flags; |
366 | |
|
367 | 0 | { |
368 | 0 | int save = errno; |
369 | 0 | (void)closedir((DIR *) stream); |
370 | 0 | errno = save; |
371 | 0 | } |
372 | 0 | return nfound == 0 ? GLOB_NOMATCH : 0; |
373 | 0 | } |
374 | | |
375 | | /* Free storage allocated in PGLOB by a previous `glob' call. */ |
376 | | void globfree(glob_t *pglob) |
377 | 0 | { |
378 | 0 | if (pglob->gl_pathv != NULL) { |
379 | 0 | int i = pglob->gl_flags & GLOB_DOOFFS ? pglob->gl_offs : 0; |
380 | 0 | for (; i < pglob->gl_pathc; ++i) |
381 | 0 | if (pglob->gl_pathv[i] != NULL) |
382 | 0 | free(pglob->gl_pathv[i]); |
383 | 0 | free(pglob->gl_pathv); |
384 | 0 | } |
385 | 0 | } |