1 /*- 2 * Copyright (c) 1990, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * John B. Roll Jr. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * $xMach: xargs.c,v 1.6 2002/02/23 05:27:47 tim Exp $ 37 * 38 * @(#) Copyright (c) 1990, 1993 The Regents of the University of California. All rights reserved. 39 * @(#)xargs.c 8.1 (Berkeley) 6/6/93 40 * $FreeBSD: src/usr.bin/xargs/xargs.c,v 1.9.2.6 2003/06/01 21:40:35 mux Exp $ 41 * $DragonFly: src/usr.bin/xargs/xargs.c,v 1.3 2004/07/27 21:42:48 dillon Exp $ 42 */ 43 44 #include <sys/types.h> 45 #include <sys/wait.h> 46 47 #include <err.h> 48 #include <errno.h> 49 #include <fcntl.h> 50 #ifndef BOOTSTRAPPING 51 #include <langinfo.h> 52 #endif 53 #include <locale.h> 54 #include <paths.h> 55 #include <regex.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include <unistd.h> 60 61 #include "pathnames.h" 62 63 static void parse_input(int, char *[]); 64 static void prerun(int, char *[]); 65 static int prompt(void); 66 static void run(char **); 67 static void usage(void); 68 void strnsubst(char **, const char *, const char *, size_t); 69 70 static char echo[] = _PATH_ECHO; 71 static char **av, **bxp, **ep, **expx, **xp; 72 static char *argp, *bbp, *ebp, *inpline, *p, *replstr; 73 static const char *eofstr; 74 static int count, insingle, indouble, oflag, pflag, tflag, Rflag, rval, zflag; 75 static int cnt, Iflag, jfound, Lflag, wasquoted, xflag; 76 77 extern char **environ; 78 79 int 80 main(int argc, char *argv[]) 81 { 82 char *tmp; 83 long arg_max; 84 int ch, Jflag, nargs, nflag, nline; 85 size_t linelen; 86 87 inpline = replstr = NULL; 88 ep = environ; 89 eofstr = ""; 90 Jflag = nflag = 0; 91 92 (void)setlocale(LC_MESSAGES, ""); 93 94 /* 95 * POSIX.2 limits the exec line length to ARG_MAX - 2K. Running that 96 * caused some E2BIG errors, so it was changed to ARG_MAX - 4K. Given 97 * that the smallest argument is 2 bytes in length, this means that 98 * the number of arguments is limited to: 99 * 100 * (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2. 101 * 102 * We arbitrarily limit the number of arguments to 5000. This is 103 * allowed by POSIX.2 as long as the resulting minimum exec line is 104 * at least LINE_MAX. Realloc'ing as necessary is possible, but 105 * probably not worthwhile. 106 */ 107 nargs = 5000; 108 if ((arg_max = sysconf(_SC_ARG_MAX)) == -1) 109 errx(1, "sysconf(_SC_ARG_MAX) failed"); 110 nline = arg_max - 4 * 1024; 111 while (*ep != NULL) { 112 /* 1 byte for each '\0' */ 113 nline -= strlen(*ep++) + 1 + sizeof(*ep); 114 } 115 while ((ch = getopt(argc, argv, "0E:I:J:L:n:opR:s:tx")) != -1) 116 switch(ch) { 117 case 'E': 118 eofstr = optarg; 119 break; 120 case 'I': 121 Jflag = 0; 122 Iflag = 1; 123 Lflag = 1; 124 replstr = optarg; 125 break; 126 case 'J': 127 Iflag = 0; 128 Jflag = 1; 129 replstr = optarg; 130 break; 131 case 'L': 132 Lflag = strtol(optarg, &tmp, 10); 133 if (*tmp != 0 || *optarg == 0) 134 errx(1, "illegal argument count"); 135 break; 136 case 'n': 137 nflag = 1; 138 if ((nargs = atoi(optarg)) <= 0) 139 errx(1, "illegal argument count"); 140 break; 141 case 'o': 142 oflag = 1; 143 break; 144 case 'p': 145 pflag = 1; 146 break; 147 case 'R': 148 if ((Rflag = atoi(optarg)) <= 0) 149 errx(1, "illegal number of replacements"); 150 break; 151 case 's': 152 nline = atoi(optarg); 153 break; 154 case 't': 155 tflag = 1; 156 break; 157 case 'x': 158 xflag = 1; 159 break; 160 case '0': 161 zflag = 1; 162 break; 163 case '?': 164 default: 165 usage(); 166 } 167 argc -= optind; 168 argv += optind; 169 170 if (!Iflag && Rflag) 171 usage(); 172 if (Iflag && !Rflag) 173 Rflag = 5; 174 if (xflag && !nflag) 175 usage(); 176 if (Iflag || Lflag) 177 xflag = 1; 178 if (replstr != NULL && *replstr == '\0') 179 errx(1, "replstr may not be empty"); 180 181 /* 182 * Allocate pointers for the utility name, the utility arguments, 183 * the maximum arguments to be read from stdin and the trailing 184 * NULL. 185 */ 186 linelen = 1 + argc + nargs + 1; 187 if ((av = bxp = malloc(linelen * sizeof(char **))) == NULL) 188 errx(1, "malloc failed"); 189 190 /* 191 * Use the user's name for the utility as argv[0], just like the 192 * shell. Echo is the default. Set up pointers for the user's 193 * arguments. 194 */ 195 if (*argv == NULL) 196 cnt = strlen(*bxp++ = echo); 197 else { 198 do { 199 if (Jflag && strcmp(*argv, replstr) == 0) { 200 char **avj; 201 jfound = 1; 202 argv++; 203 for (avj = argv; *avj; avj++) 204 cnt += strlen(*avj) + 1; 205 break; 206 } 207 cnt += strlen(*bxp++ = *argv) + 1; 208 } while (*++argv != NULL); 209 } 210 211 /* 212 * Set up begin/end/traversing pointers into the array. The -n 213 * count doesn't include the trailing NULL pointer, so the malloc 214 * added in an extra slot. 215 */ 216 expx = (xp = bxp) + nargs; 217 218 /* 219 * Allocate buffer space for the arguments read from stdin and the 220 * trailing NULL. Buffer space is defined as the default or specified 221 * space, minus the length of the utility name and arguments. Set up 222 * begin/end/traversing pointers into the array. The -s count does 223 * include the trailing NULL, so the malloc didn't add in an extra 224 * slot. 225 */ 226 nline -= cnt; 227 if (nline <= 0) 228 errx(1, "insufficient space for command"); 229 230 if ((bbp = malloc((size_t)(nline + 1))) == NULL) 231 errx(1, "malloc failed"); 232 ebp = (argp = p = bbp) + nline - 1; 233 for (;;) 234 parse_input(argc, argv); 235 } 236 237 static void 238 parse_input(int argc, char *argv[]) 239 { 240 int ch, foundeof; 241 char **avj; 242 243 foundeof = 0; 244 245 switch(ch = getchar()) { 246 case EOF: 247 /* No arguments since last exec. */ 248 if (p == bbp) 249 exit(rval); 250 goto arg1; 251 case ' ': 252 case '\t': 253 /* Quotes escape tabs and spaces. */ 254 if (insingle || indouble || zflag) 255 goto addch; 256 goto arg2; 257 case '\0': 258 if (zflag) 259 goto arg2; 260 goto addch; 261 case '\n': 262 count++; 263 if (zflag) 264 goto addch; 265 266 /* Quotes do not escape newlines. */ 267 arg1: if (insingle || indouble) 268 errx(1, "unterminated quote"); 269 arg2: 270 foundeof = *eofstr != '\0' && 271 strcmp(argp, eofstr) == 0; 272 273 /* Do not make empty args unless they are quoted */ 274 if ((argp != p || wasquoted) && !foundeof) { 275 *p++ = '\0'; 276 *xp++ = argp; 277 if (Iflag) { 278 size_t curlen; 279 280 if (inpline == NULL) 281 curlen = 0; 282 else { 283 /* 284 * If this string is not zero 285 * length, append a space for 286 * separation before the next 287 * argument. 288 */ 289 if ((curlen = strlen(inpline))) 290 strcat(inpline, " "); 291 } 292 curlen++; 293 /* 294 * Allocate enough to hold what we will 295 * be holding in a second, and to append 296 * a space next time through, if we have 297 * to. 298 */ 299 inpline = realloc(inpline, curlen + 2 + 300 strlen(argp)); 301 if (inpline == NULL) 302 errx(1, "realloc failed"); 303 if (curlen == 1) 304 strcpy(inpline, argp); 305 else 306 strcat(inpline, argp); 307 } 308 } 309 310 /* 311 * If max'd out on args or buffer, or reached EOF, 312 * run the command. If xflag and max'd out on buffer 313 * but not on args, object. Having reached the limit 314 * of input lines, as specified by -L is the same as 315 * maxing out on arguments. 316 */ 317 if (xp == expx || p > ebp || ch == EOF || 318 (Lflag <= count && xflag) || foundeof) { 319 if (xflag && xp != expx && p > ebp) 320 errx(1, "insufficient space for arguments"); 321 if (jfound) { 322 for (avj = argv; *avj; avj++) 323 *xp++ = *avj; 324 } 325 prerun(argc, av); 326 if (ch == EOF || foundeof) 327 exit(rval); 328 p = bbp; 329 xp = bxp; 330 count = 0; 331 } 332 argp = p; 333 wasquoted = 0; 334 break; 335 case '\'': 336 if (indouble || zflag) 337 goto addch; 338 insingle = !insingle; 339 wasquoted = 1; 340 break; 341 case '"': 342 if (insingle || zflag) 343 goto addch; 344 indouble = !indouble; 345 wasquoted = 1; 346 break; 347 case '\\': 348 if (zflag) 349 goto addch; 350 /* Backslash escapes anything, is escaped by quotes. */ 351 if (!insingle && !indouble && (ch = getchar()) == EOF) 352 errx(1, "backslash at EOF"); 353 /* FALLTHROUGH */ 354 default: 355 addch: if (p < ebp) { 356 *p++ = ch; 357 break; 358 } 359 360 /* If only one argument, not enough buffer space. */ 361 if (bxp == xp) 362 errx(1, "insufficient space for argument"); 363 /* Didn't hit argument limit, so if xflag object. */ 364 if (xflag) 365 errx(1, "insufficient space for arguments"); 366 367 if (jfound) { 368 for (avj = argv; *avj; avj++) 369 *xp++ = *avj; 370 } 371 prerun(argc, av); 372 xp = bxp; 373 cnt = ebp - argp; 374 memcpy(bbp, argp, (size_t)cnt); 375 p = (argp = bbp) + cnt; 376 *p++ = ch; 377 break; 378 } 379 return; 380 } 381 382 /* 383 * Do things necessary before run()'ing, such as -I substitution, 384 * and then call run(). 385 */ 386 static void 387 prerun(int argc, char *argv[]) 388 { 389 char **tmp, **tmp2, **avj; 390 int repls; 391 392 repls = Rflag; 393 394 if (argc == 0 || repls == 0) { 395 *xp = NULL; 396 run(argv); 397 return; 398 } 399 400 avj = argv; 401 402 /* 403 * Allocate memory to hold the argument list, and 404 * a NULL at the tail. 405 */ 406 tmp = malloc((argc + 1) * sizeof(char**)); 407 if (tmp == NULL) 408 errx(1, "malloc failed"); 409 tmp2 = tmp; 410 411 /* 412 * Save the first argument and iterate over it, we 413 * cannot do strnsubst() to it. 414 */ 415 if ((*tmp++ = strdup(*avj++)) == NULL) 416 errx(1, "strdup failed"); 417 418 /* 419 * For each argument to utility, if we have not used up 420 * the number of replacements we are allowed to do, and 421 * if the argument contains at least one occurrence of 422 * replstr, call strnsubst(), else just save the string. 423 * Iterations over elements of avj and tmp are done 424 * where appropriate. 425 */ 426 while (--argc) { 427 *tmp = *avj++; 428 if (repls && strstr(*tmp, replstr) != NULL) { 429 strnsubst(tmp++, replstr, inpline, (size_t)255); 430 repls--; 431 } else { 432 if ((*tmp = strdup(*tmp)) == NULL) 433 errx(1, "strdup failed"); 434 tmp++; 435 } 436 } 437 438 /* 439 * Run it. 440 */ 441 *tmp = NULL; 442 run(tmp2); 443 444 /* 445 * Walk from the tail to the head, free along the way. 446 */ 447 for (; tmp2 != tmp; tmp--) 448 free(*tmp); 449 /* 450 * Now free the list itself. 451 */ 452 free(tmp2); 453 454 /* 455 * Free the input line buffer, if we have one. 456 */ 457 if (inpline != NULL) { 458 free(inpline); 459 inpline = NULL; 460 } 461 } 462 463 static void 464 run(char **argv) 465 { 466 volatile int childerr; 467 char **avec; 468 pid_t pid; 469 int status; 470 471 /* 472 * If the user wants to be notified of each command before it is 473 * executed, notify them. If they want the notification to be 474 * followed by a prompt, then prompt them. 475 */ 476 if (tflag || pflag) { 477 (void)fprintf(stderr, "%s", *argv); 478 for (avec = argv + 1; *avec != NULL; ++avec) 479 (void)fprintf(stderr, " %s", *avec); 480 /* 481 * If the user has asked to be prompted, do so. 482 */ 483 if (pflag) 484 /* 485 * If they asked not to exec, return without execution 486 * but if they asked to, go to the execution. If we 487 * could not open their tty, break the switch and drop 488 * back to -t behaviour. 489 */ 490 switch (prompt()) { 491 case 0: 492 return; 493 case 1: 494 goto exec; 495 case 2: 496 break; 497 } 498 (void)fprintf(stderr, "\n"); 499 (void)fflush(stderr); 500 } 501 exec: 502 childerr = 0; 503 switch(pid = vfork()) { 504 case -1: 505 err(1, "vfork"); 506 case 0: 507 close(0); 508 if (oflag) { 509 if (open("/dev/tty", O_RDONLY) == -1) 510 err(1, "open /dev/tty"); 511 } else { 512 if (open("/dev/null", O_RDONLY) == -1) 513 err(1, "open /dev/null"); 514 } 515 execvp(argv[0], argv); 516 childerr = errno; 517 _exit(1); 518 } 519 pid = waitpid(pid, &status, 0); 520 if (pid == -1) 521 err(1, "waitpid"); 522 /* If we couldn't invoke the utility, exit. */ 523 if (childerr != 0) 524 err(childerr == ENOENT ? 127 : 126, "%s", *argv); 525 /* If utility signaled or exited with a value of 255, exit 1-125. */ 526 if (WIFSIGNALED(status) || WEXITSTATUS(status) == 255) 527 exit(1); 528 if (WEXITSTATUS(status)) 529 rval = 1; 530 } 531 532 /* 533 * Prompt the user about running a command. 534 */ 535 static int 536 prompt(void) 537 { 538 regex_t cre; 539 size_t rsize; 540 int match; 541 char *response; 542 FILE *ttyfp; 543 544 if ((ttyfp = fopen(_PATH_TTY, "r")) == NULL) 545 return (2); /* Indicate that the TTY failed to open. */ 546 (void)fprintf(stderr, "?..."); 547 (void)fflush(stderr); 548 if ((response = fgetln(ttyfp, &rsize)) == NULL || 549 regcomp(&cre, 550 #ifdef BOOTSTRAPPING 551 "^[yY]", 552 #else 553 nl_langinfo(YESEXPR), 554 #endif 555 REG_BASIC) != 0) { 556 (void)fclose(ttyfp); 557 return (0); 558 } 559 match = regexec(&cre, response, 0, NULL, 0); 560 (void)fclose(ttyfp); 561 regfree(&cre); 562 return (match == 0); 563 } 564 565 static void 566 usage(void) 567 { 568 fprintf(stderr, 569 "usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements]] [-J replstr]\n" 570 " [-L number] [-n number [-x] [-s size] [utility [argument ...]]\n"); 571 exit(1); 572 } 573