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 9USE_XARGS(NEWTOY(xargs, "^I:E:L#ptxrn#<1s#0[!0E]", TOYFLAG_USR|TOYFLAG_BIN)) 10 11config XARGS 12 bool "xargs" 13 default y 14 help 15 usage: xargs [-ptxr0] [-s NUM] [-n NUM] [-L NUM] [-E STR] COMMAND... 16 17 Run command line one or more times, appending arguments from stdin. 18 19 If command exits with 255, don't launch another even if arguments remain. 20 21 -s Size in bytes per command line 22 -n Max number of arguments per command 23 -0 Each argument is NULL terminated, no whitespace or quote processing 24 #-p Prompt for y/n from tty before running each command 25 #-t Trace, print command line to stderr 26 #-x Exit if can't fit everything in one command 27 #-r Don't run command with empty input 28 #-L Max number of lines of input per command 29 -E stop at line matching string 30 31config XARGS_PEDANTIC 32 bool "TODO xargs pedantic posix compatability" 33 default n 34 depends on XARGS 35 help 36 This version supports insane posix whitespace handling rendered obsolete 37 by -0 mode. 38*/ 39 40#define FOR_xargs 41#include "toys.h" 42 43GLOBALS( 44 long max_bytes; 45 long max_entries; 46 long L; 47 char *eofstr; 48 char *I; 49 50 long entries, bytes; 51 char delim; 52) 53 54// If out==NULL count TT.bytes and TT.entries, stopping at max. 55// Otherwise, fill out out[] 56 57// Returning NULL means need more data. 58// Returning char * means hit data limits, start of data left over 59// Returning 1 means hit data limits, but consumed all data 60// Returning 2 means hit -E eofstr 61 62static char *handle_entries(char *data, char **entry) 63{ 64 if (TT.delim) { 65 char *s = data; 66 67 // Chop up whitespace delimited string into args 68 while (*s) { 69 char *save; 70 71 while (isspace(*s)) { 72 if (entry) *s = 0; 73 s++; 74 } 75 76 if (TT.max_entries && TT.entries >= TT.max_entries) 77 return *s ? s : (char *)1; 78 79 if (!*s) break; 80 save = s; 81 82 TT.bytes += sizeof(char *); 83 84 for (;;) { 85 if (++TT.bytes >= TT.max_bytes && TT.max_bytes) return save; 86 if (!*s || isspace(*s)) break; 87 s++; 88 } 89 if (TT.eofstr) { 90 int len = s-save; 91 if (len == strlen(TT.eofstr) && !strncmp(save, TT.eofstr, len)) 92 return (char *)2; 93 } 94 if (entry) entry[TT.entries] = save; 95 ++TT.entries; 96 } 97 98 // -0 support 99 } else { 100 TT.bytes += sizeof(char *)+strlen(data)+1; 101 if (TT.max_bytes && TT.bytes >= TT.max_bytes) return data; 102 if (TT.max_entries && TT.entries >= TT.max_entries) return data; 103 if (entry) entry[TT.entries] = data; 104 TT.entries++; 105 } 106 107 return NULL; 108} 109 110void xargs_main(void) 111{ 112 struct double_list *dlist = NULL, *dtemp; 113 int entries, bytes, done = 0, status; 114 char *data = NULL, **out; 115 pid_t pid; 116 long posix_max_bytes; 117 118 // POSIX requires that we never hit the ARG_MAX limit, even if we try to 119 // with -s. POSIX also says we have to reserve 2048 bytes "to guarantee 120 // that the invoked utility has room to modify its environment variables 121 // and command line arguments and still be able to invoke another utility", 122 // though obviously that's not really something you can guarantee. 123 posix_max_bytes = sysconf(_SC_ARG_MAX) - environ_bytes() - 2048; 124 if (TT.max_bytes == 0 || TT.max_bytes > posix_max_bytes) 125 TT.max_bytes = posix_max_bytes; 126 127 if (!(toys.optflags & FLAG_0)) TT.delim = '\n'; 128 129 // If no optargs, call echo. 130 if (!toys.optc) { 131 free(toys.optargs); 132 *(toys.optargs = xzalloc(2*sizeof(char *)))="echo"; 133 toys.optc = 1; 134 } 135 136 for (entries = 0, bytes = -1; entries < toys.optc; entries++, bytes++) 137 bytes += strlen(toys.optargs[entries]); 138 139 // Loop through exec chunks. 140 while (data || !done) { 141 TT.entries = 0; 142 TT.bytes = bytes; 143 144 // Loop reading input 145 for (;;) { 146 147 // Read line 148 if (!data) { 149 ssize_t l = 0; 150 l = getdelim(&data, (size_t *)&l, TT.delim, stdin); 151 152 if (l<0) { 153 data = 0; 154 done++; 155 break; 156 } 157 } 158 dlist_add(&dlist, data); 159 160 // Count data used 161 data = handle_entries(data, NULL); 162 if (!data) continue; 163 if (data == (char *)2) done++; 164 if ((long)data <= 2) data = 0; 165 else data = xstrdup(data); 166 167 break; 168 } 169 170 // Accumulate cally thing 171 172 if (data && !TT.entries) error_exit("argument too long"); 173 out = xzalloc((entries+TT.entries+1)*sizeof(char *)); 174 175 // Fill out command line to exec 176 memcpy(out, toys.optargs, entries*sizeof(char *)); 177 TT.entries = 0; 178 TT.bytes = bytes; 179 if (dlist) dlist->prev->next = 0; 180 for (dtemp = dlist; dtemp; dtemp = dtemp->next) 181 handle_entries(dtemp->data, out+entries); 182 183 if (!(pid = XVFORK())) { 184 xclose(0); 185 open("/dev/null", O_RDONLY); 186 xexec(out); 187 } 188 waitpid(pid, &status, 0); 189 status = WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status)+127; 190 191 // Abritrary number of execs, can't just leak memory each time... 192 while (dlist) { 193 struct double_list *dtemp = dlist->next; 194 195 free(dlist->data); 196 free(dlist); 197 dlist = dtemp; 198 } 199 free(out); 200 } 201} 202