xargs.c (5961B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <sys/wait.h> 3 4 #include <errno.h> 5 #include <limits.h> 6 #include <stdint.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <unistd.h> 11 12 #include "util.h" 13 14 #define NARGS 10000 15 16 static int inputc(void); 17 static void fillargbuf(int); 18 static int eatspace(void); 19 static int parsequote(int); 20 static int parseescape(void); 21 static char *poparg(void); 22 static void waitchld(int); 23 static void spawn(void); 24 25 static size_t argbsz; 26 static size_t argbpos; 27 static size_t maxargs; 28 static size_t curprocs, maxprocs = 1; 29 static int nerrors; 30 static int nulflag, nflag, pflag, rflag, tflag, xflag, Iflag; 31 static char *argb; 32 static char *cmd[NARGS]; 33 static char *eofstr; 34 35 static int 36 inputc(void) 37 { 38 int ch; 39 40 ch = getc(stdin); 41 if (ch == EOF && ferror(stdin)) 42 eprintf("getc <stdin>:"); 43 44 return ch; 45 } 46 47 static void 48 fillargbuf(int ch) 49 { 50 if (argbpos >= argbsz) { 51 argbsz = argbpos == 0 ? 1 : argbsz * 2; 52 argb = erealloc(argb, argbsz); 53 } 54 argb[argbpos] = ch; 55 } 56 57 static int 58 eatspace(void) 59 { 60 int ch; 61 62 while ((ch = inputc()) != EOF) { 63 if (nulflag || !(ch == ' ' || ch == '\t' || ch == '\n')) { 64 ungetc(ch, stdin); 65 return ch; 66 } 67 } 68 return -1; 69 } 70 71 static int 72 parsequote(int q) 73 { 74 int ch; 75 76 while ((ch = inputc()) != EOF) { 77 if (ch == q) 78 return 0; 79 if (ch != '\n') { 80 fillargbuf(ch); 81 argbpos++; 82 } 83 } 84 85 return -1; 86 } 87 88 static int 89 parseescape(void) 90 { 91 int ch; 92 93 if ((ch = inputc()) != EOF) { 94 fillargbuf(ch); 95 argbpos++; 96 return ch; 97 } 98 99 return -1; 100 } 101 102 static char * 103 poparg(void) 104 { 105 int ch; 106 107 argbpos = 0; 108 if (eatspace() < 0) 109 return NULL; 110 while ((ch = inputc()) != EOF) { 111 /* NUL separator: no escaping */ 112 if (nulflag) { 113 if (ch == '\0') 114 goto out; 115 else 116 goto fill; 117 } 118 119 switch (ch) { 120 case ' ': 121 case '\t': 122 if (Iflag) 123 goto fill; 124 case '\n': 125 goto out; 126 case '\'': 127 if (parsequote('\'') < 0) 128 eprintf("unterminated single quote\n"); 129 break; 130 case '\"': 131 if (parsequote('\"') < 0) 132 eprintf("unterminated double quote\n"); 133 break; 134 case '\\': 135 if (parseescape() < 0) 136 eprintf("backslash at EOF\n"); 137 break; 138 default: 139 fill: 140 fillargbuf(ch); 141 argbpos++; 142 break; 143 } 144 } 145 out: 146 fillargbuf('\0'); 147 148 return (eofstr && !strcmp(argb, eofstr)) ? NULL : argb; 149 } 150 151 static void 152 waitchld(int waitall) 153 { 154 pid_t pid; 155 int status; 156 157 while ((pid = waitpid(-1, &status, !waitall && curprocs < maxprocs ? 158 WNOHANG : 0)) > 0) { 159 curprocs--; 160 161 if (WIFEXITED(status)) { 162 if (WEXITSTATUS(status) == 255) 163 exit(124); 164 if (WEXITSTATUS(status) == 127 || 165 WEXITSTATUS(status) == 126) 166 exit(WEXITSTATUS(status)); 167 if (WEXITSTATUS(status)) 168 nerrors++; 169 } 170 if (WIFSIGNALED(status)) 171 exit(125); 172 } 173 if (pid == -1 && errno != ECHILD) 174 eprintf("waitpid:"); 175 } 176 177 static int 178 prompt(void) 179 { 180 FILE *fp; 181 int ch, ret; 182 183 if (!(fp = fopen("/dev/tty", "r"))) 184 return -1; 185 186 fputs("?...", stderr); 187 fflush(stderr); 188 189 ch = fgetc(fp); 190 ret = (ch == 'y' || ch == 'Y'); 191 if (ch != EOF && ch != '\n') { 192 while ((ch = fgetc(fp)) != EOF) { 193 if (ch == '\n') 194 break; 195 } 196 } 197 198 fclose(fp); 199 200 return ret; 201 } 202 203 static void 204 spawn(void) 205 { 206 int savederrno; 207 int first = 1; 208 char **p; 209 210 if (pflag || tflag) { 211 for (p = cmd; *p; p++) { 212 if (!first) 213 fputc(' ', stderr); 214 fputs(*p, stderr); 215 first = 0; 216 } 217 if (pflag) { 218 switch (prompt()) { 219 case -1: break; /* error */ 220 case 0: return; /* no */ 221 case 1: goto dospawn; /* yes */ 222 } 223 } 224 fputc('\n', stderr); 225 fflush(stderr); 226 } 227 228 dospawn: 229 switch (fork()) { 230 case -1: 231 eprintf("fork:"); 232 case 0: 233 execvp(*cmd, cmd); 234 savederrno = errno; 235 weprintf("execvp %s:", *cmd); 236 _exit(126 + (savederrno == ENOENT)); 237 } 238 curprocs++; 239 waitchld(0); 240 } 241 242 static void 243 usage(void) 244 { 245 eprintf("usage: %s [-0prtx] [-E eofstr] [-n num] [-P maxprocs] [-s num] " 246 "[cmd [arg ...]]\n", argv0); 247 } 248 249 int 250 main(int argc, char *argv[]) 251 { 252 int ret = 0, leftover = 0, i, j; 253 size_t argsz, argmaxsz; 254 size_t arglen, a; 255 char *arg = ""; 256 char *replstr; 257 258 if ((argmaxsz = sysconf(_SC_ARG_MAX)) == (size_t)-1) 259 argmaxsz = _POSIX_ARG_MAX; 260 /* Leave some room for environment variables */ 261 argmaxsz -= 4096; 262 263 ARGBEGIN { 264 case '0': 265 nulflag = 1; 266 break; 267 case 'n': 268 nflag = 1; 269 maxargs = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX)); 270 break; 271 case 'p': 272 pflag = 1; 273 break; 274 case 'r': 275 rflag = 1; 276 break; 277 case 's': 278 argmaxsz = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX)); 279 break; 280 case 't': 281 tflag = 1; 282 break; 283 case 'x': 284 xflag = 1; 285 break; 286 case 'E': 287 eofstr = EARGF(usage()); 288 break; 289 case 'I': 290 Iflag = 1; 291 xflag = 1; 292 nflag = 1; 293 maxargs = 1; 294 replstr = EARGF(usage()); 295 break; 296 case 'P': 297 maxprocs = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX)); 298 break; 299 default: 300 usage(); 301 } ARGEND 302 303 do { 304 argsz = 0; i = 0; a = 0; 305 if (argc) { 306 for (; i < argc; i++) { 307 cmd[i] = estrdup(argv[i]); 308 argsz += strlen(cmd[i]) + 1; 309 } 310 } else { 311 cmd[i] = estrdup("/bin/echo"); 312 argsz += strlen("/bin/echo") + 1; 313 i++; 314 } 315 while (leftover || (arg = poparg())) { 316 arglen = strlen(arg); 317 if (argsz + arglen >= argmaxsz || i >= NARGS - 1) { 318 if (xflag || arglen >= argmaxsz || leftover) 319 eprintf("insufficient argument space\n"); 320 leftover = 1; 321 break; 322 } 323 324 if (!Iflag) { 325 cmd[i] = estrdup(arg); 326 argsz += arglen + 1; 327 } else { 328 for (j = 1; j < i; j++) { 329 char *p = cmd[j]; 330 argsz -= strlen(cmd[j]); 331 strnsubst(&cmd[j], replstr, arg, 255); 332 argsz += strlen(cmd[j]); 333 free(p); 334 } 335 } 336 337 i++; 338 a++; 339 leftover = 0; 340 if (nflag && a >= maxargs) 341 break; 342 } 343 cmd[i] = NULL; 344 if (a >= maxargs && nflag) 345 spawn(); 346 else if (!a || (i == 1 && rflag)) 347 ; 348 else 349 spawn(); 350 for (; i >= 0; i--) 351 free(cmd[i]); 352 } while (arg); 353 354 free(argb); 355 356 waitchld(1); 357 358 if (nerrors || (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))) 359 ret = 123; 360 361 return ret; 362 }