diff options
Diffstat (limited to 'src/toybox.c')
-rw-r--r-- | src/toybox.c | 520 |
1 files changed, 520 insertions, 0 deletions
diff --git a/src/toybox.c b/src/toybox.c new file mode 100644 index 0000000..cd1071b --- /dev/null +++ b/src/toybox.c | |||
@@ -0,0 +1,520 @@ | |||
1 | #include "toybox.h" | ||
2 | |||
3 | |||
4 | char libbuf[4096]; | ||
5 | |||
6 | |||
7 | // From lib.c | ||
8 | |||
9 | void tb_verror_msg(char *msg, int err, va_list va) | ||
10 | { | ||
11 | char *s = ": %s"; | ||
12 | |||
13 | // fprintf(stderr, "%s: ", toys.which->name); | ||
14 | if (msg) vfprintf(stderr, msg, va); | ||
15 | else s+=2; | ||
16 | if (err>0) fprintf(stderr, s, strerror(err)); | ||
17 | // if (err<0 && CFG_TOYBOX_HELP) | ||
18 | // fprintf(stderr, " (see \"%s --help\")", toys.which->name); | ||
19 | if (msg || err) putc('\n', stderr); | ||
20 | // if (!toys.exitval) toys.exitval++; | ||
21 | } | ||
22 | |||
23 | // These functions don't collapse together because of the va_stuff. | ||
24 | |||
25 | void tb_error_msg(char *msg, ...) | ||
26 | { | ||
27 | va_list va; | ||
28 | |||
29 | va_start(va, msg); | ||
30 | tb_verror_msg(msg, 0, va); | ||
31 | va_end(va); | ||
32 | } | ||
33 | |||
34 | void tb_perror_msg(char *msg, ...) | ||
35 | { | ||
36 | va_list va; | ||
37 | |||
38 | va_start(va, msg); | ||
39 | tb_verror_msg(msg, errno, va); | ||
40 | va_end(va); | ||
41 | } | ||
42 | |||
43 | // Die with an error message. | ||
44 | void tb_error_exit(char *msg, ...) | ||
45 | { | ||
46 | va_list va; | ||
47 | |||
48 | va_start(va, msg); | ||
49 | tb_verror_msg(msg, 0, va); | ||
50 | va_end(va); | ||
51 | |||
52 | xexit(); | ||
53 | } | ||
54 | |||
55 | // Die with an error message and strerror(errno) | ||
56 | void tb_perror_exit(char *msg, ...) | ||
57 | { | ||
58 | // Die silently if our pipeline exited. | ||
59 | if (errno != EPIPE) { | ||
60 | va_list va; | ||
61 | |||
62 | va_start(va, msg); | ||
63 | tb_verror_msg(msg, errno, va); | ||
64 | va_end(va); | ||
65 | } | ||
66 | |||
67 | xexit(); | ||
68 | } | ||
69 | |||
70 | // If you want to explicitly disable the printf() behavior (because you're | ||
71 | // printing user-supplied data, or because android's static checker produces | ||
72 | // false positives for 'char *s = x ? "blah1" : "blah2"; printf(s);' and it's | ||
73 | // -Werror there for policy reasons). | ||
74 | void tb_error_msg_raw(char *msg) | ||
75 | { | ||
76 | tb_error_msg("%s", msg); | ||
77 | } | ||
78 | |||
79 | void tb_perror_msg_raw(char *msg) | ||
80 | { | ||
81 | tb_perror_msg("%s", msg); | ||
82 | } | ||
83 | |||
84 | void tb_error_exit_raw(char *msg) | ||
85 | { | ||
86 | tb_error_exit("%s", msg); | ||
87 | } | ||
88 | |||
89 | void tb_perror_exit_raw(char *msg) | ||
90 | { | ||
91 | tb_perror_exit("%s", msg); | ||
92 | } | ||
93 | |||
94 | // Sleep for this many thousandths of a second | ||
95 | void msleep(long milliseconds) | ||
96 | { | ||
97 | struct timespec ts; | ||
98 | |||
99 | ts.tv_sec = milliseconds/1000; | ||
100 | ts.tv_nsec = (milliseconds%1000)*1000000; | ||
101 | nanosleep(&ts, &ts); | ||
102 | } | ||
103 | |||
104 | // Slow, but small. | ||
105 | char *get_line(int fd) | ||
106 | { | ||
107 | char c, *buf = NULL; | ||
108 | long len = 0; | ||
109 | |||
110 | for (;;) { | ||
111 | if (1>read(fd, &c, 1)) break; | ||
112 | if (!(len & 63)) buf=xrealloc(buf, len+65); | ||
113 | if ((buf[len++]=c) == '\n') break; | ||
114 | } | ||
115 | if (buf) { | ||
116 | buf[len]=0; | ||
117 | if (buf[--len]=='\n') buf[len]=0; | ||
118 | } | ||
119 | |||
120 | return buf; | ||
121 | } | ||
122 | |||
123 | // The qsort man page says you can use alphasort, the posix committee | ||
124 | // disagreed, and doubled down: http://austingroupbugs.net/view.php?id=142 | ||
125 | // So just do our own. (The const is entirely to humor the stupid compiler.) | ||
126 | int qstrcmp(const void *a, const void *b) | ||
127 | { | ||
128 | return strcmp(*(char **)a, *(char **)b); | ||
129 | } | ||
130 | |||
131 | |||
132 | |||
133 | // From xwrap.c | ||
134 | |||
135 | // We replaced exit(), _exit(), and atexit() with xexit(), _xexit(), and | ||
136 | // sigatexit(). This gives _xexit() the option to siglongjmp(toys.rebound, 1) | ||
137 | // instead of exiting, lets xexit() report stdout flush failures to stderr | ||
138 | // and change the exit code to indicate error, lets our toys.exit function | ||
139 | // change happen for signal exit paths and lets us remove the functions | ||
140 | // after we've called them. | ||
141 | |||
142 | void _xexit(void) | ||
143 | { | ||
144 | // if (toys.rebound) siglongjmp(*toys.rebound, 1); | ||
145 | |||
146 | // _exit(toys.exitval); | ||
147 | _exit(0); | ||
148 | } | ||
149 | |||
150 | void xexit(void) | ||
151 | { | ||
152 | // Call toys.xexit functions in reverse order added. | ||
153 | // while (toys.xexit) { | ||
154 | // struct arg_list *al = tb_llist_pop(&toys.xexit); | ||
155 | |||
156 | // typecast xexit->arg to a function pointer, then call it using invalid | ||
157 | // signal 0 to let signal handlers tell actual signal from regular exit. | ||
158 | // ((void (*)(int))(al->arg))(0); | ||
159 | |||
160 | // free(al); | ||
161 | // } | ||
162 | xflush(1); | ||
163 | _xexit(); | ||
164 | } | ||
165 | |||
166 | // Die unless we can allocate memory. | ||
167 | void *xmalloc(size_t size) | ||
168 | { | ||
169 | void *ret = malloc(size); | ||
170 | if (!ret) tb_error_exit("xmalloc(%ld)", (long)size); | ||
171 | |||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | // Die unless we can allocate prezeroed memory. | ||
176 | void *xzalloc(size_t size) | ||
177 | { | ||
178 | void *ret = xmalloc(size); | ||
179 | memset(ret, 0, size); | ||
180 | return ret; | ||
181 | } | ||
182 | |||
183 | // Die unless we can change the size of an existing allocation, possibly | ||
184 | // moving it. (Notice different arguments from libc function.) | ||
185 | void *xrealloc(void *ptr, size_t size) | ||
186 | { | ||
187 | ptr = realloc(ptr, size); | ||
188 | if (!ptr) tb_error_exit("xrealloc"); | ||
189 | |||
190 | return ptr; | ||
191 | } | ||
192 | |||
193 | // Die unless we can allocate a copy of this many bytes of string. | ||
194 | char *xstrndup(char *s, size_t n) | ||
195 | { | ||
196 | char *ret = strndup(s, n); | ||
197 | |||
198 | if (!ret) tb_error_exit("xstrndup"); | ||
199 | |||
200 | return ret; | ||
201 | } | ||
202 | |||
203 | // Die unless we can allocate a copy of this string. | ||
204 | char *xstrdup(char *s) | ||
205 | { | ||
206 | return xstrndup(s, strlen(s)); | ||
207 | } | ||
208 | |||
209 | void *xmemdup(void *s, long len) | ||
210 | { | ||
211 | void *ret = xmalloc(len); | ||
212 | memcpy(ret, s, len); | ||
213 | |||
214 | return ret; | ||
215 | } | ||
216 | |||
217 | // Die unless we can allocate enough space to sprintf() into. | ||
218 | char *xmprintf(char *format, ...) | ||
219 | { | ||
220 | va_list va, va2; | ||
221 | int len; | ||
222 | char *ret; | ||
223 | |||
224 | va_start(va, format); | ||
225 | va_copy(va2, va); | ||
226 | |||
227 | // How long is it? | ||
228 | len = vsnprintf(0, 0, format, va); | ||
229 | len++; | ||
230 | va_end(va); | ||
231 | |||
232 | // Allocate and do the sprintf() | ||
233 | ret = xmalloc(len); | ||
234 | vsnprintf(ret, len, format, va2); | ||
235 | va_end(va2); | ||
236 | |||
237 | return ret; | ||
238 | } | ||
239 | |||
240 | // if !flush just check for error on stdout without flushing | ||
241 | void xflush(int flush) | ||
242 | { | ||
243 | if ((flush && fflush(0)) || ferror(stdout)) | ||
244 | ;// if (!toys.exitval) tb_perror_msg("write"); | ||
245 | } | ||
246 | |||
247 | // Die unless we can open/create a file, returning file descriptor. | ||
248 | // The meaning of O_CLOEXEC is reversed (it defaults on, pass it to disable) | ||
249 | // and WARN_ONLY tells us not to exit. | ||
250 | int xcreate_stdio(char *path, int flags, int mode) | ||
251 | { | ||
252 | int fd = open(path, (flags^O_CLOEXEC)&~WARN_ONLY, mode); | ||
253 | |||
254 | if (fd == -1) ((mode&WARN_ONLY) ? tb_perror_msg_raw : tb_perror_exit_raw)(path); | ||
255 | return fd; | ||
256 | } | ||
257 | |||
258 | // Die unless we can open a file, returning file descriptor. | ||
259 | int xopen_stdio(char *path, int flags) | ||
260 | { | ||
261 | return xcreate_stdio(path, flags, 0); | ||
262 | } | ||
263 | |||
264 | void xclose(int fd) | ||
265 | { | ||
266 | if (close(fd)) tb_perror_exit("xclose"); | ||
267 | } | ||
268 | |||
269 | int xdup(int fd) | ||
270 | { | ||
271 | if (fd != -1) { | ||
272 | fd = dup(fd); | ||
273 | if (fd == -1) tb_perror_exit("xdup"); | ||
274 | } | ||
275 | return fd; | ||
276 | } | ||
277 | |||
278 | // Move file descriptor above stdin/stdout/stderr, using /dev/null to consume | ||
279 | // old one. (We should never be called with stdin/stdout/stderr closed, but...) | ||
280 | int notstdio(int fd) | ||
281 | { | ||
282 | if (fd<0) return fd; | ||
283 | |||
284 | while (fd<3) { | ||
285 | int fd2 = xdup(fd); | ||
286 | |||
287 | close(fd); | ||
288 | xopen_stdio("/dev/null", O_RDWR); | ||
289 | fd = fd2; | ||
290 | } | ||
291 | |||
292 | return fd; | ||
293 | } | ||
294 | |||
295 | // Open a file descriptor NOT in stdin/stdout/stderr | ||
296 | int xopen(char *path, int flags) | ||
297 | { | ||
298 | return notstdio(xopen_stdio(path, flags)); | ||
299 | } | ||
300 | |||
301 | // Open read only, treating "-" as a synonym for stdin, defaulting to warn only | ||
302 | int openro(char *path, int flags) | ||
303 | { | ||
304 | if (!strcmp(path, "-")) return 0; | ||
305 | |||
306 | return xopen(path, flags^WARN_ONLY); | ||
307 | } | ||
308 | |||
309 | // Open read only, treating "-" as a synonym for stdin. | ||
310 | int xopenro(char *path) | ||
311 | { | ||
312 | return openro(path, O_RDONLY|WARN_ONLY); | ||
313 | } | ||
314 | |||
315 | // Compile a regular expression into a regex_t | ||
316 | void xregcomp(regex_t *preg, char *regex, int cflags) | ||
317 | { | ||
318 | int rc; | ||
319 | |||
320 | // BSD regex implementations don't support the empty regex (which isn't | ||
321 | // allowed in the POSIX grammar), but glibc does. Fake it for BSD. | ||
322 | if (!*regex) { | ||
323 | regex = "()"; | ||
324 | cflags |= REG_EXTENDED; | ||
325 | } | ||
326 | |||
327 | if ((rc = regcomp(preg, regex, cflags))) { | ||
328 | regerror(rc, preg, libbuf, sizeof(libbuf)); | ||
329 | tb_error_exit("bad regex: %s", libbuf); | ||
330 | } | ||
331 | } | ||
332 | |||
333 | |||
334 | |||
335 | // From dirtree.c | ||
336 | |||
337 | int tb_isdotdot(char *name) | ||
338 | { | ||
339 | if (name[0]=='.' && (!name[1] || (name[1]=='.' && !name[2]))) return 1; | ||
340 | |||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | // Create a tb_dirtree node from a path, with stat and symlink info. | ||
345 | // (This doesn't open directory filehandles yet so as not to exhaust the | ||
346 | // filehandle space on large trees, tb_dirtree_handle_callback() does that.) | ||
347 | |||
348 | struct tb_dirtree *tb_dirtree_add_node(struct tb_dirtree *parent, char *name, int flags) | ||
349 | { | ||
350 | struct tb_dirtree *dt = 0; | ||
351 | struct stat st; | ||
352 | int len = 0, linklen = 0, statless = 0; | ||
353 | |||
354 | if (name) { | ||
355 | // open code fd = because haven't got node to call tb_dirtree_parentfd() on yet | ||
356 | int fd = parent ? parent->dirfd : AT_FDCWD, | ||
357 | sym = AT_SYMLINK_NOFOLLOW*!(flags&TB_DIRTREE_SYMFOLLOW); | ||
358 | |||
359 | // stat dangling symlinks | ||
360 | if (fstatat(fd, name, &st, sym)) { | ||
361 | // If we got ENOENT without NOFOLLOW, try again with NOFOLLOW. | ||
362 | if (errno!=ENOENT || sym || fstatat(fd, name, &st, AT_SYMLINK_NOFOLLOW)) { | ||
363 | if (flags&TB_DIRTREE_STATLESS) statless++; | ||
364 | else goto error; | ||
365 | } | ||
366 | } | ||
367 | if (!statless && S_ISLNK(st.st_mode)) { | ||
368 | if (0>(linklen = readlinkat(fd, name, libbuf, 4095))) goto error; | ||
369 | libbuf[linklen++]=0; | ||
370 | } | ||
371 | len = strlen(name); | ||
372 | } | ||
373 | |||
374 | // Allocate/populate return structure | ||
375 | dt = xmalloc((len = sizeof(struct tb_dirtree)+len+1)+linklen); | ||
376 | memset(dt, 0, statless ? offsetof(struct tb_dirtree, again) | ||
377 | : offsetof(struct tb_dirtree, st)); | ||
378 | dt->parent = parent; | ||
379 | dt->again = statless ? 2 : 0; | ||
380 | if (!statless) memcpy(&dt->st, &st, sizeof(struct stat)); | ||
381 | strcpy(dt->name, name ? name : ""); | ||
382 | if (linklen) dt->symlink = memcpy(len+(char *)dt, libbuf, linklen); | ||
383 | |||
384 | return dt; | ||
385 | |||
386 | error: | ||
387 | if (!(flags&TB_DIRTREE_SHUTUP) && !tb_isdotdot(name)) { | ||
388 | char *path = parent ? tb_dirtree_path(parent, 0) : ""; | ||
389 | |||
390 | tb_perror_msg("%s%s%s", path, parent ? "/" : "", name); | ||
391 | if (parent) free(path); | ||
392 | } | ||
393 | if (parent) parent->symlink = (char *)1; | ||
394 | free(dt); | ||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | // Return path to this node, assembled recursively. | ||
399 | |||
400 | // Initial call can pass in NULL to plen, or point to an int initialized to 0 | ||
401 | // to return the length of the path, or a value greater than 0 to allocate | ||
402 | // extra space if you want to append your own text to the string. | ||
403 | |||
404 | char *tb_dirtree_path(struct tb_dirtree *node, int *plen) | ||
405 | { | ||
406 | char *path; | ||
407 | int len; | ||
408 | |||
409 | if (!node) { | ||
410 | path = xmalloc(*plen); | ||
411 | *plen = 0; | ||
412 | return path; | ||
413 | } | ||
414 | |||
415 | len = (plen ? *plen : 0)+strlen(node->name)+1; | ||
416 | path = tb_dirtree_path(node->parent, &len); | ||
417 | if (len && path[len-1] != '/') path[len++]='/'; | ||
418 | len = stpcpy(path+len, node->name) - path; | ||
419 | if (plen) *plen = len; | ||
420 | |||
421 | return path; | ||
422 | } | ||
423 | |||
424 | int tb_dirtree_parentfd(struct tb_dirtree *node) | ||
425 | { | ||
426 | return node->parent ? node->parent->dirfd : AT_FDCWD; | ||
427 | } | ||
428 | |||
429 | // Handle callback for a node in the tree. Returns saved node(s) if | ||
430 | // callback returns TB_DIRTREE_SAVE, otherwise frees consumed nodes and | ||
431 | // returns NULL. If !callback return top node unchanged. | ||
432 | // If !new return TB_DIRTREE_ABORTVAL | ||
433 | |||
434 | struct tb_dirtree *tb_dirtree_handle_callback(struct tb_dirtree *new, | ||
435 | int (*callback)(struct tb_dirtree *node)) | ||
436 | { | ||
437 | int flags; | ||
438 | |||
439 | if (!new) return TB_DIRTREE_ABORTVAL; | ||
440 | if (!callback) return new; | ||
441 | flags = callback(new); | ||
442 | |||
443 | if (S_ISDIR(new->st.st_mode) && (flags & (TB_DIRTREE_RECURSE|TB_DIRTREE_COMEAGAIN))) | ||
444 | flags = tb_dirtree_recurse(new, callback, | ||
445 | openat(tb_dirtree_parentfd(new), new->name, O_CLOEXEC), flags); | ||
446 | |||
447 | // If this had children, it was callback's job to free them already. | ||
448 | if (!(flags & TB_DIRTREE_SAVE)) { | ||
449 | free(new); | ||
450 | new = 0; | ||
451 | } | ||
452 | |||
453 | return (flags & TB_DIRTREE_ABORT)==TB_DIRTREE_ABORT ? TB_DIRTREE_ABORTVAL : new; | ||
454 | } | ||
455 | |||
456 | // Recursively read/process children of directory node, filtering through | ||
457 | // callback(). Uses and closes supplied ->dirfd. | ||
458 | |||
459 | int tb_dirtree_recurse(struct tb_dirtree *node, | ||
460 | int (*callback)(struct tb_dirtree *node), int dirfd, int flags) | ||
461 | { | ||
462 | struct tb_dirtree *new, **ddt = &(node->child); | ||
463 | struct dirent *entry; | ||
464 | DIR *dir; | ||
465 | |||
466 | node->dirfd = dirfd; | ||
467 | if (node->dirfd == -1 || !(dir = fdopendir(node->dirfd))) { | ||
468 | if (!(flags & TB_DIRTREE_SHUTUP)) { | ||
469 | char *path = tb_dirtree_path(node, 0); | ||
470 | tb_perror_msg_raw(path); | ||
471 | free(path); | ||
472 | } | ||
473 | close(node->dirfd); | ||
474 | |||
475 | return flags; | ||
476 | } | ||
477 | |||
478 | // according to the fddir() man page, the filehandle in the DIR * can still | ||
479 | // be externally used by things that don't lseek() it. | ||
480 | |||
481 | // The extra parentheses are to shut the stupid compiler up. | ||
482 | while ((entry = readdir(dir))) { | ||
483 | if ((flags&TB_DIRTREE_PROC) && !isdigit(*entry->d_name)) continue; | ||
484 | if (!(new = tb_dirtree_add_node(node, entry->d_name, flags))) continue; | ||
485 | if (!new->st.st_blksize && !new->st.st_mode) | ||
486 | new->st.st_mode = entry->d_type<<12; | ||
487 | new = tb_dirtree_handle_callback(new, callback); | ||
488 | if (new == TB_DIRTREE_ABORTVAL) break; | ||
489 | if (new) { | ||
490 | *ddt = new; | ||
491 | ddt = &((*ddt)->next); | ||
492 | } | ||
493 | } | ||
494 | |||
495 | if (flags & TB_DIRTREE_COMEAGAIN) { | ||
496 | node->again |= 1; | ||
497 | flags = callback(node); | ||
498 | } | ||
499 | |||
500 | // This closes filehandle as well, so note it | ||
501 | closedir(dir); | ||
502 | node->dirfd = -1; | ||
503 | |||
504 | return flags; | ||
505 | } | ||
506 | |||
507 | void tb_dirtree_free(struct tb_dirtree *new) | ||
508 | { | ||
509 | struct tb_dirtree *dt; | ||
510 | |||
511 | if ((dt = new)) { | ||
512 | new = 0; | ||
513 | while (dt->child) { | ||
514 | new = dt->child->next; | ||
515 | free(dt->child); | ||
516 | dt->child = new; | ||
517 | } | ||
518 | free(dt); | ||
519 | } | ||
520 | } | ||