#include "toybox.h" char libbuf[4096]; // From lib.c void tb_verror_msg(char *msg, int err, va_list va) { char *s = ": %s"; // fprintf(stderr, "%s: ", toys.which->name); if (msg) vfprintf(stderr, msg, va); else s+=2; if (err>0) fprintf(stderr, s, strerror(err)); // if (err<0 && CFG_TOYBOX_HELP) // fprintf(stderr, " (see \"%s --help\")", toys.which->name); if (msg || err) putc('\n', stderr); // if (!toys.exitval) toys.exitval++; } // These functions don't collapse together because of the va_stuff. void tb_error_msg(char *msg, ...) { va_list va; va_start(va, msg); tb_verror_msg(msg, 0, va); va_end(va); } void tb_perror_msg(char *msg, ...) { va_list va; va_start(va, msg); tb_verror_msg(msg, errno, va); va_end(va); } // Die with an error message. void tb_error_exit(char *msg, ...) { va_list va; va_start(va, msg); tb_verror_msg(msg, 0, va); va_end(va); xexit(); } // Die with an error message and strerror(errno) void tb_perror_exit(char *msg, ...) { // Die silently if our pipeline exited. if (errno != EPIPE) { va_list va; va_start(va, msg); tb_verror_msg(msg, errno, va); va_end(va); } xexit(); } // If you want to explicitly disable the printf() behavior (because you're // printing user-supplied data, or because android's static checker produces // false positives for 'char *s = x ? "blah1" : "blah2"; printf(s);' and it's // -Werror there for policy reasons). void tb_error_msg_raw(char *msg) { tb_error_msg("%s", msg); } void tb_perror_msg_raw(char *msg) { tb_perror_msg("%s", msg); } void tb_error_exit_raw(char *msg) { tb_error_exit("%s", msg); } void tb_perror_exit_raw(char *msg) { tb_perror_exit("%s", msg); } // Sleep for this many thousandths of a second void msleep(long milliseconds) { struct timespec ts; ts.tv_sec = milliseconds/1000; ts.tv_nsec = (milliseconds%1000)*1000000; nanosleep(&ts, &ts); } // Slow, but small. char *get_line(int fd) { char c, *buf = NULL; long len = 0; for (;;) { if (1>read(fd, &c, 1)) break; if (!(len & 63)) buf=xrealloc(buf, len+65); if ((buf[len++]=c) == '\n') break; } if (buf) { buf[len]=0; if (buf[--len]=='\n') buf[len]=0; } return buf; } // The qsort man page says you can use alphasort, the posix committee // disagreed, and doubled down: http://austingroupbugs.net/view.php?id=142 // So just do our own. (The const is entirely to humor the stupid compiler.) int qstrcmp(const void *a, const void *b) { return strcmp(*(char **)a, *(char **)b); } // From xwrap.c // We replaced exit(), _exit(), and atexit() with xexit(), _xexit(), and // sigatexit(). This gives _xexit() the option to siglongjmp(toys.rebound, 1) // instead of exiting, lets xexit() report stdout flush failures to stderr // and change the exit code to indicate error, lets our toys.exit function // change happen for signal exit paths and lets us remove the functions // after we've called them. void _xexit(void) { // if (toys.rebound) siglongjmp(*toys.rebound, 1); // _exit(toys.exitval); _exit(0); } void xexit(void) { // Call toys.xexit functions in reverse order added. // while (toys.xexit) { // struct arg_list *al = tb_llist_pop(&toys.xexit); // typecast xexit->arg to a function pointer, then call it using invalid // signal 0 to let signal handlers tell actual signal from regular exit. // ((void (*)(int))(al->arg))(0); // free(al); // } xflush(1); _xexit(); } // Die unless we can allocate memory. void *xmalloc(size_t size) { void *ret = malloc(size); if (!ret) tb_error_exit("xmalloc(%ld)", (long)size); return ret; } // Die unless we can allocate prezeroed memory. void *xzalloc(size_t size) { void *ret = xmalloc(size); memset(ret, 0, size); return ret; } // Die unless we can change the size of an existing allocation, possibly // moving it. (Notice different arguments from libc function.) void *xrealloc(void *ptr, size_t size) { ptr = realloc(ptr, size); if (!ptr) tb_error_exit("xrealloc"); return ptr; } // Die unless we can allocate a copy of this many bytes of string. char *xstrndup(char *s, size_t n) { char *ret = strndup(s, n); if (!ret) tb_error_exit("xstrndup"); return ret; } // Die unless we can allocate a copy of this string. char *xstrdup(char *s) { return xstrndup(s, strlen(s)); } void *xmemdup(void *s, long len) { void *ret = xmalloc(len); memcpy(ret, s, len); return ret; } // Die unless we can allocate enough space to sprintf() into. char *xmprintf(char *format, ...) { va_list va, va2; int len; char *ret; va_start(va, format); va_copy(va2, va); // How long is it? len = vsnprintf(0, 0, format, va); len++; va_end(va); // Allocate and do the sprintf() ret = xmalloc(len); vsnprintf(ret, len, format, va2); va_end(va2); return ret; } // if !flush just check for error on stdout without flushing void xflush(int flush) { if ((flush && fflush(0)) || ferror(stdout)) ;// if (!toys.exitval) tb_perror_msg("write"); } // Die unless we can open/create a file, returning file descriptor. // The meaning of O_CLOEXEC is reversed (it defaults on, pass it to disable) // and WARN_ONLY tells us not to exit. int xcreate_stdio(char *path, int flags, int mode) { int fd = open(path, (flags^O_CLOEXEC)&~WARN_ONLY, mode); if (fd == -1) ((mode&WARN_ONLY) ? tb_perror_msg_raw : tb_perror_exit_raw)(path); return fd; } // Die unless we can open a file, returning file descriptor. int xopen_stdio(char *path, int flags) { return xcreate_stdio(path, flags, 0); } void xclose(int fd) { if (close(fd)) tb_perror_exit("xclose"); } int xdup(int fd) { if (fd != -1) { fd = dup(fd); if (fd == -1) tb_perror_exit("xdup"); } return fd; } // Move file descriptor above stdin/stdout/stderr, using /dev/null to consume // old one. (We should never be called with stdin/stdout/stderr closed, but...) int notstdio(int fd) { if (fd<0) return fd; while (fd<3) { int fd2 = xdup(fd); close(fd); xopen_stdio("/dev/null", O_RDWR); fd = fd2; } return fd; } // Open a file descriptor NOT in stdin/stdout/stderr int xopen(char *path, int flags) { return notstdio(xopen_stdio(path, flags)); } // Open read only, treating "-" as a synonym for stdin, defaulting to warn only int openro(char *path, int flags) { if (!strcmp(path, "-")) return 0; return xopen(path, flags^WARN_ONLY); } // Open read only, treating "-" as a synonym for stdin. int xopenro(char *path) { return openro(path, O_RDONLY|WARN_ONLY); } // Compile a regular expression into a regex_t void xregcomp(regex_t *preg, char *regex, int cflags) { int rc; // BSD regex implementations don't support the empty regex (which isn't // allowed in the POSIX grammar), but glibc does. Fake it for BSD. if (!*regex) { regex = "()"; cflags |= REG_EXTENDED; } if ((rc = regcomp(preg, regex, cflags))) { regerror(rc, preg, libbuf, sizeof(libbuf)); tb_error_exit("bad regex: %s", libbuf); } } // From dirtree.c int tb_isdotdot(char *name) { if (name[0]=='.' && (!name[1] || (name[1]=='.' && !name[2]))) return 1; return 0; } // Create a tb_dirtree node from a path, with stat and symlink info. // (This doesn't open directory filehandles yet so as not to exhaust the // filehandle space on large trees, tb_dirtree_handle_callback() does that.) struct tb_dirtree *tb_dirtree_add_node(struct tb_dirtree *parent, char *name, int flags) { struct tb_dirtree *dt = 0; struct stat st; int len = 0, linklen = 0, statless = 0; if (name) { // open code fd = because haven't got node to call tb_dirtree_parentfd() on yet int fd = parent ? parent->dirfd : AT_FDCWD, sym = AT_SYMLINK_NOFOLLOW*!(flags&TB_DIRTREE_SYMFOLLOW); // stat dangling symlinks if (fstatat(fd, name, &st, sym)) { // If we got ENOENT without NOFOLLOW, try again with NOFOLLOW. if (errno!=ENOENT || sym || fstatat(fd, name, &st, AT_SYMLINK_NOFOLLOW)) { if (flags&TB_DIRTREE_STATLESS) statless++; else goto error; } } if (!statless && S_ISLNK(st.st_mode)) { if (0>(linklen = readlinkat(fd, name, libbuf, 4095))) goto error; libbuf[linklen++]=0; } len = strlen(name); } // Allocate/populate return structure dt = xmalloc((len = sizeof(struct tb_dirtree)+len+1)+linklen); memset(dt, 0, statless ? offsetof(struct tb_dirtree, again) : offsetof(struct tb_dirtree, st)); dt->parent = parent; dt->again = statless ? 2 : 0; if (!statless) memcpy(&dt->st, &st, sizeof(struct stat)); strcpy(dt->name, name ? name : ""); if (linklen) dt->symlink = memcpy(len+(char *)dt, libbuf, linklen); return dt; error: if (!(flags&TB_DIRTREE_SHUTUP) && !tb_isdotdot(name)) { char *path = parent ? tb_dirtree_path(parent, 0) : ""; tb_perror_msg("%s%s%s", path, parent ? "/" : "", name); if (parent) free(path); } if (parent) parent->symlink = (char *)1; free(dt); return 0; } // Return path to this node, assembled recursively. // Initial call can pass in NULL to plen, or point to an int initialized to 0 // to return the length of the path, or a value greater than 0 to allocate // extra space if you want to append your own text to the string. char *tb_dirtree_path(struct tb_dirtree *node, int *plen) { char *path; int len; if (!node) { path = xmalloc(*plen); *plen = 0; return path; } len = (plen ? *plen : 0)+strlen(node->name)+1; path = tb_dirtree_path(node->parent, &len); if (len && path[len-1] != '/') path[len++]='/'; len = stpcpy(path+len, node->name) - path; if (plen) *plen = len; return path; } int tb_dirtree_parentfd(struct tb_dirtree *node) { return node->parent ? node->parent->dirfd : AT_FDCWD; } // Handle callback for a node in the tree. Returns saved node(s) if // callback returns TB_DIRTREE_SAVE, otherwise frees consumed nodes and // returns NULL. If !callback return top node unchanged. // If !new return TB_DIRTREE_ABORTVAL struct tb_dirtree *tb_dirtree_handle_callback(struct tb_dirtree *new, int (*callback)(struct tb_dirtree *node)) { int flags; if (!new) return TB_DIRTREE_ABORTVAL; if (!callback) return new; flags = callback(new); if (S_ISDIR(new->st.st_mode) && (flags & (TB_DIRTREE_RECURSE|TB_DIRTREE_COMEAGAIN))) flags = tb_dirtree_recurse(new, callback, openat(tb_dirtree_parentfd(new), new->name, O_CLOEXEC), flags); // If this had children, it was callback's job to free them already. if (!(flags & TB_DIRTREE_SAVE)) { free(new); new = 0; } return (flags & TB_DIRTREE_ABORT)==TB_DIRTREE_ABORT ? TB_DIRTREE_ABORTVAL : new; } // Recursively read/process children of directory node, filtering through // callback(). Uses and closes supplied ->dirfd. int tb_dirtree_recurse(struct tb_dirtree *node, int (*callback)(struct tb_dirtree *node), int dirfd, int flags) { struct tb_dirtree *new, **ddt = &(node->child); struct dirent *entry; DIR *dir; node->dirfd = dirfd; if (node->dirfd == -1 || !(dir = fdopendir(node->dirfd))) { if (!(flags & TB_DIRTREE_SHUTUP)) { char *path = tb_dirtree_path(node, 0); tb_perror_msg_raw(path); free(path); } close(node->dirfd); return flags; } // according to the fddir() man page, the filehandle in the DIR * can still // be externally used by things that don't lseek() it. // The extra parentheses are to shut the stupid compiler up. while ((entry = readdir(dir))) { if ((flags&TB_DIRTREE_PROC) && !isdigit(*entry->d_name)) continue; if (!(new = tb_dirtree_add_node(node, entry->d_name, flags))) continue; if (!new->st.st_blksize && !new->st.st_mode) new->st.st_mode = entry->d_type<<12; new = tb_dirtree_handle_callback(new, callback); if (new == TB_DIRTREE_ABORTVAL) break; if (new) { *ddt = new; ddt = &((*ddt)->next); } } if (flags & TB_DIRTREE_COMEAGAIN) { node->again |= 1; flags = callback(node); } // This closes filehandle as well, so note it closedir(dir); node->dirfd = -1; return flags; } void tb_dirtree_free(struct tb_dirtree *new) { struct tb_dirtree *dt; if ((dt = new)) { new = 0; while (dt->child) { new = dt->child->next; free(dt->child); dt->child = new; } free(dt); } }