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 long arg_max; 83 int ch, Jflag, nargs, nflag, nline; 84 size_t linelen; 85 86 inpline = replstr = NULL; 87 ep = environ; 88 eofstr = ""; 89 Jflag = nflag = 0; 90 91 (void)setlocale(LC_MESSAGES, ""); 92 93 /* 94 * POSIX.2 limits the exec line length to ARG_MAX - 2K. Running that 95 * caused some E2BIG errors, so it was changed to ARG_MAX - 4K. Given 96 * that the smallest argument is 2 bytes in length, this means that 97 * the number of arguments is limited to: 98 * 99 * (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2. 100 * 101 * We arbitrarily limit the number of arguments to 5000. This is 102 * allowed by POSIX.2 as long as the resulting minimum exec line is 103 * at least LINE_MAX. Realloc'ing as necessary is possible, but 104 * probably not worthwhile. 105 */ 106 nargs = 5000; 107 if ((arg_max = sysconf(_SC_ARG_MAX)) == -1) 108 errx(1, "sysconf(_SC_ARG_MAX) failed"); 109 nline = arg_max - 4 * 1024; 110 while (*ep != NULL) { 111 /* 1 byte for each '\0' */ 112 nline -= strlen(*ep++) + 1 + sizeof(*ep); 113 } 114 while ((ch = getopt(argc, argv, "0E:I:J:L:n:opR:s:tx")) != -1) 115 switch(ch) { 116 case 'E': 117 eofstr = optarg; 118 break; 119 case 'I': 120 Jflag = 0; 121 Iflag = 1; 122 Lflag = 1; 123 replstr = optarg; 124 break; 125 case 'J': 126 Iflag = 0; 127 Jflag = 1; 128 replstr = optarg; 129 break; 130 case 'L': 131 Lflag = atoi(optarg); 132 break; 133 case 'n': 134 nflag = 1; 135 if ((nargs = atoi(optarg)) <= 0) 136 errx(1, "illegal argument count"); 137 break; 138 case 'o': 139 oflag = 1; 140 break; 141 case 'p': 142 pflag = 1; 143 break; 144 case 'R': 145 if ((Rflag = atoi(optarg)) <= 0) 146 errx(1, "illegal number of replacements"); 147 break; 148 case 's': 149 nline = atoi(optarg); 150 break; 151 case 't': 152 tflag = 1; 153 break; 154 case 'x': 155 xflag = 1; 156 break; 157 case '0': 158 zflag = 1; 159 break; 160 case '?': 161 default: 162 usage(); 163 } 164 argc -= optind; 165 argv += optind; 166 167 if (!Iflag && Rflag) 168 usage(); 169 if (Iflag && !Rflag) 170 Rflag = 5; 171 if (xflag && !nflag) 172 usage(); 173 if (Iflag || Lflag) 174 xflag = 1; 175 if (replstr != NULL && *replstr == '\0') 176 errx(1, "replstr may not be empty"); 177 178 /* 179 * Allocate pointers for the utility name, the utility arguments, 180 * the maximum arguments to be read from stdin and the trailing 181 * NULL. 182 */ 183 linelen = 1 + argc + nargs + 1; 184 if ((av = bxp = malloc(linelen * sizeof(char **))) == NULL) 185 errx(1, "malloc failed"); 186 187 /* 188 * Use the user's name for the utility as argv[0], just like the 189 * shell. Echo is the default. Set up pointers for the user's 190 * arguments. 191 */ 192 if (*argv == NULL) 193 cnt = strlen(*bxp++ = echo); 194 else { 195 do { 196 if (Jflag && strcmp(*argv, replstr) == 0) { 197 char **avj; 198 jfound = 1; 199 argv++; 200 for (avj = argv; *avj; avj++) 201 cnt += strlen(*avj) + 1; 202 break; 203 } 204 cnt += strlen(*bxp++ = *argv) + 1; 205 } while (*++argv != NULL); 206 } 207 208 /* 209 * Set up begin/end/traversing pointers into the array. The -n 210 * count doesn't include the trailing NULL pointer, so the malloc 211 * added in an extra slot. 212 */ 213 expx = (xp = bxp) + nargs; 214 215 /* 216 * Allocate buffer space for the arguments read from stdin and the 217 * trailing NULL. Buffer space is defined as the default or specified 218 * space, minus the length of the utility name and arguments. Set up 219 * begin/end/traversing pointers into the array. The -s count does 220 * include the trailing NULL, so the malloc didn't add in an extra 221 * slot. 222 */ 223 nline -= cnt; 224 if (nline <= 0) 225 errx(1, "insufficient space for command"); 226 227 if ((bbp = malloc((size_t)(nline + 1))) == NULL) 228 errx(1, "malloc failed"); 229 ebp = (argp = p = bbp) + nline - 1; 230 for (;;) 231 parse_input(argc, argv); 232 } 233 234 static void 235 parse_input(int argc, char *argv[]) 236 { 237 int ch, foundeof; 238 char **avj; 239 240 foundeof = 0; 241 242 switch(ch = getchar()) { 243 case EOF: 244 /* No arguments since last exec. */ 245 if (p == bbp) 246 exit(rval); 247 goto arg1; 248 case ' ': 249 case '\t': 250 /* Quotes escape tabs and spaces. */ 251 if (insingle || indouble || zflag) 252 goto addch; 253 goto arg2; 254 case '\0': 255 if (zflag) 256 goto arg2; 257 goto addch; 258 case '\n': 259 count++; 260 if (zflag) 261 goto addch; 262 263 /* Quotes do not escape newlines. */ 264 arg1: if (insingle || indouble) 265 errx(1, "unterminated quote"); 266 arg2: 267 foundeof = *eofstr != '\0' && 268 strcmp(argp, eofstr) == 0; 269 270 /* Do not make empty args unless they are quoted */ 271 if ((argp != p || wasquoted) && !foundeof) { 272 *p++ = '\0'; 273 *xp++ = argp; 274 if (Iflag) { 275 size_t curlen; 276 277 if (inpline == NULL) 278 curlen = 0; 279 else { 280 /* 281 * If this string is not zero 282 * length, append a space for 283 * separation before the next 284 * argument. 285 */ 286 if ((curlen = strlen(inpline))) 287 strcat(inpline, " "); 288 } 289 curlen++; 290 /* 291 * Allocate enough to hold what we will 292 * be holding in a second, and to append 293 * a space next time through, if we have 294 * to. 295 */ 296 inpline = realloc(inpline, curlen + 2 + 297 strlen(argp)); 298 if (inpline == NULL) 299 errx(1, "realloc failed"); 300 if (curlen == 1) 301 strcpy(inpline, argp); 302 else 303 strcat(inpline, argp); 304 } 305 } 306 307 /* 308 * If max'd out on args or buffer, or reached EOF, 309 * run the command. If xflag and max'd out on buffer 310 * but not on args, object. Having reached the limit 311 * of input lines, as specified by -L is the same as 312 * maxing out on arguments. 313 */ 314 if (xp == expx || p > ebp || ch == EOF || 315 (Lflag <= count && xflag) || foundeof) { 316 if (xflag && xp != expx && p > ebp) 317 errx(1, "insufficient space for arguments"); 318 if (jfound) { 319 for (avj = argv; *avj; avj++) 320 *xp++ = *avj; 321 } 322 prerun(argc, av); 323 if (ch == EOF || foundeof) 324 exit(rval); 325 p = bbp; 326 xp = bxp; 327 count = 0; 328 } 329 argp = p; 330 wasquoted = 0; 331 break; 332 case '\'': 333 if (indouble || zflag) 334 goto addch; 335 insingle = !insingle; 336 wasquoted = 1; 337 break; 338 case '"': 339 if (insingle || zflag) 340 goto addch; 341 indouble = !indouble; 342 wasquoted = 1; 343 break; 344 case '\\': 345 if (zflag) 346 goto addch; 347 /* Backslash escapes anything, is escaped by quotes. */ 348 if (!insingle && !indouble && (ch = getchar()) == EOF) 349 errx(1, "backslash at EOF"); 350 /* FALLTHROUGH */ 351 default: 352 addch: if (p < ebp) { 353 *p++ = ch; 354 break; 355 } 356 357 /* If only one argument, not enough buffer space. */ 358 if (bxp == xp) 359 errx(1, "insufficient space for argument"); 360 /* Didn't hit argument limit, so if xflag object. */ 361 if (xflag) 362 errx(1, "insufficient space for arguments"); 363 364 if (jfound) { 365 for (avj = argv; *avj; avj++) 366 *xp++ = *avj; 367 } 368 prerun(argc, av); 369 xp = bxp; 370 cnt = ebp - argp; 371 memcpy(bbp, argp, (size_t)cnt); 372 p = (argp = bbp) + cnt; 373 *p++ = ch; 374 break; 375 } 376 return; 377 } 378 379 /* 380 * Do things necessary before run()'ing, such as -I substitution, 381 * and then call run(). 382 */ 383 static void 384 prerun(int argc, char *argv[]) 385 { 386 char **tmp, **tmp2, **avj; 387 int repls; 388 389 repls = Rflag; 390 391 if (argc == 0 || repls == 0) { 392 *xp = NULL; 393 run(argv); 394 return; 395 } 396 397 avj = argv; 398 399 /* 400 * Allocate memory to hold the argument list, and 401 * a NULL at the tail. 402 */ 403 tmp = malloc((argc + 1) * sizeof(char**)); 404 if (tmp == NULL) 405 errx(1, "malloc failed"); 406 tmp2 = tmp; 407 408 /* 409 * Save the first argument and iterate over it, we 410 * cannot do strnsubst() to it. 411 */ 412 if ((*tmp++ = strdup(*avj++)) == NULL) 413 errx(1, "strdup failed"); 414 415 /* 416 * For each argument to utility, if we have not used up 417 * the number of replacements we are allowed to do, and 418 * if the argument contains at least one occurrence of 419 * replstr, call strnsubst(), else just save the string. 420 * Iterations over elements of avj and tmp are done 421 * where appropriate. 422 */ 423 while (--argc) { 424 *tmp = *avj++; 425 if (repls && strstr(*tmp, replstr) != NULL) { 426 strnsubst(tmp++, replstr, inpline, (size_t)255); 427 repls--; 428 } else { 429 if ((*tmp = strdup(*tmp)) == NULL) 430 errx(1, "strdup failed"); 431 tmp++; 432 } 433 } 434 435 /* 436 * Run it. 437 */ 438 *tmp = NULL; 439 run(tmp2); 440 441 /* 442 * Walk from the tail to the head, free along the way. 443 */ 444 for (; tmp2 != tmp; tmp--) 445 free(*tmp); 446 /* 447 * Now free the list itself. 448 */ 449 free(tmp2); 450 451 /* 452 * Free the input line buffer, if we have one. 453 */ 454 if (inpline != NULL) { 455 free(inpline); 456 inpline = NULL; 457 } 458 } 459 460 static void 461 run(char **argv) 462 { 463 volatile int childerr; 464 char **avec; 465 pid_t pid; 466 int status; 467 468 /* 469 * If the user wants to be notified of each command before it is 470 * executed, notify them. If they want the notification to be 471 * followed by a prompt, then prompt them. 472 */ 473 if (tflag || pflag) { 474 (void)fprintf(stderr, "%s", *argv); 475 for (avec = argv + 1; *avec != NULL; ++avec) 476 (void)fprintf(stderr, " %s", *avec); 477 /* 478 * If the user has asked to be prompted, do so. 479 */ 480 if (pflag) 481 /* 482 * If they asked not to exec, return without execution 483 * but if they asked to, go to the execution. If we 484 * could not open their tty, break the switch and drop 485 * back to -t behaviour. 486 */ 487 switch (prompt()) { 488 case 0: 489 return; 490 case 1: 491 goto exec; 492 case 2: 493 break; 494 } 495 (void)fprintf(stderr, "\n"); 496 (void)fflush(stderr); 497 } 498 exec: 499 childerr = 0; 500 switch(pid = vfork()) { 501 case -1: 502 err(1, "vfork"); 503 case 0: 504 close(0); 505 if (oflag) { 506 if (open("/dev/tty", O_RDONLY) == -1) 507 err(1, "open /dev/tty"); 508 } else { 509 if (open("/dev/null", O_RDONLY) == -1) 510 err(1, "open /dev/null"); 511 } 512 execvp(argv[0], argv); 513 childerr = errno; 514 _exit(1); 515 } 516 pid = waitpid(pid, &status, 0); 517 if (pid == -1) 518 err(1, "waitpid"); 519 /* If we couldn't invoke the utility, exit. */ 520 if (childerr != 0) 521 err(childerr == ENOENT ? 127 : 126, "%s", *argv); 522 /* If utility signaled or exited with a value of 255, exit 1-125. */ 523 if (WIFSIGNALED(status) || WEXITSTATUS(status) == 255) 524 exit(1); 525 if (WEXITSTATUS(status)) 526 rval = 1; 527 } 528 529 /* 530 * Prompt the user about running a command. 531 */ 532 static int 533 prompt(void) 534 { 535 regex_t cre; 536 size_t rsize; 537 int match; 538 char *response; 539 FILE *ttyfp; 540 541 if ((ttyfp = fopen(_PATH_TTY, "r")) == NULL) 542 return (2); /* Indicate that the TTY failed to open. */ 543 (void)fprintf(stderr, "?..."); 544 (void)fflush(stderr); 545 if ((response = fgetln(ttyfp, &rsize)) == NULL || 546 regcomp(&cre, 547 #ifdef BOOTSTRAPPING 548 "^[yY]", 549 #else 550 nl_langinfo(YESEXPR), 551 #endif 552 REG_BASIC) != 0) { 553 (void)fclose(ttyfp); 554 return (0); 555 } 556 match = regexec(&cre, response, 0, NULL, 0); 557 (void)fclose(ttyfp); 558 regfree(&cre); 559 return (match == 0); 560 } 561 562 static void 563 usage(void) 564 { 565 fprintf(stderr, 566 "usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements]] [-J replstr]\n" 567 " [-L number] [-n number [-x] [-s size] [utility [argument ...]]\n"); 568 exit(1); 569 } 570