1/* xargs.c - Run command with arguments taken from stdin. 2 * 3 * Copyright 2011 Rob Landley <rob@landley.net> 4 * 5 * See http://opengroup.org/onlinepubs/9699919799/utilities/xargs.html 6 * 7 * TODO: Rich's whitespace objection, env size isn't fixed anymore. 8 * TODO: -I Insert mode 9 * TODO: -L Max number of lines of input per command 10 * TODO: -x Exit if can't fit everything in one command 11 * TODO: -P NUM Run up to NUM processes at once 12 13USE_XARGS(NEWTOY(xargs, "^E:P#optrn#<1(max-args)s#0[!0E]", TOYFLAG_USR|TOYFLAG_BIN)) 14 15config XARGS 16 bool "xargs" 17 default y 18 help 19 usage: xargs [-0prt] [-s NUM] [-n NUM] [-E STR] COMMAND... 20 21 Run command line one or more times, appending arguments from stdin. 22 23 If COMMAND exits with 255, don't launch another even if arguments remain. 24 25 -0 Each argument is NULL terminated, no whitespace or quote processing 26 -E Stop at line matching string 27 -n Max number of arguments per command 28 -o Open tty for COMMAND's stdin (default /dev/null) 29 -p Prompt for y/n from tty before running each command 30 -r Don't run command with empty input (otherwise always run command once) 31 -s Size in bytes per command line 32 -t Trace, print command line to stderr 33*/ 34 35#define FOR_xargs 36#include "toys.h" 37 38GLOBALS( 39 long s, n, P; 40 char *E; 41 42 long entries, bytes; 43 char delim; 44 FILE *tty; 45) 46 47// If !entry count TT.bytes and TT.entries, stopping at max. 48// Otherwise, fill out entry[]. 49 50// Returning NULL means need more data. 51// Returning char * means hit data limits, start of data left over 52// Returning 1 means hit data limits, but consumed all data 53// Returning 2 means hit -E STR 54 55static char *handle_entries(char *data, char **entry) 56{ 57 if (TT.delim) { 58 char *save, *s = data; 59 60 // Chop up whitespace delimited string into args 61 while (*s) { 62 while (isspace(*s)) { 63 if (entry) *s = 0; 64 s++; 65 } 66 67 if (TT.n && TT.entries >= TT.n) 68 return *s ? s : (char *)1; 69 70 if (!*s) break; 71 save = s; 72 73 // We ought to add sizeof(char *) to TT.bytes to be correct, but we don't 74 // for bug compatibility with busybox 1.30.1 and findutils 4.7.0. 75 76 for (;;) { 77 if (++TT.bytes >= TT.s && TT.s) return save; 78 if (!*s || isspace(*s)) break; 79 s++; 80 } 81 if (TT.E && strstart(&save, TT.E)) return (char *)2; 82 if (entry) entry[TT.entries] = save; 83 ++TT.entries; 84 } 85 86 // -0 support 87 } else { 88 TT.bytes += sizeof(char *)+strlen(data)+1; 89 if ((TT.s && TT.bytes >= TT.s) || (TT.n && TT.entries >= TT.n)) return data; 90 if (entry) entry[TT.entries] = data; 91 TT.entries++; 92 } 93 94 return 0; 95} 96 97void xargs_main(void) 98{ 99 struct double_list *dlist = 0, *dtemp; 100 int entries, bytes, done = 0, ran_once = 0, status; 101 char *data = 0, **out; 102 pid_t pid; 103 104 // POSIX requires that we never hit the ARG_MAX limit, even if we try to 105 // with -s. POSIX also says we have to reserve 2048 bytes "to guarantee 106 // that the invoked utility has room to modify its environment variables 107 // and command line arguments and still be able to invoke another utility", 108 // though obviously that's not really something you can guarantee. 109 bytes = sysconf(_SC_ARG_MAX) - environ_bytes() - 2048; 110 if (!TT.s || TT.s > bytes) TT.s = bytes; 111 112 TT.delim = '\n'*!FLAG(0); 113 114 // If no optargs, call echo. 115 if (!toys.optc) { 116 free(toys.optargs); 117 *(toys.optargs = xzalloc(2*sizeof(char *)))="echo"; 118 toys.optc = 1; 119 } 120 121 // count entries 122 for (entries = 0, bytes = -1; entries < toys.optc; entries++, bytes++) 123 bytes += strlen(toys.optargs[entries]); 124 if (bytes >= TT.s) error_exit("argument too long"); 125 126 // Loop through exec chunks. 127 while (data || !done) { 128 TT.entries = 0; 129 TT.bytes = bytes; 130 131 // Loop reading input 132 for (;;) { 133 134 // Read line 135 if (!data) { 136 ssize_t l = 0; 137 if (getdelim(&data, (size_t *)&l, TT.delim, stdin)<0) { 138 data = 0; 139 done++; 140 141 break; 142 } 143 } 144 dlist_add(&dlist, data); 145 146 // Count data used 147 if (!(data = handle_entries(data, 0))) continue; 148 if (data == (char *)2) done++; 149 if ((unsigned long)data <= 2) data = 0; 150 else data = xstrdup(data); 151 152 break; 153 } 154 155 if (!TT.entries) { 156 if (data) error_exit("argument too long"); 157 else if (ran_once) return; 158 else if (FLAG(r)) continue; 159 } 160 161 // Fill out command line to exec 162 out = xzalloc((entries+TT.entries+1)*sizeof(char *)); 163 memcpy(out, toys.optargs, entries*sizeof(char *)); 164 TT.entries = 0; 165 TT.bytes = bytes; 166 if (dlist) dlist->prev->next = 0; 167 for (dtemp = dlist; dtemp; dtemp = dtemp->next) 168 handle_entries(dtemp->data, out+entries); 169 170 if (FLAG(p) || FLAG(t)) { 171 int i; 172 173 for (i = 0; out[i]; ++i) fprintf(stderr, "%s ", out[i]); 174 if (FLAG(p)) { 175 fprintf(stderr, "?"); 176 if (!TT.tty) TT.tty = xfopen("/dev/tty", "re"); 177 if (!fyesno(TT.tty, 0)) goto skip; 178 } else fprintf(stderr, "\n"); 179 } 180 181 if (!(pid = XVFORK())) { 182 close(0); 183 xopen_stdio(FLAG(o) ? "/dev/tty" : "/dev/null", O_RDONLY); 184 xexec(out); 185 } 186 waitpid(pid, &status, 0); 187 188 // xargs is yet another weird collection of exit value special cases, 189 // different from all the others. 190 if (WIFEXITED(status)) { 191 if (WEXITSTATUS(status) == 126 || WEXITSTATUS(status) == 127) { 192 toys.exitval = WEXITSTATUS(status); 193 return; 194 } else if (WEXITSTATUS(status) >= 1 && WEXITSTATUS(status) <= 125) { 195 toys.exitval = 123; 196 } else if (WEXITSTATUS(status) == 255) { 197 error_msg("%s: exited with status 255; aborting", out[0]); 198 toys.exitval = 124; 199 return; 200 } 201 } else toys.exitval = 127; 202 203 // Abritrary number of execs, can't just leak memory each time... 204skip: 205 ran_once = 1; 206 while (dlist) { 207 struct double_list *dtemp = dlist->next; 208 209 free(dlist->data); 210 free(dlist); 211 dlist = dtemp; 212 } 213 free(out); 214 } 215 if (TT.tty) fclose(TT.tty); 216} 217