diff options
Diffstat (limited to 'toys/posix/xargs.c')
-rw-r--r-- | toys/posix/xargs.c | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/toys/posix/xargs.c b/toys/posix/xargs.c new file mode 100644 index 00000000..0d513253 --- /dev/null +++ b/toys/posix/xargs.c @@ -0,0 +1,188 @@ +/* vi: set sw=4 ts=4: + * + * xargs.c - Run command with arguments taken from stdin. + * + * Copyright 2011 Rob Landley <rob@landley.net> + * + * See http://opengroup.org/onlinepubs/9699919799/utilities/xargs.html + +USE_XARGS(NEWTOY(xargs, "^I:E:L#ptxrn#<1s#0", TOYFLAG_USR|TOYFLAG_BIN)) + +config XARGS + bool "xargs" + default y + help + usage: xargs [-ptxr0] [-s NUM] [-n NUM] [-L NUM] [-E STR] COMMAND... + + Run command line one or more times, appending arguments from stdin. + + If command exits with 255, don't launch another even if arguments remain. + + -s Size in bytes per command line + -n Max number of arguments per command + -0 Each argument is NULL terminated, no whitespace or quote processing + #-p Prompt for y/n from tty before running each command + #-t Trace, print command line to stderr + #-x Exit if can't fit everything in one command + #-r Don't run command with empty input + #-L Max number of lines of input per command + -E stop at line matching string +*/ + +#include "toys.h" + +DEFINE_GLOBALS( + long max_bytes; + long max_entries; + long L; + char *eofstr; + char *I; + + long entries, bytes; + char delim; +) + +#define TT this.xargs + +// If out==NULL count TT.bytes and TT.entries, stopping at max. +// Otherwise, fill out out[] + +// Returning NULL means need more data. +// Returning char * means hit data limits, start of data left over +// Returning 1 means hit data limits, but consumed all data +// Returning 2 means hit -E eofstr + +static char *handle_entries(char *data, char **entry) +{ + if (TT.delim) { + char *s = data; + + // Chop up whitespace delimited string into args + while (*s) { + char *save; + + while (isspace(*s)) { + if (entry) *s = 0; + s++; + } + + if (TT.max_entries && TT.entries >= TT.max_entries) + return *s ? s : (char *)1; + + if (!*s) break; + save = s; + + for (;;) { + if (++TT.bytes >= TT.max_bytes && TT.max_bytes) return save; + if (!*s || isspace(*s)) break; + s++; + } + if (TT.eofstr) { + int len = s-save; + if (len == strlen(TT.eofstr) && !strncmp(save, TT.eofstr, len)) + return (char *)2; + } + if (entry) entry[TT.entries] = save; + ++TT.entries; + } + + // -0 support + } else { + TT.bytes += strlen(data)+1; + if (TT.max_bytes && TT.bytes >= TT.max_bytes) return data; + if (TT.max_entries && TT.entries >= TT.max_entries) + return (char *)1; + if (entry) entry[TT.entries] = data; + TT.entries++; + } + + return NULL; +} + +void xargs_main(void) +{ + struct double_list *dlist = NULL; + int entries, bytes, done = 0, status; + char *data = NULL; + + if (!(toys.optflags&1)) TT.delim = '\n'; + + // If no optargs, call echo. + if (!toys.optc) { + free(toys.optargs); + *(toys.optargs = xzalloc(2*sizeof(char *)))="echo"; + toys.optc = 1; + } + + for (entries = 0, bytes = -1; entries < toys.optc; entries++, bytes++) + bytes += strlen(toys.optargs[entries]); + + // Loop through exec chunks. + while (data || !done) { + char **out; + + TT.entries = 0; + TT.bytes = bytes; + + // Loop reading input + for (;;) { + + // Read line + if (!data) { + ssize_t l = 0; + l = getdelim(&data, (size_t *)&l, TT.delim, stdin); + + if (l<0) { + data = 0; + done++; + break; + } + } + dlist_add(&dlist, data); + + // Count data used + data = handle_entries(data, NULL); + if (!data) continue; + if (data == (char *)2) done++; + if ((long)data <= 2) data = 0; + else data = xstrdup(data); + + break; + } + + // Accumulate cally thing + + if (data && !TT.entries) error_exit("argument too long"); + out = xzalloc((entries+TT.entries+1)*sizeof(char *)); + + if (dlist) { + struct double_list *dtemp; + + // Fill out command line to exec + memcpy(out, toys.optargs, entries*sizeof(char *)); + TT.entries = 0; + TT.bytes = bytes; + dlist->prev->next = 0; + for (dtemp = dlist; dtemp; dtemp = dtemp->next) + handle_entries(dtemp->data, out+entries); + } + pid_t pid=fork(); + if (!pid) { + xclose(0); + open("/dev/null", O_RDONLY); + xexec(out); + } + waitpid(pid, &status, 0); + status = WEXITSTATUS(status); + + // Abritrary number of execs, can't just leak memory each time... + while (dlist) { + struct double_list *dtemp = dlist->next; + + free(dlist->data); + free(dlist); + dlist = dtemp; + } + free(out); + } +} |