Add basic support for mini2440 board to barebox.
[barebox-mini2440.git] / lib / glob.c
bloba5e3d1d67a6f70749b04470f2e899a590fbe77de
1 /* Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
3 This library is free software; you can redistribute it and/or
4 modify it under the terms of the GNU Library General Public License as
5 published by the Free Software Foundation; either version 2 of the
6 License, or (at your option) any later version.
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
13 You should have received a copy of the GNU Library General Public
14 License along with this library; see the file COPYING.LIB. If
15 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
16 Cambridge, MA 02139, USA. */
18 #include <common.h>
19 #include <errno.h>
20 #include <fs.h>
21 #include <linux/stat.h>
22 #include <malloc.h>
23 #include <xfuncs.h>
24 #include <fnmatch.h>
25 #define _GNU_SOURCE
26 #include <glob.h>
28 #ifdef CONFIG_GLOB
30 extern __ptr_t(*__glob_opendir_hook) __P((const char *directory));
31 extern void (*__glob_closedir_hook) __P((__ptr_t stream));
32 extern const char *(*__glob_readdir_hook) __P((__ptr_t stream));
34 static int glob_in_dir __P((const char *pattern, const char *directory,
35 int flags,
36 int (*errfunc) __P((const char *, int)),
37 glob_t * pglob));
38 static int prefix_array __P((const char *prefix, char **array, size_t n,
39 int add_slash));
41 #ifdef __GLOB64
42 extern int glob_pattern_p(const char *pattern, int quote);
43 #else
44 /* Return nonzero if PATTERN contains any metacharacters.
45 Metacharacters can be quoted with backslashes if QUOTE is nonzero. */
46 int glob_pattern_p(const char *pattern, int quote)
48 const char *p;
49 int open = 0;
51 for (p = pattern; *p != '\0'; ++p)
52 switch (*p) {
53 case '?':
54 case '*':
55 return 1;
57 case '\\':
58 if (quote && p[1] != '\0')
59 ++p;
60 break;
62 case '[':
63 open = 1;
64 break;
66 case ']':
67 if (open)
68 return 1;
69 break;
72 return 0;
74 #endif
76 #ifdef CONFIG_GLOB_SORT
77 /* Do a collated comparison of A and B. */
78 static int collated_compare(a, b)
79 const __ptr_t a;
80 const __ptr_t b;
82 const char *const s1 = *(const char *const *)a;
83 const char *const s2 = *(const char *const *)b;
85 if (s1 == s2)
86 return 0;
87 if (s1 == NULL)
88 return 1;
89 if (s2 == NULL)
90 return -1;
91 return strcmp(s1, s2);
93 #endif
95 /* Do glob searching for PATTERN, placing results in PGLOB.
96 The bits defined above may be set in FLAGS.
97 If a directory cannot be opened or read and ERRFUNC is not nil,
98 it is called with the pathname that caused the error, and the
99 `errno' value from the failing call; if it returns non-zero
100 `glob' returns GLOB_ABEND; if it returns zero, the error is ignored.
101 If memory cannot be allocated for PGLOB, GLOB_NOSPACE is returned.
102 Otherwise, `glob' returns zero. */
103 int glob(pattern, flags, errfunc, pglob)
104 const char *pattern;
105 int flags;
106 int (*errfunc) __P((const char *, int));
107 glob_t *pglob;
109 const char *filename;
110 char *dirname = NULL;
111 size_t dirlen;
112 int status;
113 int oldcount;
115 if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0) {
116 errno = EINVAL;
117 return -1;
120 /* Find the filename. */
121 filename = strrchr(pattern, '/');
122 if (filename == NULL) {
123 filename = pattern;
124 dirname = strdup(".");
125 dirlen = 0;
126 } else if (filename == pattern) {
127 /* "/pattern". */
128 dirname = strdup("/");
129 dirlen = 1;
130 ++filename;
131 } else {
132 dirlen = filename - pattern;
133 dirname = (char *)xmalloc(dirlen + 1);
134 memcpy(dirname, pattern, dirlen);
135 dirname[dirlen] = '\0';
136 ++filename;
139 if (filename[0] == '\0' && dirlen > 1) {
140 /* "pattern/". Expand "pattern", appending slashes. */
141 int val = glob(dirname, flags | GLOB_MARK, errfunc, pglob);
142 if (val == 0)
143 pglob->gl_flags =
144 (pglob->
145 gl_flags & ~GLOB_MARK) | (flags & GLOB_MARK);
146 status = val;
147 goto out;
150 if (!(flags & GLOB_APPEND)) {
151 pglob->gl_pathc = 0;
152 pglob->gl_pathv = NULL;
155 oldcount = pglob->gl_pathc;
157 if (glob_pattern_p(dirname, !(flags & GLOB_NOESCAPE))) {
158 /* The directory name contains metacharacters, so we
159 have to glob for the directory, and then glob for
160 the pattern in each directory found. */
161 glob_t dirs;
162 register int i;
163 status = glob(dirname,
164 ((flags &
165 (GLOB_ERR | GLOB_NOCHECK | GLOB_NOESCAPE)) |
166 GLOB_NOSORT), errfunc, &dirs);
167 if (status != 0)
168 goto out;
170 /* We have successfully globbed the preceding directory name.
171 For each name we found, call glob_in_dir on it and FILENAME,
172 appending the results to PGLOB. */
173 for (i = 0; i < dirs.gl_pathc; ++i) {
174 int oldcount;
176 #ifdef SHELL
178 /* Make globbing interruptible in the bash shell. */
179 extern int interrupt_state;
181 if (interrupt_state) {
182 globfree(&dirs);
183 globfree(&files);
184 status = GLOB_ABEND goto out;
187 #endif /* SHELL. */
189 oldcount = pglob->gl_pathc;
190 status = glob_in_dir(filename, dirs.gl_pathv[i],
191 (flags | GLOB_APPEND) &
192 ~GLOB_NOCHECK, errfunc, pglob);
193 if (status == GLOB_NOMATCH) {
194 /* No matches in this directory. Try the next. */
195 continue;
198 if (status != 0) {
199 globfree(pglob);
200 goto out;
203 /* Stick the directory on the front of each name. */
204 prefix_array(dirs.gl_pathv[i],
205 &pglob->gl_pathv[oldcount],
206 pglob->gl_pathc - oldcount,
207 flags & GLOB_MARK);
210 globfree(&dirs);
211 flags |= GLOB_MAGCHAR;
213 if (pglob->gl_pathc == oldcount) {
214 /* No matches. */
215 #ifdef CONFIG_GLOB_NOCHECK
216 if (flags & GLOB_NOCHECK) {
217 size_t len = strlen(pattern) + 1;
218 char *patcopy = (char *)xmalloc(len);
219 memcpy(patcopy, pattern, len);
221 pglob->gl_pathv
222 = (char **)xrealloc(pglob->gl_pathv,
223 (pglob->gl_pathc +
224 ((flags & GLOB_DOOFFS) ?
225 pglob->gl_offs : 0) +
226 1 + 1) *
227 sizeof(char *));
229 if (flags & GLOB_DOOFFS)
230 while (pglob->gl_pathc < pglob->gl_offs)
231 pglob->gl_pathv[pglob->
232 gl_pathc++] =
233 NULL;
235 pglob->gl_pathv[pglob->gl_pathc++] = patcopy;
236 pglob->gl_pathv[pglob->gl_pathc] = NULL;
237 pglob->gl_flags = flags;
238 } else
239 #endif
241 status = GLOB_NOMATCH;
242 goto out;
245 } else {
246 status = glob_in_dir(filename, dirname, flags, errfunc, pglob);
247 if (status != 0)
248 goto out;
250 if (dirlen > 0) {
251 /* Stick the directory on the front of each name. */
252 prefix_array(dirname,
253 &pglob->gl_pathv[oldcount],
254 pglob->gl_pathc - oldcount,
255 flags & GLOB_MARK);
259 if (flags & GLOB_MARK) {
260 /* Append slashes to directory names. glob_in_dir has already
261 allocated the extra character for us. */
262 int i;
263 struct stat st;
264 for (i = oldcount; i < pglob->gl_pathc; ++i)
265 if (stat(pglob->gl_pathv[i], &st) == 0 &&
266 S_ISDIR(st.st_mode))
267 strcat(pglob->gl_pathv[i], "/");
269 #ifdef CONFIG_GLOB_SORT
270 if (!(flags & GLOB_NOSORT))
271 /* Sort the vector. */
272 qsort((__ptr_t) & pglob->gl_pathv[oldcount],
273 pglob->gl_pathc - oldcount,
274 sizeof(char *), (__compar_fn_t) collated_compare);
275 #endif
276 status = 0;
277 out:
278 free(dirname);
280 return status;
283 /* Prepend DIRNAME to each of N members of ARRAY, replacing ARRAY's
284 elements in place. Return nonzero if out of memory, zero if successful.
285 A slash is inserted between DIRNAME and each elt of ARRAY,
286 unless DIRNAME is just "/". Each old element of ARRAY is freed.
287 If ADD_SLASH is non-zero, allocate one character more than
288 necessary, so that a slash can be appended later. */
289 static int prefix_array(dirname, array, n, add_slash)
290 const char *dirname;
291 char **array;
292 size_t n;
293 int add_slash;
295 register size_t i;
296 size_t dirlen = strlen(dirname);
298 if (dirlen == 1 && dirname[0] == '/')
299 /* DIRNAME is just "/", so normal prepending would get us "//foo".
300 We want "/foo" instead, so don't prepend any chars from DIRNAME. */
301 dirlen = 0;
303 for (i = 0; i < n; ++i) {
304 size_t eltlen = strlen(array[i]) + 1;
305 char *new =
306 (char *)xmalloc(dirlen + 1 + eltlen + (add_slash ? 1 : 0));
308 memcpy(new, dirname, dirlen);
309 new[dirlen] = '/';
310 memcpy(&new[dirlen + 1], array[i], eltlen);
311 free((__ptr_t) array[i]);
312 array[i] = new;
315 return 0;
318 /* Like `glob', but PATTERN is a final pathname component,
319 and matches are searched for in DIRECTORY.
320 The GLOB_NOSORT bit in FLAGS is ignored. No sorting is ever done.
321 The GLOB_APPEND flag is assumed to be set (always appends). */
322 static int glob_in_dir(pattern, directory, flags, errfunc, pglob)
323 const char *pattern;
324 const char *directory;
325 int flags;
326 int (*errfunc) __P((const char *, int));
327 glob_t *pglob;
329 __ptr_t stream;
331 struct globlink {
332 struct globlink *next;
333 char *name;
335 struct globlink *names = NULL;
336 size_t nfound = 0;
337 int meta;
339 stream = opendir(directory);
341 if (stream == NULL) {
342 if ((errfunc != NULL && (*errfunc) (directory, errno)) ||
343 (flags & GLOB_ERR))
344 return GLOB_ABORTED;
347 meta = glob_pattern_p(pattern, !(flags & GLOB_NOESCAPE));
349 if (meta)
350 flags |= GLOB_MAGCHAR;
352 while (1) {
353 const char *name;
354 size_t len;
356 struct dirent *d = readdir((DIR *) stream);
357 if (d == NULL)
358 break;
359 // if (! (d->d_ino != 0))
360 // continue;
361 name = d->d_name;
363 if ((!meta && strcmp(pattern, name) == 0)
364 || fnmatch(pattern, name,
365 (!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0) |
366 ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0)) == 0) {
367 struct globlink *new =
368 (struct globlink *)xmalloc(sizeof(struct globlink));
369 len = strlen(name);
370 new->name = xmalloc(len + ((flags & GLOB_MARK) ? 1 : 0) + 1);
371 memcpy((__ptr_t) new->name, name, len);
372 new->name[len] = '\0';
373 new->next = names;
374 names = new;
375 ++nfound;
376 if (!meta)
377 break;
380 #ifdef CONFIG_GLOB_NOCHECK
381 if (nfound == 0 && (flags & GLOB_NOCHECK)) {
382 size_t len = strlen(pattern);
383 nfound = 1;
384 names = (struct globlink *)xmalloc(sizeof(struct globlink));
385 names->next = NULL;
386 names->name =
387 (char *)xmalloc(len + (flags & GLOB_MARK ? 1 : 0) + 1);
388 memcpy(names->name, pattern, len);
389 names->name[len] = '\0';
391 #endif
392 pglob->gl_pathv
393 = (char **)xrealloc(pglob->gl_pathv,
394 (pglob->gl_pathc +
395 ((flags & GLOB_DOOFFS) ? pglob->gl_offs : 0) +
396 nfound + 1) * sizeof(char *));
398 if (flags & GLOB_DOOFFS)
399 while (pglob->gl_pathc < pglob->gl_offs)
400 pglob->gl_pathv[pglob->gl_pathc++] = NULL;
402 while (names) {
403 struct globlink *tmp;
405 pglob->gl_pathv[pglob->gl_pathc++] = names->name;
406 tmp = names;
407 names = names->next;
408 free(tmp);
410 pglob->gl_pathv[pglob->gl_pathc] = NULL;
412 pglob->gl_flags = flags;
415 int save = errno;
416 (void)closedir((DIR *) stream);
417 errno = save;
419 return nfound == 0 ? GLOB_NOMATCH : 0;
421 #endif /* CONFIG_GLOB */
423 #ifdef CONFIG_FAKE_GLOB
424 /* Fake version of glob. We simply put the input string into
425 * the gl_pathv array. Currently we don't need it as hush.c won't
426 * call us if no glob support is available.
428 int glob(pattern, flags, errfunc, pglob)
429 const char *pattern;
430 int flags;
431 int (*errfunc) __P((const char *, int));
432 glob_t *pglob;
434 int elems, i;
436 if (!(flags & GLOB_APPEND)) {
437 pglob->gl_pathc = 0;
438 pglob->gl_pathv = NULL;
441 elems = pglob->gl_pathc + 2;
442 if (flags & GLOB_DOOFFS)
443 elems += pglob->gl_offs;
445 pglob->gl_pathv = xrealloc(pglob->gl_pathv, elems * sizeof(char *));
447 if (flags & GLOB_DOOFFS)
448 for (i = 0; i < pglob->gl_offs; i++)
449 pglob->gl_pathv[i] = NULL;
451 pglob->gl_pathv[pglob->gl_pathc] = strdup(pattern);
452 pglob->gl_pathc++;
453 pglob->gl_pathv[pglob->gl_pathc] = NULL;
455 return 0;
457 #endif /* CONFIG_FAKE_GLOB */
459 /* Free storage allocated in PGLOB by a previous `glob' call. */
460 void globfree(pglob)
461 register glob_t *pglob;
463 if (pglob->gl_pathv != NULL) {
464 register int i =
465 pglob->gl_flags & GLOB_DOOFFS ? pglob->gl_offs : 0;
466 for (; i < pglob->gl_pathc; ++i)
467 if (pglob->gl_pathv[i] != NULL)
468 free((__ptr_t) pglob->gl_pathv[i]);
469 free((__ptr_t) pglob->gl_pathv);