diff options
author | Ron Yorston <rmy@pobox.com> | 2022-08-01 12:45:10 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2022-08-01 12:51:37 +0100 |
commit | 67a630e5af1ace1dd528ea9652ee69102b3136c3 (patch) | |
tree | c918ed81ad1791c415a811d63d2f8771a7dd6ef7 /win32 | |
parent | b0f279a48f5f7e57b6f6e941e4b59e9a1bc54548 (diff) | |
download | busybox-w32-67a630e5af1ace1dd528ea9652ee69102b3136c3.tar.gz busybox-w32-67a630e5af1ace1dd528ea9652ee69102b3136c3.tar.bz2 busybox-w32-67a630e5af1ace1dd528ea9652ee69102b3136c3.zip |
make: new applet
This is an experimental implementation of make for busybox-w32,
based on my public domain POSIX make:
https://frippery.org/make/
(GitHub issue #44)
Diffstat (limited to 'win32')
-rw-r--r-- | win32/Kbuild | 1 | ||||
-rw-r--r-- | win32/glob.c | 343 | ||||
-rw-r--r-- | win32/glob.h | 89 |
3 files changed, 433 insertions, 0 deletions
diff --git a/win32/Kbuild b/win32/Kbuild index 9f486d5e9..10614461a 100644 --- a/win32/Kbuild +++ b/win32/Kbuild | |||
@@ -8,6 +8,7 @@ lib-$(CONFIG_PLATFORM_MINGW32) += dirent.o | |||
8 | lib-$(CONFIG_PLATFORM_MINGW32) += env.o | 8 | lib-$(CONFIG_PLATFORM_MINGW32) += env.o |
9 | lib-$(CONFIG_PLATFORM_MINGW32) += fnmatch.o | 9 | lib-$(CONFIG_PLATFORM_MINGW32) += fnmatch.o |
10 | lib-$(CONFIG_PLATFORM_MINGW32) += fsync.o | 10 | lib-$(CONFIG_PLATFORM_MINGW32) += fsync.o |
11 | lib-$(CONFIG_MAKE) += glob.o | ||
11 | lib-$(CONFIG_PLATFORM_MINGW32) += inet_pton.o | 12 | lib-$(CONFIG_PLATFORM_MINGW32) += inet_pton.o |
12 | lib-$(CONFIG_PLATFORM_MINGW32) += ioctl.o | 13 | lib-$(CONFIG_PLATFORM_MINGW32) += ioctl.o |
13 | lib-$(CONFIG_FEATURE_PRNG_ISAAC) += isaac.o | 14 | lib-$(CONFIG_FEATURE_PRNG_ISAAC) += isaac.o |
diff --git a/win32/glob.c b/win32/glob.c new file mode 100644 index 000000000..1cc6483e7 --- /dev/null +++ b/win32/glob.c | |||
@@ -0,0 +1,343 @@ | |||
1 | /* | ||
2 | glob from musl (https://www.musl-libc.org/). | ||
3 | |||
4 | MIT licensed: | ||
5 | |||
6 | ---------------------------------------------------------------------- | ||
7 | Copyright © 2005-2020 Rich Felker, et al. | ||
8 | |||
9 | Permission is hereby granted, free of charge, to any person obtaining | ||
10 | a copy of this software and associated documentation files (the | ||
11 | "Software"), to deal in the Software without restriction, including | ||
12 | without limitation the rights to use, copy, modify, merge, publish, | ||
13 | distribute, sublicense, and/or sell copies of the Software, and to | ||
14 | permit persons to whom the Software is furnished to do so, subject to | ||
15 | the following conditions: | ||
16 | |||
17 | The above copyright notice and this permission notice shall be | ||
18 | included in all copies or substantial portions of the Software. | ||
19 | |||
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
21 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
22 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
23 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
25 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||
26 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
27 | ---------------------------------------------------------------------- | ||
28 | */ | ||
29 | #include "libbb.h" | ||
30 | #include <glob.h> | ||
31 | #include <fnmatch.h> | ||
32 | |||
33 | struct match | ||
34 | { | ||
35 | struct match *next; | ||
36 | char name[]; | ||
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 + 2); | ||
42 | if (!new) return -1; | ||
43 | (*tail)->next = new; | ||
44 | new->next = NULL; | ||
45 | memcpy(new->name, name, len+1); | ||
46 | if (mark && len && name[len-1]!='/') { | ||
47 | new->name[len] = '/'; | ||
48 | new->name[len+1] = 0; | ||
49 | } | ||
50 | *tail = new; | ||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | static int do_glob(char *buf, size_t pos, int type, char *pat, int flags, int (*errfunc)(const char *path, int err), struct match **tail) | ||
55 | { | ||
56 | ptrdiff_t i, j; | ||
57 | int in_bracket, overflow; | ||
58 | char *p2, saved_sep; | ||
59 | int readerr, old_errno; | ||
60 | DIR *dir; | ||
61 | struct dirent *de; | ||
62 | |||
63 | /* If GLOB_MARK is unused, we don't care about type. */ | ||
64 | if (!type && !(flags & GLOB_MARK)) type = DT_REG; | ||
65 | |||
66 | /* Special-case the remaining pattern being all slashes, in | ||
67 | * which case we can use caller-passed type if it's a dir. */ | ||
68 | if (*pat && type!=DT_DIR) type = 0; | ||
69 | while (pos+1 < PATH_MAX && *pat=='/') buf[pos++] = *pat++; | ||
70 | |||
71 | /* Consume maximal [escaped-]literal prefix of pattern, copying | ||
72 | * and un-escaping it to the running buffer as we go. */ | ||
73 | i=0; j=0; | ||
74 | in_bracket = 0; overflow = 0; | ||
75 | for (; pat[i]!='*' && pat[i]!='?' && (!in_bracket || pat[i]!=']'); i++) { | ||
76 | if (!pat[i]) { | ||
77 | if (overflow) return 0; | ||
78 | pat += i; | ||
79 | pos += j; | ||
80 | i = j = 0; | ||
81 | break; | ||
82 | } else if (pat[i] == '[') { | ||
83 | in_bracket = 1; | ||
84 | } else if (pat[i] == '\\' && !(flags & GLOB_NOESCAPE)) { | ||
85 | /* Backslashes inside a bracket are (at least by | ||
86 | * our interpretation) non-special, so if next | ||
87 | * char is ']' we have a complete expression. */ | ||
88 | if (in_bracket && pat[i+1]==']') break; | ||
89 | /* Unpaired final backslash never matches. */ | ||
90 | if (!pat[i+1]) return 0; | ||
91 | i++; | ||
92 | } | ||
93 | if (pat[i] == '/') { | ||
94 | if (overflow) return 0; | ||
95 | in_bracket = 0; | ||
96 | pat += i+1; | ||
97 | i = -1; | ||
98 | pos += j+1; | ||
99 | j = -1; | ||
100 | } | ||
101 | /* Only store a character if it fits in the buffer, but if | ||
102 | * a potential bracket expression is open, the overflow | ||
103 | * must be remembered and handled later only if the bracket | ||
104 | * is unterminated (and thereby a literal), so as not to | ||
105 | * disallow long bracket expressions with short matches. */ | ||
106 | if (pos+(j+1) < PATH_MAX) { | ||
107 | buf[pos+j++] = pat[i]; | ||
108 | } else if (in_bracket) { | ||
109 | overflow = 1; | ||
110 | } else { | ||
111 | return 0; | ||
112 | } | ||
113 | /* If we consume any new components, the caller-passed type | ||
114 | * or dummy type from above is no longer valid. */ | ||
115 | type = 0; | ||
116 | } | ||
117 | buf[pos] = 0; | ||
118 | if (!*pat) { | ||
119 | /* If we consumed any components above, or if GLOB_MARK is | ||
120 | * requested and we don't yet know if the match is a dir, | ||
121 | * we must confirm the file exists and/or determine its type. | ||
122 | * | ||
123 | * If marking dirs, symlink type is inconclusive; we need the | ||
124 | * type for the symlink target, and therefore must try stat | ||
125 | * first unless type is known not to be a symlink. Otherwise, | ||
126 | * or if that fails, use lstat for determining existence to | ||
127 | * avoid false negatives in the case of broken symlinks. */ | ||
128 | struct stat st; | ||
129 | if ((flags & GLOB_MARK) && (!type||type==DT_LNK) && !stat(buf, &st)) { | ||
130 | if (S_ISDIR(st.st_mode)) type = DT_DIR; | ||
131 | else type = DT_REG; | ||
132 | } | ||
133 | if (!type && lstat(buf, &st)) { | ||
134 | if (errno!=ENOENT && (errfunc(buf, errno) || (flags & GLOB_ERR))) | ||
135 | return GLOB_ABORTED; | ||
136 | return 0; | ||
137 | } | ||
138 | if (append(tail, buf, pos, (flags & GLOB_MARK) && type==DT_DIR)) | ||
139 | return GLOB_NOSPACE; | ||
140 | return 0; | ||
141 | } | ||
142 | p2 = strchr(pat, '/'); | ||
143 | saved_sep = '/'; | ||
144 | /* Check if the '/' was escaped and, if so, remove the escape char | ||
145 | * so that it will not be unpaired when passed to fnmatch. */ | ||
146 | if (p2 && !(flags & GLOB_NOESCAPE)) { | ||
147 | char *p; | ||
148 | for (p=p2; p>pat && p[-1]=='\\'; p--); | ||
149 | if ((p2-p)%2) { | ||
150 | p2--; | ||
151 | saved_sep = '\\'; | ||
152 | } | ||
153 | } | ||
154 | dir = opendir(pos ? buf : "."); | ||
155 | if (!dir) { | ||
156 | if (errfunc(buf, errno) || (flags & GLOB_ERR)) | ||
157 | return GLOB_ABORTED; | ||
158 | return 0; | ||
159 | } | ||
160 | old_errno = errno; | ||
161 | while (errno=0, de=readdir(dir)) { | ||
162 | size_t l; | ||
163 | int fnm_flags, r; | ||
164 | |||
165 | /* Quickly skip non-directories when there's pattern left. */ | ||
166 | if (p2 && de->d_type && de->d_type!=DT_DIR && de->d_type!=DT_LNK) | ||
167 | continue; | ||
168 | |||
169 | l = strlen(de->d_name); | ||
170 | if (l >= PATH_MAX-pos) continue; | ||
171 | |||
172 | if (p2) *p2 = 0; | ||
173 | |||
174 | fnm_flags= ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0) | ||
175 | | ((!(flags & GLOB_PERIOD)) ? FNM_PERIOD : 0); | ||
176 | |||
177 | if (fnmatch(pat, de->d_name, fnm_flags)) | ||
178 | continue; | ||
179 | |||
180 | /* With GLOB_PERIOD, don't allow matching . or .. unless | ||
181 | * fnmatch would match them with FNM_PERIOD rules in effect. */ | ||
182 | if (p2 && (flags & GLOB_PERIOD) && de->d_name[0]=='.' | ||
183 | && (!de->d_name[1] || (de->d_name[1]=='.' && !de->d_name[2])) | ||
184 | && fnmatch(pat, de->d_name, fnm_flags | FNM_PERIOD)) | ||
185 | continue; | ||
186 | |||
187 | memcpy(buf+pos, de->d_name, l+1); | ||
188 | if (p2) *p2 = saved_sep; | ||
189 | r = do_glob(buf, pos+l, de->d_type, p2 ? p2 : (char *)"", flags, errfunc, tail); | ||
190 | if (r) { | ||
191 | closedir(dir); | ||
192 | return r; | ||
193 | } | ||
194 | } | ||
195 | readerr = errno; | ||
196 | if (p2) *p2 = saved_sep; | ||
197 | closedir(dir); | ||
198 | if (readerr && (errfunc(buf, errno) || (flags & GLOB_ERR))) | ||
199 | return GLOB_ABORTED; | ||
200 | errno = old_errno; | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static int ignore_err(const char *path UNUSED_PARAM, int err UNUSED_PARAM) | ||
205 | { | ||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | static void freelist(struct match *head) | ||
210 | { | ||
211 | struct match *match, *next; | ||
212 | for (match=head->next; match; match=next) { | ||
213 | next = match->next; | ||
214 | free(match); | ||
215 | } | ||
216 | } | ||
217 | |||
218 | #if !ENABLE_PLATFORM_MINGW32 | ||
219 | static int sort(const void *a, const void *b) | ||
220 | { | ||
221 | return strcmp(*(const char **)a, *(const char **)b); | ||
222 | } | ||
223 | |||
224 | static int expand_tilde(char **pat, char *buf, size_t *pos) | ||
225 | { | ||
226 | char *p = *pat + 1; | ||
227 | size_t i = 0; | ||
228 | |||
229 | char delim, *name_end = __strchrnul(p, '/'); | ||
230 | if ((delim = *name_end)) *name_end++ = 0; | ||
231 | *pat = name_end; | ||
232 | |||
233 | char *home = *p ? NULL : getenv("HOME"); | ||
234 | if (!home) { | ||
235 | struct passwd pw, *res; | ||
236 | switch (*p ? getpwnam_r(p, &pw, buf, PATH_MAX, &res) | ||
237 | : getpwuid_r(getuid(), &pw, buf, PATH_MAX, &res)) { | ||
238 | case ENOMEM: | ||
239 | return GLOB_NOSPACE; | ||
240 | case 0: | ||
241 | if (!res) | ||
242 | default: | ||
243 | return GLOB_NOMATCH; | ||
244 | } | ||
245 | home = pw.pw_dir; | ||
246 | } | ||
247 | while (i < PATH_MAX - 2 && *home) | ||
248 | buf[i++] = *home++; | ||
249 | if (*home) | ||
250 | return GLOB_NOMATCH; | ||
251 | if ((buf[i] = delim)) | ||
252 | buf[++i] = 0; | ||
253 | *pos = i; | ||
254 | return 0; | ||
255 | } | ||
256 | #endif | ||
257 | |||
258 | int glob(const char *restrict pat, int flags, int (*errfunc)(const char *path, int err), glob_t *restrict g) | ||
259 | { | ||
260 | struct match head = { .next = NULL }, *tail = &head; | ||
261 | size_t cnt, i; | ||
262 | size_t offs = (flags & GLOB_DOOFFS) ? g->gl_offs : 0; | ||
263 | int error = 0; | ||
264 | char buf[PATH_MAX]; | ||
265 | |||
266 | if (!errfunc) errfunc = ignore_err; | ||
267 | |||
268 | if (!(flags & GLOB_APPEND)) { | ||
269 | g->gl_offs = offs; | ||
270 | g->gl_pathc = 0; | ||
271 | g->gl_pathv = NULL; | ||
272 | } | ||
273 | |||
274 | if (*pat) { | ||
275 | char *p = strdup(pat); | ||
276 | size_t pos = 0; | ||
277 | char *s = p; | ||
278 | if (!p) return GLOB_NOSPACE; | ||
279 | buf[0] = 0; | ||
280 | #if !ENABLE_PLATFORM_MINGW32 | ||
281 | if ((flags & (GLOB_TILDE | GLOB_TILDE_CHECK)) && *p == '~') | ||
282 | error = expand_tilde(&s, buf, &pos); | ||
283 | if (!error) | ||
284 | #endif | ||
285 | error = do_glob(buf, pos, 0, s, flags, errfunc, &tail); | ||
286 | free(p); | ||
287 | } | ||
288 | |||
289 | if (error == GLOB_NOSPACE) { | ||
290 | freelist(&head); | ||
291 | return error; | ||
292 | } | ||
293 | |||
294 | for (cnt=0, tail=head.next; tail; tail=tail->next, cnt++); | ||
295 | if (!cnt) { | ||
296 | if (flags & GLOB_NOCHECK) { | ||
297 | tail = &head; | ||
298 | if (append(&tail, pat, strlen(pat), 0)) | ||
299 | return GLOB_NOSPACE; | ||
300 | cnt++; | ||
301 | } else | ||
302 | return GLOB_NOMATCH; | ||
303 | } | ||
304 | |||
305 | if (flags & GLOB_APPEND) { | ||
306 | char **pathv = realloc(g->gl_pathv, (offs + g->gl_pathc + cnt + 1) * sizeof(char *)); | ||
307 | if (!pathv) { | ||
308 | freelist(&head); | ||
309 | return GLOB_NOSPACE; | ||
310 | } | ||
311 | g->gl_pathv = pathv; | ||
312 | offs += g->gl_pathc; | ||
313 | } else { | ||
314 | g->gl_pathv = malloc((offs + cnt + 1) * sizeof(char *)); | ||
315 | if (!g->gl_pathv) { | ||
316 | freelist(&head); | ||
317 | return GLOB_NOSPACE; | ||
318 | } | ||
319 | for (i=0; i<offs; i++) | ||
320 | g->gl_pathv[i] = NULL; | ||
321 | } | ||
322 | for (i=0, tail=head.next; i<cnt; tail=tail->next, i++) | ||
323 | g->gl_pathv[offs + i] = tail->name; | ||
324 | g->gl_pathv[offs + i] = NULL; | ||
325 | g->gl_pathc += cnt; | ||
326 | |||
327 | #if !ENABLE_PLATFORM_MINGW32 | ||
328 | if (!(flags & GLOB_NOSORT)) | ||
329 | qsort(g->gl_pathv+offs, cnt, sizeof(char *), sort); | ||
330 | #endif | ||
331 | |||
332 | return error; | ||
333 | } | ||
334 | |||
335 | void globfree(glob_t *g) | ||
336 | { | ||
337 | size_t i; | ||
338 | for (i=0; i<g->gl_pathc; i++) | ||
339 | free(g->gl_pathv[g->gl_offs + i] - offsetof(struct match, name)); | ||
340 | free(g->gl_pathv); | ||
341 | g->gl_pathc = 0; | ||
342 | g->gl_pathv = NULL; | ||
343 | } | ||
diff --git a/win32/glob.h b/win32/glob.h new file mode 100644 index 000000000..a8141b8bf --- /dev/null +++ b/win32/glob.h | |||
@@ -0,0 +1,89 @@ | |||
1 | /* | ||
2 | glob from musl (https://www.musl-libc.org/). | ||
3 | |||
4 | MIT licensed: | ||
5 | |||
6 | ---------------------------------------------------------------------- | ||
7 | Copyright © 2005-2020 Rich Felker, et al. | ||
8 | |||
9 | Permission is hereby granted, free of charge, to any person obtaining | ||
10 | a copy of this software and associated documentation files (the | ||
11 | "Software"), to deal in the Software without restriction, including | ||
12 | without limitation the rights to use, copy, modify, merge, publish, | ||
13 | distribute, sublicense, and/or sell copies of the Software, and to | ||
14 | permit persons to whom the Software is furnished to do so, subject to | ||
15 | the following conditions: | ||
16 | |||
17 | The above copyright notice and this permission notice shall be | ||
18 | included in all copies or substantial portions of the Software. | ||
19 | |||
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
21 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
22 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
23 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
25 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||
26 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
27 | ---------------------------------------------------------------------- | ||
28 | */ | ||
29 | #ifndef _GLOB_H | ||
30 | #define _GLOB_H | ||
31 | |||
32 | #ifdef __cplusplus | ||
33 | extern "C" { | ||
34 | #endif | ||
35 | |||
36 | typedef struct { | ||
37 | size_t gl_pathc; | ||
38 | char **gl_pathv; | ||
39 | size_t gl_offs; | ||
40 | int __dummy1; | ||
41 | void *__dummy2[5]; | ||
42 | } glob_t; | ||
43 | |||
44 | int glob(const char *__restrict, int, int (*)(const char *, int), glob_t *__restrict); | ||
45 | void globfree(glob_t *); | ||
46 | |||
47 | #if ENABLE_PLATFORM_MINGW32 | ||
48 | // Set some flags to zero so the compiler can exclude unused code. | ||
49 | #define GLOB_ERR 0 | ||
50 | #define GLOB_MARK 0 | ||
51 | #define GLOB_NOSORT 0x04 | ||
52 | #define GLOB_DOOFFS 0 | ||
53 | #define GLOB_NOCHECK 0x10 | ||
54 | #define GLOB_APPEND 0 | ||
55 | #define GLOB_NOESCAPE 0x40 | ||
56 | #define GLOB_PERIOD 0 | ||
57 | |||
58 | #define GLOB_TILDE 0 | ||
59 | #define GLOB_TILDE_CHECK 0 | ||
60 | #else | ||
61 | #define GLOB_ERR 0x01 | ||
62 | #define GLOB_MARK 0x02 | ||
63 | #define GLOB_NOSORT 0x04 | ||
64 | #define GLOB_DOOFFS 0x08 | ||
65 | #define GLOB_NOCHECK 0x10 | ||
66 | #define GLOB_APPEND 0x20 | ||
67 | #define GLOB_NOESCAPE 0x40 | ||
68 | #define GLOB_PERIOD 0x80 | ||
69 | |||
70 | #define GLOB_TILDE 0x1000 | ||
71 | #define GLOB_TILDE_CHECK 0x4000 | ||
72 | #endif | ||
73 | |||
74 | #define GLOB_NOSPACE 1 | ||
75 | #define GLOB_ABORTED 2 | ||
76 | #define GLOB_NOMATCH 3 | ||
77 | #define GLOB_NOSYS 4 | ||
78 | |||
79 | #if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE) | ||
80 | #define glob64 glob | ||
81 | #define globfree64 globfree | ||
82 | #define glob64_t glob_t | ||
83 | #endif | ||
84 | |||
85 | #ifdef __cplusplus | ||
86 | } | ||
87 | #endif | ||
88 | |||
89 | #endif | ||