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