blob: 59b4d8e5fe4a81fc7949f7b887f0a4bf86be17ea [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/*
2 * Copyright (C) 2006 Rich Felker <dalias@aerifal.cx>
3 *
4 * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
5 */
6
7#include <features.h>
8
9#ifdef __UCLIBC_HAS_LFS__
10# define BUILD_GLOB64
11#endif
12
13#include <glob.h>
14#include <fnmatch.h>
15#include <sys/stat.h>
16#include <dirent.h>
17#include <limits.h>
18#include <string.h>
19#include <stdlib.h>
20#include <errno.h>
21#include <stddef.h>
22
23#include <unistd.h>
24#include <stdio.h>
25
26
27struct match
28{
29 struct match *next;
30 char name[1];
31};
32
33#ifdef BUILD_GLOB64
34extern int __glob_is_literal(const char *p, int useesc) attribute_hidden;
35extern int __glob_append(struct match **tail, const char *name, size_t len, int mark) attribute_hidden;
36extern int __glob_ignore_err(const char *path, int err) attribute_hidden;
37extern void __glob_freelist(struct match *head) attribute_hidden;
38extern int __glob_sort(const void *a, const void *b) attribute_hidden;
39extern int __glob_match_in_dir(const char *d, const char *p, int flags, int (*errfunc)(const char *path, int err), struct match **tail) attribute_hidden;
40#endif
41
42#ifdef __UCLIBC_HAS_LFS__
43# define stat stat64
44# define readdir_r readdir64_r
45# define dirent dirent64
46# define struct_stat struct stat64
47#else
48# define struct_stat struct stat
49#endif
50
51/* keep only one copy of these */
52#ifndef __GLOB64
53
54# ifndef BUILD_GLOB64
55static
56# endif
57int __glob_is_literal(const char *p, int useesc)
58{
59 int bracket = 0;
60 for (; *p; p++) {
61 switch (*p) {
62 case '\\':
63 if (!useesc) break;
64 case '?':
65 case '*':
66 return 0;
67 case '[':
68 bracket = 1;
69 break;
70 case ']':
71 if (bracket) return 0;
72 break;
73 }
74 }
75 return 1;
76}
77
78# ifndef BUILD_GLOB64
79static
80# endif
81int __glob_append(struct match **tail, const char *name, size_t len, int mark)
82{
83 struct match *new = malloc(sizeof(struct match) + len + 1);
84 if (!new) return -1;
85 (*tail)->next = new;
86 new->next = NULL;
87 strcpy(new->name, name);
88 if (mark) strcat(new->name, "/");
89 *tail = new;
90 return 0;
91}
92
93# ifndef BUILD_GLOB64
94static
95# endif
96int __glob_match_in_dir(const char *d, const char *p, int flags, int (*errfunc)(const char *path, int err), struct match **tail)
97{
98 DIR *dir;
99 long long de_buf[(sizeof(struct dirent) + NAME_MAX + sizeof(long long))/sizeof(long long)];
100 struct dirent *de;
101 char pat[strlen(p)+1];
102 char *p2;
103 size_t l = strlen(d);
104 int literal;
105 int fnm_flags= ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0) | FNM_PERIOD;
106 int error;
107
108 if ((p2 = strchr(p, '/'))) {
109 strcpy(pat, p);
110 pat[p2-p] = 0;
111 for (; *p2 == '/'; p2++);
112 p = pat;
113 }
114 literal = __glob_is_literal(p, !(flags & GLOB_NOESCAPE));
115 if (*d == '/' && !*(d+1)) l = 0;
116
117 /* rely on opendir failing for nondirectory objects */
118 dir = opendir(*d ? d : ".");
119 error = errno;
120 if (!dir) {
121 /* this is not an error -- we let opendir call stat for us */
122 if (error == ENOTDIR) return 0;
123 if (error == EACCES && !*p) {
124 struct_stat st;
125 if (!stat(d, &st) && S_ISDIR(st.st_mode)) {
126 if (__glob_append(tail, d, l, l))
127 return GLOB_NOSPACE;
128 return 0;
129 }
130 }
131 if (errfunc(d, error) || (flags & GLOB_ERR))
132 return GLOB_ABORTED;
133 return 0;
134 }
135 if (!*p) {
136 error = __glob_append(tail, d, l, l) ? GLOB_NOSPACE : 0;
137 closedir(dir);
138 return error;
139 }
140 while (!(error = readdir_r(dir, (void *)de_buf, &de)) && de) {
141 char namebuf[l+de->d_reclen+2], *name = namebuf;
142 if (!literal && fnmatch(p, de->d_name, fnm_flags))
143 continue;
144 if (literal && strcmp(p, de->d_name))
145 continue;
146 if (p2 && de->d_type && !S_ISDIR(de->d_type<<12) && !S_ISLNK(de->d_type<<12))
147 continue;
148 if (*d) {
149 memcpy(name, d, l);
150 name[l] = '/';
151 strcpy(name+l+1, de->d_name);
152 } else {
153 name = de->d_name;
154 }
155 if (p2) {
156 if ((error = __glob_match_in_dir(name, p2, flags, errfunc, tail))) {
157 closedir(dir);
158 return error;
159 }
160 } else {
161 int mark = 0;
162 if (flags & GLOB_MARK) {
163 if (de->d_type)
164 mark = S_ISDIR(de->d_type<<12);
165 else {
166 struct_stat st;
167 stat(name, &st);
168 mark = S_ISDIR(st.st_mode);
169 }
170 }
171 if (__glob_append(tail, name, l+de->d_reclen+1, mark)) {
172 closedir(dir);
173 return GLOB_NOSPACE;
174 }
175 }
176 }
177 closedir(dir);
178 if (error && (errfunc(d, error) || (flags & GLOB_ERR)))
179 return GLOB_ABORTED;
180 return 0;
181}
182
183# ifndef BUILD_GLOB64
184static
185# endif
186int __glob_ignore_err(const char * path attribute_unused,
187 int err attribute_unused)
188{
189 return 0;
190}
191
192# ifndef BUILD_GLOB64
193static
194# endif
195void __glob_freelist(struct match *head)
196{
197 struct match *match, *next;
198 for (match=head->next; match; match=next) {
199 next = match->next;
200 free(match);
201 }
202}
203
204# ifndef BUILD_GLOB64
205static
206# endif
207int __glob_sort(const void *a, const void *b)
208{
209 return strcmp(*(const char **)a, *(const char **)b);
210}
211#endif /* !__GLOB64 */
212
213int glob(const char *pat, int flags, int (*errfunc)(const char *path, int err), glob_t *g)
214{
215 const char *p=pat, *d;
216 struct match head = { .next = NULL }, *tail = &head;
217 size_t cnt, i;
218 size_t offs = (flags & GLOB_DOOFFS) ? g->gl_offs : 0;
219 int error = 0;
220
221 if (*p == '/') {
222 for (; *p == '/'; p++);
223 d = "/";
224 } else {
225 d = "";
226 }
227
228 if (!errfunc) errfunc = __glob_ignore_err;
229
230 if (!(flags & GLOB_APPEND)) {
231 g->gl_offs = offs;
232 g->gl_pathc = 0;
233 g->gl_pathv = NULL;
234 }
235
236 if (*p) error = __glob_match_in_dir(d, p, flags, errfunc, &tail);
237 if (error == GLOB_NOSPACE) {
238 __glob_freelist(&head);
239 return error;
240 }
241
242 for (cnt=0, tail=head.next; tail; tail=tail->next, cnt++);
243 if (!cnt) {
244 if (flags & GLOB_NOCHECK) {
245 tail = &head;
246 if (__glob_append(&tail, pat, strlen(pat), 0))
247 return GLOB_NOSPACE;
248 cnt++;
249 } else
250 return GLOB_NOMATCH;
251 }
252
253 if (flags & GLOB_APPEND) {
254 char **pathv = realloc(g->gl_pathv, (offs + g->gl_pathc + cnt + 1) * sizeof(char *));
255 if (!pathv) {
256 __glob_freelist(&head);
257 return GLOB_NOSPACE;
258 }
259 g->gl_pathv = pathv;
260 offs += g->gl_pathc;
261 } else {
262 g->gl_pathv = malloc((offs + cnt + 1) * sizeof(char *));
263 if (!g->gl_pathv) {
264 __glob_freelist(&head);
265 return GLOB_NOSPACE;
266 }
267 for (i=0; i<offs; i++)
268 g->gl_pathv[i] = NULL;
269 }
270 for (i=0, tail=head.next; i<cnt; tail=tail->next, i++)
271 g->gl_pathv[offs + i] = tail->name;
272 g->gl_pathv[offs + i] = NULL;
273 g->gl_pathc += cnt;
274
275 if (!(flags & GLOB_NOSORT))
276 qsort(g->gl_pathv+offs, cnt, sizeof(char *), __glob_sort);
277
278 return error;
279}
280#ifdef __GLOB64
281libc_hidden_def(glob64)
282#else
283libc_hidden_def(glob)
284#endif
285
286void globfree(glob_t *g)
287{
288 size_t i;
289 for (i=0; i<g->gl_pathc; i++)
290 free(g->gl_pathv[g->gl_offs + i] - offsetof(struct match, name));
291 free(g->gl_pathv);
292 g->gl_pathc = 0;
293 g->gl_pathv = NULL;
294}
295#ifdef __GLOB64
296libc_hidden_def(globfree64)
297#else
298libc_hidden_def(globfree)
299#endif