11590Srgrimes/*- 21590Srgrimes * Copyright (c) 1990, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * This code is derived from software contributed to Berkeley by 61590Srgrimes * John B. Roll Jr. 71590Srgrimes * 81590Srgrimes * Redistribution and use in source and binary forms, with or without 91590Srgrimes * modification, are permitted provided that the following conditions 101590Srgrimes * are met: 111590Srgrimes * 1. Redistributions of source code must retain the above copyright 121590Srgrimes * notice, this list of conditions and the following disclaimer. 131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141590Srgrimes * notice, this list of conditions and the following disclaimer in the 151590Srgrimes * documentation and/or other materials provided with the distribution. 161590Srgrimes * 4. Neither the name of the University nor the names of its contributors 171590Srgrimes * may be used to endorse or promote products derived from this software 181590Srgrimes * without specific prior written permission. 191590Srgrimes * 201590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301590Srgrimes * SUCH DAMAGE. 3195080Sjmallett * 3295080Sjmallett * $xMach: xargs.c,v 1.6 2002/02/23 05:27:47 tim Exp $ 331590Srgrimes */ 341590Srgrimes 35114591Sobrien#if 0 3695983Sjmallett#ifndef lint 3795983Sjmallettstatic const char copyright[] = 3895983Sjmallett"@(#) Copyright (c) 1990, 1993\n\ 3995983Sjmallett The Regents of the University of California. All rights reserved.\n"; 4095983Sjmallett#endif /* not lint */ 4195983Sjmallett 4295983Sjmallett#ifndef lint 4395983Sjmallettstatic char sccsid[] = "@(#)xargs.c 8.1 (Berkeley) 6/6/93"; 4495983Sjmallett#endif /* not lint */ 4595983Sjmallett#endif 4691664Smike#include <sys/cdefs.h> 4791664Smike__FBSDID("$FreeBSD$"); 4891664Smike 49293726Sallanjude#include <sys/types.h> 501590Srgrimes#include <sys/wait.h> 51293726Sallanjude#include <sys/time.h> 52293726Sallanjude#include <sys/limits.h> 53293726Sallanjude#include <sys/resource.h> 5428826Scharnier#include <err.h> 5591969Sdes#include <errno.h> 56112514Smux#include <fcntl.h> 5796057Sjmallett#include <langinfo.h> 5896057Sjmallett#include <locale.h> 5996057Sjmallett#include <paths.h> 6096057Sjmallett#include <regex.h> 611590Srgrimes#include <stdio.h> 621590Srgrimes#include <stdlib.h> 631590Srgrimes#include <string.h> 641590Srgrimes#include <unistd.h> 6587684Smarkm 661590Srgrimes#include "pathnames.h" 671590Srgrimes 6898617Sjmallettstatic void parse_input(int, char *[]); 6998617Sjmallettstatic void prerun(int, char *[]); 7096057Sjmallettstatic int prompt(void); 7195080Sjmallettstatic void run(char **); 7292377Sjmallettstatic void usage(void); 7395080Sjmallettvoid strnsubst(char **, const char *, const char *, size_t); 74215615Smckaystatic pid_t xwait(int block, int *status); 75233038Sjillesstatic void xexit(const char *, const int); 76108156Stjrstatic void waitchildren(const char *, int); 77215615Smckaystatic void pids_init(void); 78215615Smckaystatic int pids_empty(void); 79215615Smckaystatic int pids_full(void); 80215615Smckaystatic void pids_add(pid_t pid); 81215615Smckaystatic int pids_remove(pid_t pid); 82215615Smckaystatic int findslot(pid_t pid); 83215615Smckaystatic int findfreeslot(void); 84215615Smckaystatic void clearslot(int slot); 851590Srgrimes 8687684Smarkmstatic char echo[] = _PATH_ECHO; 87114591Sobrienstatic char **av, **bxp, **ep, **endxp, **xp; 8896050Sjmallettstatic char *argp, *bbp, *ebp, *inpline, *p, *replstr; 8996050Sjmallettstatic const char *eofstr; 90112514Smuxstatic int count, insingle, indouble, oflag, pflag, tflag, Rflag, rval, zflag; 91153918Sjmallettstatic int cnt, Iflag, jfound, Lflag, Sflag, wasquoted, xflag; 92108156Stjrstatic int curprocs, maxprocs; 93215615Smckaystatic pid_t *childpids; 9487684Smarkm 95108156Stjrstatic volatile int childerr; 96108156Stjr 9799199Stjrextern char **environ; 9892377Sjmallett 9928826Scharnierint 10098617Sjmallettmain(int argc, char *argv[]) 1011590Srgrimes{ 10237029Sjkoshy long arg_max; 10396050Sjmallett int ch, Jflag, nargs, nflag, nline; 10495080Sjmallett size_t linelen; 105293726Sallanjude struct rlimit rl; 106111581Sjmallett char *endptr; 107293726Sallanjude const char *errstr; 1081590Srgrimes 10996050Sjmallett inpline = replstr = NULL; 11092377Sjmallett ep = environ; 11195080Sjmallett eofstr = ""; 11296050Sjmallett Jflag = nflag = 0; 11376605Sdd 114132008Stjr (void)setlocale(LC_ALL, ""); 11596057Sjmallett 1161590Srgrimes /* 1171590Srgrimes * POSIX.2 limits the exec line length to ARG_MAX - 2K. Running that 1181590Srgrimes * caused some E2BIG errors, so it was changed to ARG_MAX - 4K. Given 1191590Srgrimes * that the smallest argument is 2 bytes in length, this means that 1201590Srgrimes * the number of arguments is limited to: 1211590Srgrimes * 1221590Srgrimes * (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2. 1231590Srgrimes * 1241590Srgrimes * We arbitrarily limit the number of arguments to 5000. This is 1251590Srgrimes * allowed by POSIX.2 as long as the resulting minimum exec line is 1261590Srgrimes * at least LINE_MAX. Realloc'ing as necessary is possible, but 1271590Srgrimes * probably not worthwhile. 1281590Srgrimes */ 1291590Srgrimes nargs = 5000; 13037029Sjkoshy if ((arg_max = sysconf(_SC_ARG_MAX)) == -1) 13137029Sjkoshy errx(1, "sysconf(_SC_ARG_MAX) failed"); 13237029Sjkoshy nline = arg_max - 4 * 1024; 13396050Sjmallett while (*ep != NULL) { 13414387Sasami /* 1 byte for each '\0' */ 13514387Sasami nline -= strlen(*ep++) + 1 + sizeof(*ep); 13614387Sasami } 137108156Stjr maxprocs = 1; 138153918Sjmallett while ((ch = getopt(argc, argv, "0E:I:J:L:n:oP:pR:S:s:rtx")) != -1) 139153959Sjmallett switch (ch) { 14095080Sjmallett case 'E': 14195080Sjmallett eofstr = optarg; 14295080Sjmallett break; 14395080Sjmallett case 'I': 14497620Sjmallett Jflag = 0; 14595080Sjmallett Iflag = 1; 14695985Sjmallett Lflag = 1; 14795080Sjmallett replstr = optarg; 14895080Sjmallett break; 14976605Sdd case 'J': 15097620Sjmallett Iflag = 0; 15195905Sjmallett Jflag = 1; 15276605Sdd replstr = optarg; 15376605Sdd break; 15495080Sjmallett case 'L': 155293726Sallanjude Lflag = strtonum(optarg, 0, INT_MAX, &errstr); 156293726Sallanjude if (errstr) 157293726Sallanjude errx(1, "-L %s: %s", optarg, errstr); 15895080Sjmallett break; 1591590Srgrimes case 'n': 1601590Srgrimes nflag = 1; 161293726Sallanjude nargs = strtonum(optarg, 1, INT_MAX, &errstr); 162293726Sallanjude if (errstr) 163293726Sallanjude errx(1, "-n %s: %s", optarg, errstr); 1641590Srgrimes break; 165112514Smux case 'o': 166112514Smux oflag = 1; 167112514Smux break; 168108156Stjr case 'P': 169293726Sallanjude maxprocs = strtonum(optarg, 0, INT_MAX, &errstr); 170293726Sallanjude if (errstr) 171293726Sallanjude errx(1, "-P %s: %s", optarg, errstr); 172293726Sallanjude if (getrlimit(RLIMIT_NPROC, &rl) != 0) 173293726Sallanjude errx(1, "getrlimit failed"); 174293726Sallanjude if (maxprocs == 0 || maxprocs > rl.rlim_cur) 175293726Sallanjude maxprocs = rl.rlim_cur; 176108156Stjr break; 17795080Sjmallett case 'p': 17895080Sjmallett pflag = 1; 17995080Sjmallett break; 18095905Sjmallett case 'R': 181111581Sjmallett Rflag = strtol(optarg, &endptr, 10); 182111581Sjmallett if (*endptr != '\0') 183111581Sjmallett errx(1, "replacements must be a number"); 18495905Sjmallett break; 185153196Sdes case 'r': 186153196Sdes /* GNU compatibility */ 187153196Sdes break; 188153918Sjmallett case 'S': 189153918Sjmallett Sflag = strtoul(optarg, &endptr, 10); 190153918Sjmallett if (*endptr != '\0') 191153918Sjmallett errx(1, "replsize must be a number"); 192153918Sjmallett break; 1931590Srgrimes case 's': 194293726Sallanjude nline = strtonum(optarg, 0, INT_MAX, &errstr); 195293726Sallanjude if (errstr) 196293726Sallanjude errx(1, "-s %s: %s", optarg, errstr); 1971590Srgrimes break; 1981590Srgrimes case 't': 1991590Srgrimes tflag = 1; 2001590Srgrimes break; 2011590Srgrimes case 'x': 2021590Srgrimes xflag = 1; 2031590Srgrimes break; 20419318Simp case '0': 20519318Simp zflag = 1; 20619318Simp break; 2071590Srgrimes case '?': 2081590Srgrimes default: 2091590Srgrimes usage(); 2101590Srgrimes } 2111590Srgrimes argc -= optind; 2121590Srgrimes argv += optind; 2131590Srgrimes 21497620Sjmallett if (!Iflag && Rflag) 21597620Sjmallett usage(); 216153918Sjmallett if (!Iflag && Sflag) 217153918Sjmallett usage(); 21897620Sjmallett if (Iflag && !Rflag) 21997620Sjmallett Rflag = 5; 220153918Sjmallett if (Iflag && !Sflag) 221153918Sjmallett Sflag = 255; 2221590Srgrimes if (xflag && !nflag) 2231590Srgrimes usage(); 22495985Sjmallett if (Iflag || Lflag) 22595080Sjmallett xflag = 1; 22695080Sjmallett if (replstr != NULL && *replstr == '\0') 22795080Sjmallett errx(1, "replstr may not be empty"); 2281590Srgrimes 229215615Smckay pids_init(); 230215615Smckay 2311590Srgrimes /* 2321590Srgrimes * Allocate pointers for the utility name, the utility arguments, 2331590Srgrimes * the maximum arguments to be read from stdin and the trailing 2341590Srgrimes * NULL. 2351590Srgrimes */ 23695080Sjmallett linelen = 1 + argc + nargs + 1; 23795898Sjmallett if ((av = bxp = malloc(linelen * sizeof(char **))) == NULL) 23896050Sjmallett errx(1, "malloc failed"); 2391590Srgrimes 2401590Srgrimes /* 2411590Srgrimes * Use the user's name for the utility as argv[0], just like the 2421590Srgrimes * shell. Echo is the default. Set up pointers for the user's 2431590Srgrimes * arguments. 2441590Srgrimes */ 24596013Sjmallett if (*argv == NULL) 24696014Sjmallett cnt = strlen(*bxp++ = echo); 2471590Srgrimes else { 2481590Srgrimes do { 24995905Sjmallett if (Jflag && strcmp(*argv, replstr) == 0) { 25096050Sjmallett char **avj; 25176605Sdd jfound = 1; 25276605Sdd argv++; 25376605Sdd for (avj = argv; *avj; avj++) 25476605Sdd cnt += strlen(*avj) + 1; 25576605Sdd break; 25676605Sdd } 2571590Srgrimes cnt += strlen(*bxp++ = *argv) + 1; 25896013Sjmallett } while (*++argv != NULL); 2591590Srgrimes } 2601590Srgrimes 2611590Srgrimes /* 2621590Srgrimes * Set up begin/end/traversing pointers into the array. The -n 2631590Srgrimes * count doesn't include the trailing NULL pointer, so the malloc 2641590Srgrimes * added in an extra slot. 2651590Srgrimes */ 266114591Sobrien endxp = (xp = bxp) + nargs; 2671590Srgrimes 2681590Srgrimes /* 2691590Srgrimes * Allocate buffer space for the arguments read from stdin and the 2701590Srgrimes * trailing NULL. Buffer space is defined as the default or specified 2711590Srgrimes * space, minus the length of the utility name and arguments. Set up 2721590Srgrimes * begin/end/traversing pointers into the array. The -s count does 2731590Srgrimes * include the trailing NULL, so the malloc didn't add in an extra 2741590Srgrimes * slot. 2751590Srgrimes */ 2761590Srgrimes nline -= cnt; 2771590Srgrimes if (nline <= 0) 27828826Scharnier errx(1, "insufficient space for command"); 2791590Srgrimes 28096014Sjmallett if ((bbp = malloc((size_t)(nline + 1))) == NULL) 28196050Sjmallett errx(1, "malloc failed"); 2821590Srgrimes ebp = (argp = p = bbp) + nline - 1; 28396050Sjmallett for (;;) 28496050Sjmallett parse_input(argc, argv); 28596050Sjmallett} 2861590Srgrimes 28796050Sjmallettstatic void 28898617Sjmallettparse_input(int argc, char *argv[]) 28996050Sjmallett{ 29096050Sjmallett int ch, foundeof; 29196050Sjmallett char **avj; 29296050Sjmallett 29396050Sjmallett foundeof = 0; 29496050Sjmallett 295153959Sjmallett switch (ch = getchar()) { 29696050Sjmallett case EOF: 29796050Sjmallett /* No arguments since last exec. */ 298233038Sjilles if (p == bbp) 299233038Sjilles xexit(*av, rval); 30096050Sjmallett goto arg1; 30196050Sjmallett case ' ': 30296050Sjmallett case '\t': 30396050Sjmallett /* Quotes escape tabs and spaces. */ 30496050Sjmallett if (insingle || indouble || zflag) 30596050Sjmallett goto addch; 30696050Sjmallett goto arg2; 30796050Sjmallett case '\0': 308142604Sgad if (zflag) { 309142604Sgad /* 310142604Sgad * Increment 'count', so that nulls will be treated 311142604Sgad * as end-of-line, as well as end-of-argument. This 312142604Sgad * is needed so -0 works properly with -I and -L. 313142604Sgad */ 314142604Sgad count++; 3151590Srgrimes goto arg2; 316142604Sgad } 31796050Sjmallett goto addch; 31896050Sjmallett case '\n': 31996050Sjmallett if (zflag) 32019318Simp goto addch; 321142604Sgad count++; /* Indicate end-of-line (used by -L) */ 32219318Simp 32396050Sjmallett /* Quotes do not escape newlines. */ 324233038Sjillesarg1: if (insingle || indouble) { 325233038Sjilles warnx("unterminated quote"); 326233038Sjilles xexit(*av, 1); 327233038Sjilles } 32847429Sjmzarg2: 32996050Sjmallett foundeof = *eofstr != '\0' && 330153934Sjmallett strncmp(argp, eofstr, p - argp) == 0; 33195080Sjmallett 33296050Sjmallett /* Do not make empty args unless they are quoted */ 33396050Sjmallett if ((argp != p || wasquoted) && !foundeof) { 33496050Sjmallett *p++ = '\0'; 33596050Sjmallett *xp++ = argp; 33696050Sjmallett if (Iflag) { 33796050Sjmallett size_t curlen; 33895987Sjmallett 33996050Sjmallett if (inpline == NULL) 34096050Sjmallett curlen = 0; 34196050Sjmallett else { 34295986Sjmallett /* 34396050Sjmallett * If this string is not zero 34496050Sjmallett * length, append a space for 345101677Sschweikh * separation before the next 34696050Sjmallett * argument. 34795986Sjmallett */ 34896050Sjmallett if ((curlen = strlen(inpline))) 34996050Sjmallett strcat(inpline, " "); 35095080Sjmallett } 35196050Sjmallett curlen++; 35296050Sjmallett /* 35396050Sjmallett * Allocate enough to hold what we will 35496055Sjmallett * be holding in a second, and to append 35596050Sjmallett * a space next time through, if we have 35696050Sjmallett * to. 35796050Sjmallett */ 35896050Sjmallett inpline = realloc(inpline, curlen + 2 + 35996050Sjmallett strlen(argp)); 360233038Sjilles if (inpline == NULL) { 361233038Sjilles warnx("realloc failed"); 362233038Sjilles xexit(*av, 1); 363233038Sjilles } 36496050Sjmallett if (curlen == 1) 36596050Sjmallett strcpy(inpline, argp); 36696050Sjmallett else 36796050Sjmallett strcat(inpline, argp); 36847429Sjmz } 36996050Sjmallett } 3701590Srgrimes 37196050Sjmallett /* 37296050Sjmallett * If max'd out on args or buffer, or reached EOF, 37396050Sjmallett * run the command. If xflag and max'd out on buffer 37496050Sjmallett * but not on args, object. Having reached the limit 37596050Sjmallett * of input lines, as specified by -L is the same as 37696050Sjmallett * maxing out on arguments. 37796050Sjmallett */ 378114591Sobrien if (xp == endxp || p > ebp || ch == EOF || 37996055Sjmallett (Lflag <= count && xflag) || foundeof) { 380233038Sjilles if (xflag && xp != endxp && p > ebp) { 381233038Sjilles warnx("insufficient space for arguments"); 382233038Sjilles xexit(*av, 1); 383233038Sjilles } 38476605Sdd if (jfound) { 38576605Sdd for (avj = argv; *avj; avj++) 38676605Sdd *xp++ = *avj; 38776605Sdd } 38896050Sjmallett prerun(argc, av); 389233038Sjilles if (ch == EOF || foundeof) 390233038Sjilles xexit(*av, rval); 39196050Sjmallett p = bbp; 3921590Srgrimes xp = bxp; 39396050Sjmallett count = 0; 39496050Sjmallett } 39596050Sjmallett argp = p; 39696050Sjmallett wasquoted = 0; 39796050Sjmallett break; 39896050Sjmallett case '\'': 39996050Sjmallett if (indouble || zflag) 40096050Sjmallett goto addch; 40196050Sjmallett insingle = !insingle; 40296050Sjmallett wasquoted = 1; 40396050Sjmallett break; 40496050Sjmallett case '"': 40596050Sjmallett if (insingle || zflag) 40696050Sjmallett goto addch; 40796050Sjmallett indouble = !indouble; 40896050Sjmallett wasquoted = 1; 40996050Sjmallett break; 41096050Sjmallett case '\\': 41196050Sjmallett if (zflag) 41296050Sjmallett goto addch; 41396050Sjmallett /* Backslash escapes anything, is escaped by quotes. */ 414233038Sjilles if (!insingle && !indouble && (ch = getchar()) == EOF) { 415233038Sjilles warnx("backslash at EOF"); 416233038Sjilles xexit(*av, 1); 417233038Sjilles } 41896050Sjmallett /* FALLTHROUGH */ 41996050Sjmallett default: 42096050Sjmallettaddch: if (p < ebp) { 4211590Srgrimes *p++ = ch; 4221590Srgrimes break; 4231590Srgrimes } 42496050Sjmallett 42596050Sjmallett /* If only one argument, not enough buffer space. */ 426233038Sjilles if (bxp == xp) { 427233038Sjilles warnx("insufficient space for argument"); 428233038Sjilles xexit(*av, 1); 429233038Sjilles } 43096050Sjmallett /* Didn't hit argument limit, so if xflag object. */ 431233038Sjilles if (xflag) { 432233038Sjilles warnx("insufficient space for arguments"); 433233038Sjilles xexit(*av, 1); 434233038Sjilles } 43596050Sjmallett 43696050Sjmallett if (jfound) { 43796050Sjmallett for (avj = argv; *avj; avj++) 43896050Sjmallett *xp++ = *avj; 43996050Sjmallett } 44096050Sjmallett prerun(argc, av); 44196050Sjmallett xp = bxp; 44296050Sjmallett cnt = ebp - argp; 44396050Sjmallett memcpy(bbp, argp, (size_t)cnt); 44496050Sjmallett p = (argp = bbp) + cnt; 44596050Sjmallett *p++ = ch; 44696050Sjmallett break; 44796050Sjmallett } 4481590Srgrimes} 4491590Srgrimes 45095990Sjmallett/* 45195990Sjmallett * Do things necessary before run()'ing, such as -I substitution, 45295990Sjmallett * and then call run(). 45395990Sjmallett */ 45495080Sjmallettstatic void 45598617Sjmallettprerun(int argc, char *argv[]) 45695990Sjmallett{ 45795990Sjmallett char **tmp, **tmp2, **avj; 45896050Sjmallett int repls; 45995990Sjmallett 46096050Sjmallett repls = Rflag; 46196050Sjmallett 46296797Sjmallett if (argc == 0 || repls == 0) { 46395990Sjmallett *xp = NULL; 46495990Sjmallett run(argv); 46595990Sjmallett return; 46695990Sjmallett } 46795990Sjmallett 46895990Sjmallett avj = argv; 46995990Sjmallett 47095990Sjmallett /* 47195990Sjmallett * Allocate memory to hold the argument list, and 47295990Sjmallett * a NULL at the tail. 47395990Sjmallett */ 47496006Sjmallett tmp = malloc((argc + 1) * sizeof(char**)); 475233038Sjilles if (tmp == NULL) { 476233038Sjilles warnx("malloc failed"); 477233038Sjilles xexit(*argv, 1); 478233038Sjilles } 47995990Sjmallett tmp2 = tmp; 48095990Sjmallett 48195990Sjmallett /* 48295990Sjmallett * Save the first argument and iterate over it, we 48395990Sjmallett * cannot do strnsubst() to it. 48495990Sjmallett */ 485233038Sjilles if ((*tmp++ = strdup(*avj++)) == NULL) { 486233038Sjilles warnx("strdup failed"); 487233038Sjilles xexit(*argv, 1); 488233038Sjilles } 48995990Sjmallett 49095990Sjmallett /* 49195990Sjmallett * For each argument to utility, if we have not used up 49295990Sjmallett * the number of replacements we are allowed to do, and 493101677Sschweikh * if the argument contains at least one occurrence of 49495990Sjmallett * replstr, call strnsubst(), else just save the string. 49595990Sjmallett * Iterations over elements of avj and tmp are done 49695990Sjmallett * where appropriate. 49795990Sjmallett */ 49895990Sjmallett while (--argc) { 49995990Sjmallett *tmp = *avj++; 50095990Sjmallett if (repls && strstr(*tmp, replstr) != NULL) { 501153918Sjmallett strnsubst(tmp++, replstr, inpline, (size_t)Sflag); 502111581Sjmallett if (repls > 0) 503111581Sjmallett repls--; 50495990Sjmallett } else { 505233038Sjilles if ((*tmp = strdup(*tmp)) == NULL) { 506233038Sjilles warnx("strdup failed"); 507233038Sjilles xexit(*argv, 1); 508233038Sjilles } 50995990Sjmallett tmp++; 51095990Sjmallett } 51195990Sjmallett } 51295990Sjmallett 51395990Sjmallett /* 51495990Sjmallett * Run it. 51595990Sjmallett */ 51696006Sjmallett *tmp = NULL; 51795990Sjmallett run(tmp2); 51895990Sjmallett 51995990Sjmallett /* 52095990Sjmallett * Walk from the tail to the head, free along the way. 52195990Sjmallett */ 52295990Sjmallett for (; tmp2 != tmp; tmp--) 52395990Sjmallett free(*tmp); 52495990Sjmallett /* 52595990Sjmallett * Now free the list itself. 52695990Sjmallett */ 52795990Sjmallett free(tmp2); 52895990Sjmallett 52995990Sjmallett /* 53097619Sjmallett * Free the input line buffer, if we have one. 53195990Sjmallett */ 53297794Sjmallett if (inpline != NULL) { 53397619Sjmallett free(inpline); 53497794Sjmallett inpline = NULL; 53597794Sjmallett } 53695990Sjmallett} 53795990Sjmallett 53895990Sjmallettstatic void 53992377Sjmallettrun(char **argv) 5401590Srgrimes{ 541108156Stjr pid_t pid; 542116302Smux int fd; 54396050Sjmallett char **avec; 5441590Srgrimes 54596057Sjmallett /* 54696057Sjmallett * If the user wants to be notified of each command before it is 54796057Sjmallett * executed, notify them. If they want the notification to be 54896057Sjmallett * followed by a prompt, then prompt them. 54996057Sjmallett */ 55095080Sjmallett if (tflag || pflag) { 5511590Srgrimes (void)fprintf(stderr, "%s", *argv); 55296050Sjmallett for (avec = argv + 1; *avec != NULL; ++avec) 55396050Sjmallett (void)fprintf(stderr, " %s", *avec); 55496057Sjmallett /* 55596057Sjmallett * If the user has asked to be prompted, do so. 55696057Sjmallett */ 55796057Sjmallett if (pflag) 55896057Sjmallett /* 55996057Sjmallett * If they asked not to exec, return without execution 56096057Sjmallett * but if they asked to, go to the execution. If we 56196057Sjmallett * could not open their tty, break the switch and drop 56296057Sjmallett * back to -t behaviour. 56396057Sjmallett */ 56496057Sjmallett switch (prompt()) { 56596057Sjmallett case 0: 56695085Sjmallett return; 56796057Sjmallett case 1: 56896057Sjmallett goto exec; 56996057Sjmallett case 2: 57096057Sjmallett break; 57196057Sjmallett } 57296057Sjmallett (void)fprintf(stderr, "\n"); 57396057Sjmallett (void)fflush(stderr); 5741590Srgrimes } 57596057Sjmallettexec: 57691969Sdes childerr = 0; 577153959Sjmallett switch (pid = vfork()) { 5781590Srgrimes case -1: 579233038Sjilles warn("vfork"); 580233038Sjilles xexit(*argv, 1); 5811590Srgrimes case 0: 582112514Smux if (oflag) { 583116302Smux if ((fd = open(_PATH_TTY, O_RDONLY)) == -1) 584116302Smux err(1, "can't open /dev/tty"); 585112746Smux } else { 586116302Smux fd = open(_PATH_DEVNULL, O_RDONLY); 587112514Smux } 588116302Smux if (fd > STDIN_FILENO) { 589116302Smux if (dup2(fd, STDIN_FILENO) != 0) 590116302Smux err(1, "can't dup2 to stdin"); 591116302Smux close(fd); 592116302Smux } 5931590Srgrimes execvp(argv[0], argv); 59491969Sdes childerr = errno; 5951590Srgrimes _exit(1); 5961590Srgrimes } 597215615Smckay pids_add(pid); 598108156Stjr waitchildren(*argv, 0); 5991590Srgrimes} 6001590Srgrimes 601215615Smckay/* 602215615Smckay * Wait for a tracked child to exit and return its pid and exit status. 603215615Smckay * 604215615Smckay * Ignores (discards) all untracked child processes. 605215615Smckay * Returns -1 and sets errno to ECHILD if no tracked children exist. 606215615Smckay * If block is set, waits indefinitely for a child process to exit. 607215615Smckay * If block is not set and no children have exited, returns 0 immediately. 608215615Smckay */ 609215615Smckaystatic pid_t 610215615Smckayxwait(int block, int *status) { 611215615Smckay pid_t pid; 612215615Smckay 613215615Smckay if (pids_empty()) { 614215615Smckay errno = ECHILD; 615215642Smckay return (-1); 616215615Smckay } 617215615Smckay 618215615Smckay while ((pid = waitpid(-1, status, block ? 0 : WNOHANG)) > 0) 619215615Smckay if (pids_remove(pid)) 620215615Smckay break; 621215615Smckay 622215642Smckay return (pid); 623215615Smckay} 624215615Smckay 625108156Stjrstatic void 626233038Sjillesxexit(const char *name, const int exit_code) { 627233038Sjilles waitchildren(name, 1); 628233038Sjilles exit(exit_code); 629233038Sjilles} 630233038Sjilles 631233038Sjillesstatic void 632108156Stjrwaitchildren(const char *name, int waitall) 633108156Stjr{ 634108156Stjr pid_t pid; 635108156Stjr int status; 636233038Sjilles int cause_exit = 0; 637108156Stjr 638215615Smckay while ((pid = xwait(waitall || pids_full(), &status)) > 0) { 639233038Sjilles /* 640233038Sjilles * If we couldn't invoke the utility or if utility exited 641233038Sjilles * because of a signal or with a value of 255, warn (per 642233038Sjilles * POSIX), and then wait until all other children have 643233038Sjilles * exited before exiting 1-125. POSIX requires us to stop 644233038Sjilles * reading if child exits because of a signal or with 255, 645233038Sjilles * but it does not require us to exit immediately; waiting 646233038Sjilles * is preferable to orphaning. 647233038Sjilles */ 648233038Sjilles if (childerr != 0 && cause_exit == 0) { 649108156Stjr errno = childerr; 650233038Sjilles waitall = 1; 651233038Sjilles cause_exit = ENOENT ? 127 : 126; 652233038Sjilles warn("%s", name); 653233038Sjilles } else if (WIFSIGNALED(status)) { 654233038Sjilles waitall = cause_exit = 1; 655233038Sjilles warnx("%s: terminated with signal %d; aborting", 656232108Sjilles name, WTERMSIG(status)); 657233038Sjilles } else if (WEXITSTATUS(status) == 255) { 658233038Sjilles waitall = cause_exit = 1; 659233038Sjilles warnx("%s: exited with status 255; aborting", name); 660233038Sjilles } else if (WEXITSTATUS(status)) 661233038Sjilles rval = 1; 662108156Stjr } 663215615Smckay 664233038Sjilles if (cause_exit) 665233038Sjilles exit(cause_exit); 666108156Stjr if (pid == -1 && errno != ECHILD) 667215615Smckay err(1, "waitpid"); 668108156Stjr} 669108156Stjr 670215615Smckay#define NOPID (0) 671215615Smckay 672215615Smckaystatic void 673215642Smckaypids_init(void) 674215615Smckay{ 675215615Smckay int i; 676215615Smckay 677215615Smckay if ((childpids = malloc(maxprocs * sizeof(*childpids))) == NULL) 678215642Smckay errx(1, "malloc failed"); 679215615Smckay 680215615Smckay for (i = 0; i < maxprocs; i++) 681215615Smckay clearslot(i); 682215615Smckay} 683215615Smckay 684215615Smckaystatic int 685215642Smckaypids_empty(void) 686215615Smckay{ 687250431Seadler 688215642Smckay return (curprocs == 0); 689215615Smckay} 690215615Smckay 691215615Smckaystatic int 692215642Smckaypids_full(void) 693215615Smckay{ 694250431Seadler 695215642Smckay return (curprocs >= maxprocs); 696215615Smckay} 697215615Smckay 698215615Smckaystatic void 699215615Smckaypids_add(pid_t pid) 700215615Smckay{ 701215615Smckay int slot; 702215615Smckay 703215615Smckay slot = findfreeslot(); 704215615Smckay childpids[slot] = pid; 705215615Smckay curprocs++; 706215615Smckay} 707215615Smckay 708215615Smckaystatic int 709215615Smckaypids_remove(pid_t pid) 710215615Smckay{ 711215615Smckay int slot; 712215615Smckay 713215615Smckay if ((slot = findslot(pid)) < 0) 714215642Smckay return (0); 715215615Smckay 716215615Smckay clearslot(slot); 717215615Smckay curprocs--; 718215642Smckay return (1); 719215615Smckay} 720215615Smckay 721215615Smckaystatic int 722215642Smckayfindfreeslot(void) 723215615Smckay{ 724215615Smckay int slot; 725215615Smckay 726215615Smckay if ((slot = findslot(NOPID)) < 0) 727215615Smckay errx(1, "internal error: no free pid slot"); 728215642Smckay return (slot); 729215615Smckay} 730215615Smckay 731215615Smckaystatic int 732215615Smckayfindslot(pid_t pid) 733215615Smckay{ 734215615Smckay int slot; 735215615Smckay 736215615Smckay for (slot = 0; slot < maxprocs; slot++) 737215615Smckay if (childpids[slot] == pid) 738215642Smckay return (slot); 739215642Smckay return (-1); 740215615Smckay} 741215615Smckay 742215615Smckaystatic void 743215615Smckayclearslot(int slot) 744215615Smckay{ 745250431Seadler 746215615Smckay childpids[slot] = NOPID; 747215615Smckay} 748215615Smckay 74996057Sjmallett/* 75096057Sjmallett * Prompt the user about running a command. 75196057Sjmallett */ 75296057Sjmallettstatic int 75396057Sjmallettprompt(void) 75496057Sjmallett{ 75596057Sjmallett regex_t cre; 75696057Sjmallett size_t rsize; 75796057Sjmallett int match; 75896057Sjmallett char *response; 75996057Sjmallett FILE *ttyfp; 76096057Sjmallett 76196057Sjmallett if ((ttyfp = fopen(_PATH_TTY, "r")) == NULL) 76296057Sjmallett return (2); /* Indicate that the TTY failed to open. */ 76396057Sjmallett (void)fprintf(stderr, "?..."); 76496057Sjmallett (void)fflush(stderr); 76596057Sjmallett if ((response = fgetln(ttyfp, &rsize)) == NULL || 766113136Simp regcomp(&cre, nl_langinfo(YESEXPR), REG_BASIC) != 0) { 76796057Sjmallett (void)fclose(ttyfp); 76896057Sjmallett return (0); 76996057Sjmallett } 770153960Sjmallett response[rsize - 1] = '\0'; 77196057Sjmallett match = regexec(&cre, response, 0, NULL, 0); 77296057Sjmallett (void)fclose(ttyfp); 77396057Sjmallett regfree(&cre); 77496057Sjmallett return (match == 0); 77596057Sjmallett} 77696057Sjmallett 77728826Scharnierstatic void 77892377Sjmallettusage(void) 7791590Srgrimes{ 780250431Seadler 78176605Sdd fprintf(stderr, 782153918Sjmallett"usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements] [-S replsize]]\n" 783153918Sjmallett" [-J replstr] [-L number] [-n number [-x]] [-P maxprocs]\n" 784153918Sjmallett" [-s size] [utility [argument ...]]\n"); 7851590Srgrimes exit(1); 7861590Srgrimes} 787