Coverage Report

Created: 2026-10-17 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/barebox/lib/glob.c
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
}