xref: /freebsd/usr.bin/xargs/xargs.c (revision 1925cb245cfde37f4ca9ecdb080a5abd6153297f)
19b50d902SRodney W. Grimes /*-
29b50d902SRodney W. Grimes  * Copyright (c) 1990, 1993
39b50d902SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
49b50d902SRodney W. Grimes  *
59b50d902SRodney W. Grimes  * This code is derived from software contributed to Berkeley by
69b50d902SRodney W. Grimes  * John B. Roll Jr.
79b50d902SRodney W. Grimes  *
89b50d902SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
99b50d902SRodney W. Grimes  * modification, are permitted provided that the following conditions
109b50d902SRodney W. Grimes  * are met:
119b50d902SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
129b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
139b50d902SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
149b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
159b50d902SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
169b50d902SRodney W. Grimes  * 3. All advertising materials mentioning features or use of this software
179b50d902SRodney W. Grimes  *    must display the following acknowledgement:
189b50d902SRodney W. Grimes  *	This product includes software developed by the University of
199b50d902SRodney W. Grimes  *	California, Berkeley and its contributors.
209b50d902SRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
219b50d902SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
229b50d902SRodney W. Grimes  *    without specific prior written permission.
239b50d902SRodney W. Grimes  *
249b50d902SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
259b50d902SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
269b50d902SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
279b50d902SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
289b50d902SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
299b50d902SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
309b50d902SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
319b50d902SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
329b50d902SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
339b50d902SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
349b50d902SRodney W. Grimes  * SUCH DAMAGE.
35fc17b349SJuli Mallett  *
36fc17b349SJuli Mallett  * $xMach: xargs.c,v 1.6 2002/02/23 05:27:47 tim Exp $
379b50d902SRodney W. Grimes  */
389b50d902SRodney W. Grimes 
39d7a43b24SJuli Mallett #ifndef lint
40d7a43b24SJuli Mallett static const char copyright[] =
41d7a43b24SJuli Mallett "@(#) Copyright (c) 1990, 1993\n\
42d7a43b24SJuli Mallett 	The Regents of the University of California.  All rights reserved.\n";
43d7a43b24SJuli Mallett #endif /* not lint */
44d7a43b24SJuli Mallett 
45d7a43b24SJuli Mallett #if 0
46d7a43b24SJuli Mallett #ifndef lint
47d7a43b24SJuli Mallett static char sccsid[] = "@(#)xargs.c	8.1 (Berkeley) 6/6/93";
48d7a43b24SJuli Mallett #endif /* not lint */
49d7a43b24SJuli Mallett #endif
50d7a43b24SJuli Mallett 
5151883012SMike Barcroft #include <sys/cdefs.h>
5251883012SMike Barcroft __FBSDID("$FreeBSD$");
5351883012SMike Barcroft 
549b50d902SRodney W. Grimes #include <sys/types.h>
559b50d902SRodney W. Grimes #include <sys/wait.h>
5616b07a33SMark Murray 
57a51024e2SPhilippe Charnier #include <err.h>
588ad749a4SDag-Erling Smørgrav #include <errno.h>
599b50d902SRodney W. Grimes #include <stdio.h>
609b50d902SRodney W. Grimes #include <stdlib.h>
619b50d902SRodney W. Grimes #include <string.h>
629b50d902SRodney W. Grimes #include <unistd.h>
6316b07a33SMark Murray 
649b50d902SRodney W. Grimes #include "pathnames.h"
659b50d902SRodney W. Grimes 
66fc17b349SJuli Mallett static void	run(char **);
6773385ac6SJuli Mallett static void	usage(void);
68fc17b349SJuli Mallett void		strnsubst(char **, const char *, const char *, size_t);
699b50d902SRodney W. Grimes 
7016b07a33SMark Murray static char echo[] = _PATH_ECHO;
71fc17b349SJuli Mallett static int pflag, tflag, rval, zflag;
7216b07a33SMark Murray 
7373385ac6SJuli Mallett extern char *environ[];
7473385ac6SJuli Mallett 
75a51024e2SPhilippe Charnier int
7673385ac6SJuli Mallett main(int argc, char **argv)
779b50d902SRodney W. Grimes {
78a3e5bc4fSJoseph Koshy 	long arg_max;
794f49da74SJuli Mallett 	int ch, cnt, count, Iflag, indouble, insingle, Jflag, jfound, Lflag;
80b50a7286SJuli Mallett 	int nargs, nflag, nline, Rflag, wasquoted, foundeof, xflag;
81fc17b349SJuli Mallett 	size_t linelen;
82fc17b349SJuli Mallett 	const char *eofstr;
83fc17b349SJuli Mallett 	char **av, **avj, **bxp, **ep, **exp, **xp;
84fc17b349SJuli Mallett 	char *argp, *bbp, *ebp, *inpline, *p, *replstr;
859b50d902SRodney W. Grimes 
8673385ac6SJuli Mallett 	ep = environ;
87fc17b349SJuli Mallett 	inpline = replstr = NULL;
88fc17b349SJuli Mallett 	eofstr = "";
894f49da74SJuli Mallett 	cnt = count = Iflag = Jflag = jfound = Lflag = nflag = Rflag = xflag =
90b50a7286SJuli Mallett 	    wasquoted = 0;
918d904f15SDima Dorfman 
929b50d902SRodney W. Grimes 	/*
939b50d902SRodney W. Grimes 	 * POSIX.2 limits the exec line length to ARG_MAX - 2K.  Running that
949b50d902SRodney W. Grimes 	 * caused some E2BIG errors, so it was changed to ARG_MAX - 4K.  Given
959b50d902SRodney W. Grimes 	 * that the smallest argument is 2 bytes in length, this means that
969b50d902SRodney W. Grimes 	 * the number of arguments is limited to:
979b50d902SRodney W. Grimes 	 *
989b50d902SRodney W. Grimes 	 *	 (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2.
999b50d902SRodney W. Grimes 	 *
1009b50d902SRodney W. Grimes 	 * We arbitrarily limit the number of arguments to 5000.  This is
1019b50d902SRodney W. Grimes 	 * allowed by POSIX.2 as long as the resulting minimum exec line is
1029b50d902SRodney W. Grimes 	 * at least LINE_MAX.  Realloc'ing as necessary is possible, but
1039b50d902SRodney W. Grimes 	 * probably not worthwhile.
1049b50d902SRodney W. Grimes 	 */
1059b50d902SRodney W. Grimes 	nargs = 5000;
106a3e5bc4fSJoseph Koshy 	if ((arg_max = sysconf(_SC_ARG_MAX)) == -1)
107a3e5bc4fSJoseph Koshy 		errx(1, "sysconf(_SC_ARG_MAX) failed");
108a3e5bc4fSJoseph Koshy 	nline = arg_max - 4 * 1024;
109e5009da0SSatoshi Asami 	while (*ep) {
110e5009da0SSatoshi Asami 		/* 1 byte for each '\0' */
111e5009da0SSatoshi Asami 		nline -= strlen(*ep++) + 1 + sizeof(*ep);
112e5009da0SSatoshi Asami 	}
113b50a7286SJuli Mallett 	while ((ch = getopt(argc, argv, "0E:I:J:L:n:pR:s:tx")) != -1)
1149b50d902SRodney W. Grimes 		switch(ch) {
115fc17b349SJuli Mallett 		case 'E':
116fc17b349SJuli Mallett 			eofstr = optarg;
117fc17b349SJuli Mallett 			break;
118fc17b349SJuli Mallett 		case 'I':
119fc17b349SJuli Mallett 			Iflag = 1;
1204f49da74SJuli Mallett 			Lflag = 1;
121b50a7286SJuli Mallett 			Rflag = 5;
122fc17b349SJuli Mallett 			replstr = optarg;
123fc17b349SJuli Mallett 			break;
1248d904f15SDima Dorfman 		case 'J':
125b50a7286SJuli Mallett 			Jflag = 1;
1268d904f15SDima Dorfman 			replstr = optarg;
1278d904f15SDima Dorfman 			break;
128fc17b349SJuli Mallett 		case 'L':
1294f49da74SJuli Mallett 			Lflag = atoi(optarg);
130fc17b349SJuli Mallett 			break;
1319b50d902SRodney W. Grimes 		case 'n':
1329b50d902SRodney W. Grimes 			nflag = 1;
1339b50d902SRodney W. Grimes 			if ((nargs = atoi(optarg)) <= 0)
134a51024e2SPhilippe Charnier 				errx(1, "illegal argument count");
1359b50d902SRodney W. Grimes 			break;
136fc17b349SJuli Mallett 		case 'p':
137fc17b349SJuli Mallett 			pflag = 1;
138fc17b349SJuli Mallett 			break;
139b50a7286SJuli Mallett 		case 'R':
140b50a7286SJuli Mallett 			if (!Iflag)
141b50a7286SJuli Mallett 				usage();
142b50a7286SJuli Mallett 			if ((Rflag = atoi(optarg)) <= 0)
143b50a7286SJuli Mallett 				errx(1, "illegal number of replacements");
144b50a7286SJuli Mallett 			break;
1459b50d902SRodney W. Grimes 		case 's':
1469b50d902SRodney W. Grimes 			nline = atoi(optarg);
1479b50d902SRodney W. Grimes 			break;
1489b50d902SRodney W. Grimes 		case 't':
1499b50d902SRodney W. Grimes 			tflag = 1;
1509b50d902SRodney W. Grimes 			break;
1519b50d902SRodney W. Grimes 		case 'x':
1529b50d902SRodney W. Grimes 			xflag = 1;
1539b50d902SRodney W. Grimes 			break;
154d9198881SWarner Losh 		case '0':
155d9198881SWarner Losh 			zflag = 1;
156d9198881SWarner Losh 			break;
1579b50d902SRodney W. Grimes 		case '?':
1589b50d902SRodney W. Grimes 		default:
1599b50d902SRodney W. Grimes 			usage();
1609b50d902SRodney W. Grimes 	}
1619b50d902SRodney W. Grimes 	argc -= optind;
1629b50d902SRodney W. Grimes 	argv += optind;
1639b50d902SRodney W. Grimes 
1649b50d902SRodney W. Grimes 	if (xflag && !nflag)
1659b50d902SRodney W. Grimes 		usage();
1664f49da74SJuli Mallett 	if (Iflag || Lflag)
167fc17b349SJuli Mallett 		xflag = 1;
168fc17b349SJuli Mallett 	if (replstr != NULL && *replstr == '\0')
169fc17b349SJuli Mallett 		errx(1, "replstr may not be empty");
1709b50d902SRodney W. Grimes 
1719b50d902SRodney W. Grimes 	/*
1729b50d902SRodney W. Grimes 	 * Allocate pointers for the utility name, the utility arguments,
1739b50d902SRodney W. Grimes 	 * the maximum arguments to be read from stdin and the trailing
1749b50d902SRodney W. Grimes 	 * NULL.
1759b50d902SRodney W. Grimes 	 */
176fc17b349SJuli Mallett 	linelen = 1 + argc + nargs + 1;
1770fa5e8dcSJuli Mallett 	if ((av = bxp = malloc(linelen * sizeof(char **))) == NULL)
1780fa5e8dcSJuli Mallett 		err(1, "malloc");
1799b50d902SRodney W. Grimes 
1809b50d902SRodney W. Grimes 	/*
1819b50d902SRodney W. Grimes 	 * Use the user's name for the utility as argv[0], just like the
1829b50d902SRodney W. Grimes 	 * shell.  Echo is the default.  Set up pointers for the user's
1839b50d902SRodney W. Grimes 	 * arguments.
1849b50d902SRodney W. Grimes 	 */
1859b50d902SRodney W. Grimes 	if (!*argv)
18616b07a33SMark Murray 		cnt = strlen((*bxp++ = echo));
1879b50d902SRodney W. Grimes 	else {
1889b50d902SRodney W. Grimes 		do {
189b50a7286SJuli Mallett 			if (Jflag && strcmp(*argv, replstr) == 0) {
1908d904f15SDima Dorfman 				jfound = 1;
1918d904f15SDima Dorfman 				argv++;
1928d904f15SDima Dorfman 				for (avj = argv; *avj; avj++)
1938d904f15SDima Dorfman 					cnt += strlen(*avj) + 1;
1948d904f15SDima Dorfman 				break;
1958d904f15SDima Dorfman 			}
1969b50d902SRodney W. Grimes 			cnt += strlen(*bxp++ = *argv) + 1;
1979b50d902SRodney W. Grimes 		} while (*++argv);
1989b50d902SRodney W. Grimes 	}
1999b50d902SRodney W. Grimes 
2009b50d902SRodney W. Grimes 	/*
2019b50d902SRodney W. Grimes 	 * Set up begin/end/traversing pointers into the array.  The -n
2029b50d902SRodney W. Grimes 	 * count doesn't include the trailing NULL pointer, so the malloc
2039b50d902SRodney W. Grimes 	 * added in an extra slot.
2049b50d902SRodney W. Grimes 	 */
2059b50d902SRodney W. Grimes 	exp = (xp = bxp) + nargs;
2069b50d902SRodney W. Grimes 
2079b50d902SRodney W. Grimes 	/*
2089b50d902SRodney W. Grimes 	 * Allocate buffer space for the arguments read from stdin and the
2099b50d902SRodney W. Grimes 	 * trailing NULL.  Buffer space is defined as the default or specified
2109b50d902SRodney W. Grimes 	 * space, minus the length of the utility name and arguments.  Set up
2119b50d902SRodney W. Grimes 	 * begin/end/traversing pointers into the array.  The -s count does
2129b50d902SRodney W. Grimes 	 * include the trailing NULL, so the malloc didn't add in an extra
2139b50d902SRodney W. Grimes 	 * slot.
2149b50d902SRodney W. Grimes 	 */
2159b50d902SRodney W. Grimes 	nline -= cnt;
2169b50d902SRodney W. Grimes 	if (nline <= 0)
217a51024e2SPhilippe Charnier 		errx(1, "insufficient space for command");
2189b50d902SRodney W. Grimes 
21976ccb81eSJuli Mallett 	if ((bbp = malloc((size_t)nline + 1)) == NULL)
220fc17b349SJuli Mallett 		err(1, "malloc");
2219b50d902SRodney W. Grimes 	ebp = (argp = p = bbp) + nline - 1;
2229b50d902SRodney W. Grimes 
2239b50d902SRodney W. Grimes 	for (insingle = indouble = 0;;)
2249b50d902SRodney W. Grimes 		switch(ch = getchar()) {
2259b50d902SRodney W. Grimes 		case EOF:
2269b50d902SRodney W. Grimes 			/* No arguments since last exec. */
2279b50d902SRodney W. Grimes 			if (p == bbp)
2289b50d902SRodney W. Grimes 				exit(rval);
2299b50d902SRodney W. Grimes 			goto arg1;
2309b50d902SRodney W. Grimes 		case ' ':
2319b50d902SRodney W. Grimes 		case '\t':
2329b50d902SRodney W. Grimes 			/* Quotes escape tabs and spaces. */
233d9198881SWarner Losh 			if (insingle || indouble || zflag)
2349b50d902SRodney W. Grimes 				goto addch;
2359b50d902SRodney W. Grimes 			goto arg2;
236d9198881SWarner Losh 		case '\0':
237d9198881SWarner Losh 			if (zflag)
238d9198881SWarner Losh 				goto arg2;
239d9198881SWarner Losh 			goto addch;
2409b50d902SRodney W. Grimes 		case '\n':
241fc17b349SJuli Mallett 			count++;
242d9198881SWarner Losh 			if (zflag)
243d9198881SWarner Losh 				goto addch;
244d9198881SWarner Losh 
2459b50d902SRodney W. Grimes 			/* Quotes do not escape newlines. */
2469b50d902SRodney W. Grimes arg1:			if (insingle || indouble)
247a51024e2SPhilippe Charnier 				 errx(1, "unterminated quote");
2489b50d902SRodney W. Grimes 
249ef9866beSJean-Marc Zucconi arg2:
250fc17b349SJuli Mallett 			foundeof = *eofstr != '\0' &&
251fc17b349SJuli Mallett 			    strcmp(argp, eofstr) == 0;
252fc17b349SJuli Mallett 
253ef9866beSJean-Marc Zucconi 			/* Do not make empty args unless they are quoted */
254fc17b349SJuli Mallett 			if ((argp != p || wasquoted) && !foundeof) {
255ef9866beSJean-Marc Zucconi 				*p++ = '\0';
2569b50d902SRodney W. Grimes 				*xp++ = argp;
257fc17b349SJuli Mallett 				if (Iflag) {
258fc17b349SJuli Mallett 					char *realloc_holder;
259fc17b349SJuli Mallett 					size_t curlen;
260fc17b349SJuli Mallett 					realloc_holder = inpline;
261fc17b349SJuli Mallett 					if (realloc_holder == NULL)
262fc17b349SJuli Mallett 						curlen = 0;
263fc17b349SJuli Mallett 					else {
2641925cb24SJuli Mallett 						/*
2651925cb24SJuli Mallett 						 * If this string is not zero
2661925cb24SJuli Mallett 						 * length, append a space for
2671925cb24SJuli Mallett 						 * seperation before the next
2681925cb24SJuli Mallett 						 * argument.
2691925cb24SJuli Mallett 						 */
2701925cb24SJuli Mallett 						if (*inpline != '\0')
271fc17b349SJuli Mallett 							strcat(inpline, " ");
272fc17b349SJuli Mallett 					}
273fc17b349SJuli Mallett 					curlen++;
2741925cb24SJuli Mallett 					/*
2751925cb24SJuli Mallett 					 * Allocate enough to hold what we will
2761925cb24SJuli Mallett 					 * be holding in a secont, and to append
2771925cb24SJuli Mallett 					 * a space next time through, if we have
2781925cb24SJuli Mallett 					 * to.
2791925cb24SJuli Mallett 					 */
280fc17b349SJuli Mallett 					inpline = realloc(realloc_holder, strlen(argp) +
2810fa5e8dcSJuli Mallett 					    curlen + 2);
282fc17b349SJuli Mallett 					if (inpline == NULL)
283fc17b349SJuli Mallett 						err(1, "realloc");
284fc17b349SJuli Mallett 					if (curlen == 1)
285fc17b349SJuli Mallett 						strcpy(inpline, argp);
286fc17b349SJuli Mallett 					else
287fc17b349SJuli Mallett 						strcat(inpline, argp);
288fc17b349SJuli Mallett 				}
289ef9866beSJean-Marc Zucconi 			}
2909b50d902SRodney W. Grimes 
2919b50d902SRodney W. Grimes 			/*
2929b50d902SRodney W. Grimes 			 * If max'd out on args or buffer, or reached EOF,
2939b50d902SRodney W. Grimes 			 * run the command.  If xflag and max'd out on buffer
2941925cb24SJuli Mallett 			 * but not on args, object.  Having reached the limit
2951925cb24SJuli Mallett 			 * of input lines, as specified by -L is the same as
2961925cb24SJuli Mallett 			 * maxing out on arguments.
2979b50d902SRodney W. Grimes 			 */
2984f49da74SJuli Mallett 			if (xp == exp || p > ebp || ch == EOF || (Lflag <= count && xflag) || foundeof) {
299ef9866beSJean-Marc Zucconi 				if (xflag && xp != exp && p > ebp)
300a51024e2SPhilippe Charnier 					errx(1, "insufficient space for arguments");
3018d904f15SDima Dorfman 				if (jfound) {
3028d904f15SDima Dorfman 					for (avj = argv; *avj; avj++)
3038d904f15SDima Dorfman 						*xp++ = *avj;
3048d904f15SDima Dorfman 				}
305fc17b349SJuli Mallett 				if (Iflag) {
306fc17b349SJuli Mallett 					char **tmp, **tmp2;
307fc17b349SJuli Mallett 					size_t repls;
3080fa5e8dcSJuli Mallett 					int iter;
309fc17b349SJuli Mallett 
3101925cb24SJuli Mallett 					/*
3111925cb24SJuli Mallett 					 * Set up some locals, the number of
3121925cb24SJuli Mallett 					 * times we may replace replstr with a
3131925cb24SJuli Mallett 					 * line of input, a modifiable pointer
3141925cb24SJuli Mallett 					 * to the head of the original argument
3151925cb24SJuli Mallett 					 * list, and the number of iterations to
3161925cb24SJuli Mallett 					 * perform -- the number of arguments.
3171925cb24SJuli Mallett 					 */
3181925cb24SJuli Mallett 					repls = Rflag;
3191925cb24SJuli Mallett 					avj = av;
3201925cb24SJuli Mallett 					iter = argc;
3211925cb24SJuli Mallett 
3221925cb24SJuli Mallett 					/*
3231925cb24SJuli Mallett 					 * Allocate memory to hold the argument
3241925cb24SJuli Mallett 					 * list.
3251925cb24SJuli Mallett 					 */
3260fa5e8dcSJuli Mallett 					tmp = malloc(linelen * sizeof(char **));
327fc17b349SJuli Mallett 					if (tmp == NULL)
328fc17b349SJuli Mallett 						err(1, "malloc");
329fc17b349SJuli Mallett 					tmp2 = tmp;
3301925cb24SJuli Mallett 					/*
3311925cb24SJuli Mallett 					 * Just save the first argument, as it
3321925cb24SJuli Mallett 					 * is the utility name, and we cannot
3331925cb24SJuli Mallett 					 * be trusted to do strnsubst() to it.
3341925cb24SJuli Mallett 					 */
3351925cb24SJuli Mallett 					*tmp++ = strdup(*avj++);
3361925cb24SJuli Mallett 					/*
3371925cb24SJuli Mallett 					 * Now for every argument to utility,
3381925cb24SJuli Mallett 					 * if we have not used up the number of
3391925cb24SJuli Mallett 					 * replacements we are allowed to do, and
3401925cb24SJuli Mallett 					 * if the argument contains at least one
3411925cb24SJuli Mallett 					 * occurance of replstr, call strnsubst(),
3421925cb24SJuli Mallett 					 * or else just save the string.
3431925cb24SJuli Mallett 					 * Iterations over elements of avj and tmp
3441925cb24SJuli Mallett 					 * are done where appropriate.
3451925cb24SJuli Mallett 					 */
3461925cb24SJuli Mallett 					while (--iter) {
3471925cb24SJuli Mallett 						*tmp = *avj++;
3481925cb24SJuli Mallett 						if (repls && strstr(*tmp, replstr) != NULL) {
3491925cb24SJuli Mallett 							strnsubst(tmp++, replstr, inpline,
3501925cb24SJuli Mallett 							    (size_t)255);
351fc17b349SJuli Mallett 							repls--;
352fc17b349SJuli Mallett 						} else {
3531925cb24SJuli Mallett 							if ((*tmp = strdup(*tmp)) == NULL)
3541925cb24SJuli Mallett 								err(1, "strdup");
355fc17b349SJuli Mallett 							tmp++;
356fc17b349SJuli Mallett 						}
3571925cb24SJuli Mallett 					}
3581925cb24SJuli Mallett 					/*
3591925cb24SJuli Mallett 					 * NULL terminate the list of arguments,
3601925cb24SJuli Mallett 					 * for run().
3611925cb24SJuli Mallett 					 */
362fc17b349SJuli Mallett 					*tmp = *xp = NULL;
363fc17b349SJuli Mallett 					run(tmp2);
3641925cb24SJuli Mallett 					/*
3651925cb24SJuli Mallett 					 * From the tail to the head, free along
3661925cb24SJuli Mallett 					 * the way.
3671925cb24SJuli Mallett 					 */
368fc17b349SJuli Mallett 					for (; tmp2 != tmp; tmp--)
369fc17b349SJuli Mallett 						free(*tmp);
3701925cb24SJuli Mallett 					/*
3711925cb24SJuli Mallett 					 * Free the list.
3721925cb24SJuli Mallett 					 */
373fc17b349SJuli Mallett 					free(tmp2);
3741925cb24SJuli Mallett 					/*
3751925cb24SJuli Mallett 					 * Free the input line buffer, and create
3761925cb24SJuli Mallett 					 * a new dummy.
3771925cb24SJuli Mallett 					 */
378fc17b349SJuli Mallett 					free(inpline);
379fc17b349SJuli Mallett 					inpline = strdup("");
380fc17b349SJuli Mallett 				} else {
3811925cb24SJuli Mallett 					/*
3821925cb24SJuli Mallett 					 * Mark the tail of the argument list with
3831925cb24SJuli Mallett 					 * a NULL, and run() with it.
3841925cb24SJuli Mallett 					 */
3859b50d902SRodney W. Grimes 					*xp = NULL;
3869b50d902SRodney W. Grimes 					run(av);
387fc17b349SJuli Mallett 				}
388fc17b349SJuli Mallett 				if (ch == EOF || foundeof)
3899b50d902SRodney W. Grimes 					exit(rval);
3909b50d902SRodney W. Grimes 				p = bbp;
3919b50d902SRodney W. Grimes 				xp = bxp;
392fc17b349SJuli Mallett 				count = 0;
393ef9866beSJean-Marc Zucconi 			}
3949b50d902SRodney W. Grimes 			argp = p;
395ef9866beSJean-Marc Zucconi 			wasquoted = 0;
3969b50d902SRodney W. Grimes 			break;
3979b50d902SRodney W. Grimes 		case '\'':
398d9198881SWarner Losh 			if (indouble || zflag)
3999b50d902SRodney W. Grimes 				goto addch;
4009b50d902SRodney W. Grimes 			insingle = !insingle;
401ef9866beSJean-Marc Zucconi 			wasquoted = 1;
4029b50d902SRodney W. Grimes 			break;
4039b50d902SRodney W. Grimes 		case '"':
404d9198881SWarner Losh 			if (insingle || zflag)
4059b50d902SRodney W. Grimes 				goto addch;
4069b50d902SRodney W. Grimes 			indouble = !indouble;
407ef9866beSJean-Marc Zucconi 			wasquoted = 1;
4089b50d902SRodney W. Grimes 			break;
4099b50d902SRodney W. Grimes 		case '\\':
410d9198881SWarner Losh 			if (zflag)
411d9198881SWarner Losh 				goto addch;
4129b50d902SRodney W. Grimes 			/* Backslash escapes anything, is escaped by quotes. */
4139b50d902SRodney W. Grimes 			if (!insingle && !indouble && (ch = getchar()) == EOF)
414a51024e2SPhilippe Charnier 				errx(1, "backslash at EOF");
4159b50d902SRodney W. Grimes 			/* FALLTHROUGH */
4169b50d902SRodney W. Grimes 		default:
4179b50d902SRodney W. Grimes addch:			if (p < ebp) {
4189b50d902SRodney W. Grimes 				*p++ = ch;
4199b50d902SRodney W. Grimes 				break;
4209b50d902SRodney W. Grimes 			}
4219b50d902SRodney W. Grimes 
4229b50d902SRodney W. Grimes 			/* If only one argument, not enough buffer space. */
4239b50d902SRodney W. Grimes 			if (bxp == xp)
424a51024e2SPhilippe Charnier 				errx(1, "insufficient space for argument");
4259b50d902SRodney W. Grimes 			/* Didn't hit argument limit, so if xflag object. */
4269b50d902SRodney W. Grimes 			if (xflag)
427a51024e2SPhilippe Charnier 				errx(1, "insufficient space for arguments");
4289b50d902SRodney W. Grimes 
4298d904f15SDima Dorfman 			if (jfound) {
4308d904f15SDima Dorfman 				for (avj = argv; *avj; avj++)
4318d904f15SDima Dorfman 					*xp++ = *avj;
4328d904f15SDima Dorfman 			}
4339b50d902SRodney W. Grimes 			*xp = NULL;
4349b50d902SRodney W. Grimes 			run(av);
4359b50d902SRodney W. Grimes 			xp = bxp;
4369b50d902SRodney W. Grimes 			cnt = ebp - argp;
437fc17b349SJuli Mallett 			memcpy(bbp, argp, (size_t)cnt);
4389b50d902SRodney W. Grimes 			p = (argp = bbp) + cnt;
4399b50d902SRodney W. Grimes 			*p++ = ch;
4409b50d902SRodney W. Grimes 			break;
4419b50d902SRodney W. Grimes 		}
4429b50d902SRodney W. Grimes 	/* NOTREACHED */
4439b50d902SRodney W. Grimes }
4449b50d902SRodney W. Grimes 
445fc17b349SJuli Mallett static void
44673385ac6SJuli Mallett run(char **argv)
4479b50d902SRodney W. Grimes {
4488ad749a4SDag-Erling Smørgrav 	volatile int childerr;
44916b07a33SMark Murray 	char **p;
450fc17b349SJuli Mallett 	FILE *ttyfp;
4519b50d902SRodney W. Grimes 	pid_t pid;
452fc17b349SJuli Mallett 	int ch, status;
4539b50d902SRodney W. Grimes 
454fc17b349SJuli Mallett 	if (tflag || pflag) {
4559b50d902SRodney W. Grimes 		(void)fprintf(stderr, "%s", *argv);
4569b50d902SRodney W. Grimes 		for (p = argv + 1; *p; ++p)
4579b50d902SRodney W. Grimes 			(void)fprintf(stderr, " %s", *p);
45891ae52cbSJuli Mallett 		if (pflag && (ttyfp = fopen("/dev/tty", "r")) != NULL) {
459fc17b349SJuli Mallett 			(void)fprintf(stderr, "?");
460fc17b349SJuli Mallett 			(void)fflush(stderr);
461fc17b349SJuli Mallett 			ch = getc(ttyfp);
462fc17b349SJuli Mallett 			fclose(ttyfp);
463fc17b349SJuli Mallett 			if (ch != 'y')
464fc17b349SJuli Mallett 				return;
465fc17b349SJuli Mallett 		} else {
4669b50d902SRodney W. Grimes 			(void)fprintf(stderr, "\n");
4679b50d902SRodney W. Grimes 			(void)fflush(stderr);
4689b50d902SRodney W. Grimes 		}
469fc17b349SJuli Mallett 	}
4708ad749a4SDag-Erling Smørgrav 	childerr = 0;
4718ad749a4SDag-Erling Smørgrav 	switch(pid = vfork()) {
4729b50d902SRodney W. Grimes 	case -1:
4738ad749a4SDag-Erling Smørgrav 		err(1, "vfork");
4749b50d902SRodney W. Grimes 	case 0:
4759b50d902SRodney W. Grimes 		execvp(argv[0], argv);
4768ad749a4SDag-Erling Smørgrav 		childerr = errno;
4779b50d902SRodney W. Grimes 		_exit(1);
4789b50d902SRodney W. Grimes 	}
4799b50d902SRodney W. Grimes 	pid = waitpid(pid, &status, 0);
4809b50d902SRodney W. Grimes 	if (pid == -1)
481a51024e2SPhilippe Charnier 		err(1, "waitpid");
482fc17b349SJuli Mallett 	/* If we couldn't invoke the utility, exit. */
483fc17b349SJuli Mallett 	if (childerr != 0)
484fc17b349SJuli Mallett 		err(childerr == ENOENT ? 127 : 126, "%s", *argv);
4859b50d902SRodney W. Grimes 	/* If utility signaled or exited with a value of 255, exit 1-125. */
4869b50d902SRodney W. Grimes 	if (WIFSIGNALED(status) || WEXITSTATUS(status) == 255)
4879b50d902SRodney W. Grimes 		exit(1);
4889b50d902SRodney W. Grimes 	if (WEXITSTATUS(status))
4899b50d902SRodney W. Grimes 		rval = 1;
4909b50d902SRodney W. Grimes }
4919b50d902SRodney W. Grimes 
492a51024e2SPhilippe Charnier static void
49373385ac6SJuli Mallett usage(void)
4949b50d902SRodney W. Grimes {
4958d904f15SDima Dorfman 	fprintf(stderr,
496b50a7286SJuli Mallett "usage: xargs [-0pt] [-E eofstr] [-I replstr [-R replacements]] [-J replstr]\n"
497b50a7286SJuli Mallett "             [-L number] [-n number [-x] [-s size] [utility [argument ...]]\n");
4989b50d902SRodney W. Grimes 	exit(1);
4999b50d902SRodney W. Grimes }
500