aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/toybox.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/toybox.c')
-rw-r--r--src/toybox.c520
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
4char libbuf[4096];
5
6
7// From lib.c
8
9void 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
25void 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
34void 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.
44void 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)
56void 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).
74void tb_error_msg_raw(char *msg)
75{
76 tb_error_msg("%s", msg);
77}
78
79void tb_perror_msg_raw(char *msg)
80{
81 tb_perror_msg("%s", msg);
82}
83
84void tb_error_exit_raw(char *msg)
85{
86 tb_error_exit("%s", msg);
87}
88
89void tb_perror_exit_raw(char *msg)
90{
91 tb_perror_exit("%s", msg);
92}
93
94// Sleep for this many thousandths of a second
95void 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.
105char *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.)
126int 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
142void _xexit(void)
143{
144// if (toys.rebound) siglongjmp(*toys.rebound, 1);
145
146// _exit(toys.exitval);
147 _exit(0);
148}
149
150void 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.
167void *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.
176void *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.)
185void *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.
194char *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.
204char *xstrdup(char *s)
205{
206 return xstrndup(s, strlen(s));
207}
208
209void *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.
218char *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
241void 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.
250int 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.
259int xopen_stdio(char *path, int flags)
260{
261 return xcreate_stdio(path, flags, 0);
262}
263
264void xclose(int fd)
265{
266 if (close(fd)) tb_perror_exit("xclose");
267}
268
269int 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...)
280int 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
296int 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
302int 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.
310int xopenro(char *path)
311{
312 return openro(path, O_RDONLY|WARN_ONLY);
313}
314
315// Compile a regular expression into a regex_t
316void 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
337int 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
348struct 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
386error:
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
404char *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
424int 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
434struct 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
459int 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
507void 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}