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