aboutsummaryrefslogtreecommitdiff
path: root/win32
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2022-08-01 12:45:10 +0100
committerRon Yorston <rmy@pobox.com>2022-08-01 12:51:37 +0100
commit67a630e5af1ace1dd528ea9652ee69102b3136c3 (patch)
treec918ed81ad1791c415a811d63d2f8771a7dd6ef7 /win32
parentb0f279a48f5f7e57b6f6e941e4b59e9a1bc54548 (diff)
downloadbusybox-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/Kbuild1
-rw-r--r--win32/glob.c343
-rw-r--r--win32/glob.h89
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
8lib-$(CONFIG_PLATFORM_MINGW32) += env.o 8lib-$(CONFIG_PLATFORM_MINGW32) += env.o
9lib-$(CONFIG_PLATFORM_MINGW32) += fnmatch.o 9lib-$(CONFIG_PLATFORM_MINGW32) += fnmatch.o
10lib-$(CONFIG_PLATFORM_MINGW32) += fsync.o 10lib-$(CONFIG_PLATFORM_MINGW32) += fsync.o
11lib-$(CONFIG_MAKE) += glob.o
11lib-$(CONFIG_PLATFORM_MINGW32) += inet_pton.o 12lib-$(CONFIG_PLATFORM_MINGW32) += inet_pton.o
12lib-$(CONFIG_PLATFORM_MINGW32) += ioctl.o 13lib-$(CONFIG_PLATFORM_MINGW32) += ioctl.o
13lib-$(CONFIG_FEATURE_PRNG_ISAAC) += isaac.o 14lib-$(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----------------------------------------------------------------------
7Copyright © 2005-2020 Rich Felker, et al.
8
9Permission is hereby granted, free of charge, to any person obtaining
10a copy of this software and associated documentation files (the
11"Software"), to deal in the Software without restriction, including
12without limitation the rights to use, copy, modify, merge, publish,
13distribute, sublicense, and/or sell copies of the Software, and to
14permit persons to whom the Software is furnished to do so, subject to
15the following conditions:
16
17The above copyright notice and this permission notice shall be
18included in all copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27----------------------------------------------------------------------
28*/
29#include "libbb.h"
30#include <glob.h>
31#include <fnmatch.h>
32
33struct match
34{
35 struct match *next;
36 char name[];
37};
38
39static 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
54static 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
204static int ignore_err(const char *path UNUSED_PARAM, int err UNUSED_PARAM)
205{
206 return 0;
207}
208
209static 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
219static int sort(const void *a, const void *b)
220{
221 return strcmp(*(const char **)a, *(const char **)b);
222}
223
224static 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
258int 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
335void 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----------------------------------------------------------------------
7Copyright © 2005-2020 Rich Felker, et al.
8
9Permission is hereby granted, free of charge, to any person obtaining
10a copy of this software and associated documentation files (the
11"Software"), to deal in the Software without restriction, including
12without limitation the rights to use, copy, modify, merge, publish,
13distribute, sublicense, and/or sell copies of the Software, and to
14permit persons to whom the Software is furnished to do so, subject to
15the following conditions:
16
17The above copyright notice and this permission notice shall be
18included in all copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27----------------------------------------------------------------------
28*/
29#ifndef _GLOB_H
30#define _GLOB_H
31
32#ifdef __cplusplus
33extern "C" {
34#endif
35
36typedef 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
44int glob(const char *__restrict, int, int (*)(const char *, int), glob_t *__restrict);
45void 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