1 /* xargs.c - Run command with arguments taken from stdin. 2 * 3 * Copyright 2011 Rob Landley <rob (at) 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 9 USE_XARGS(NEWTOY(xargs, "^I:E:L#ptxrn#<1s#0", TOYFLAG_USR|TOYFLAG_BIN)) 10 11 config 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 31 config 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 43 GLOBALS( 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 62 static 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 for (;;) { 83 if (++TT.bytes >= TT.max_bytes && TT.max_bytes) return save; 84 if (!*s || isspace(*s)) break; 85 s++; 86 } 87 if (TT.eofstr) { 88 int len = s-save; 89 if (len == strlen(TT.eofstr) && !strncmp(save, TT.eofstr, len)) 90 return (char *)2; 91 } 92 if (entry) entry[TT.entries] = save; 93 ++TT.entries; 94 } 95 96 // -0 support 97 } else { 98 TT.bytes += strlen(data)+1; 99 if (TT.max_bytes && TT.bytes >= TT.max_bytes) return data; 100 if (TT.max_entries && TT.entries >= TT.max_entries) 101 return (char *)1; 102 if (entry) entry[TT.entries] = data; 103 TT.entries++; 104 } 105 106 return NULL; 107 } 108 109 void xargs_main(void) 110 { 111 struct double_list *dlist = NULL, *dtemp; 112 int entries, bytes, done = 0, status; 113 char *data = NULL, **out; 114 pid_t pid; 115 116 if (!(toys.optflags & FLAG_0)) TT.delim = '\n'; 117 118 // If no optargs, call echo. 119 if (!toys.optc) { 120 free(toys.optargs); 121 *(toys.optargs = xzalloc(2*sizeof(char *)))="echo"; 122 toys.optc = 1; 123 } 124 125 for (entries = 0, bytes = -1; entries < toys.optc; entries++, bytes++) 126 bytes += strlen(toys.optargs[entries]); 127 128 // Loop through exec chunks. 129 while (data || !done) { 130 TT.entries = 0; 131 TT.bytes = bytes; 132 133 // Loop reading input 134 for (;;) { 135 136 // Read line 137 if (!data) { 138 ssize_t l = 0; 139 l = getdelim(&data, (size_t *)&l, TT.delim, stdin); 140 141 if (l<0) { 142 data = 0; 143 done++; 144 break; 145 } 146 } 147 dlist_add(&dlist, data); 148 149 // Count data used 150 data = handle_entries(data, NULL); 151 if (!data) continue; 152 if (data == (char *)2) done++; 153 if ((long)data <= 2) data = 0; 154 else data = xstrdup(data); 155 156 break; 157 } 158 159 // Accumulate cally thing 160 161 if (data && !TT.entries) error_exit("argument too long"); 162 out = xzalloc((entries+TT.entries+1)*sizeof(char *)); 163 164 // Fill out command line to exec 165 memcpy(out, toys.optargs, entries*sizeof(char *)); 166 TT.entries = 0; 167 TT.bytes = bytes; 168 if (dlist) dlist->prev->next = 0; 169 for (dtemp = dlist; dtemp; dtemp = dtemp->next) 170 handle_entries(dtemp->data, out+entries); 171 172 if (!(pid = XVFORK())) { 173 xclose(0); 174 open("/dev/null", O_RDONLY); 175 xexec(out); 176 } 177 waitpid(pid, &status, 0); 178 status = WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status)+127; 179 180 // Abritrary number of execs, can't just leak memory each time... 181 while (dlist) { 182 struct double_list *dtemp = dlist->next; 183 184 free(dlist->data); 185 free(dlist); 186 dlist = dtemp; 187 } 188 free(out); 189 } 190 } 191