xref: /netbsd-src/usr.bin/xargs/xargs.c (revision 2a399c6883d870daece976daec6ffa7bb7f934ce)
1 /*	$NetBSD: xargs.c,v 1.9 1997/10/20 03:30:16 lukem Exp $	*/
2 
3 /*-
4  * Copyright (c) 1990, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * John B. Roll Jr.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __COPYRIGHT("@(#) Copyright (c) 1990, 1993\n\
42 	The Regents of the University of California.  All rights reserved.\n");
43 #endif /* not lint */
44 
45 #ifndef lint
46 #if 0
47 static char sccsid[] = "@(#)xargs.c	8.1 (Berkeley) 6/6/93";
48 #endif
49 __RCSID("$NetBSD: xargs.c,v 1.9 1997/10/20 03:30:16 lukem Exp $");
50 #endif /* not lint */
51 
52 #include <sys/types.h>
53 #include <sys/wait.h>
54 #include <errno.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59 #include <limits.h>
60 #include <locale.h>
61 #include <err.h>
62 #include "pathnames.h"
63 
64 int tflag, zflag, rval;
65 
66 void	run __P((char **));
67 int	main __P((int, char **));
68 void	usage __P((void));
69 
70 int
71 main(argc, argv)
72 	int argc;
73 	char **argv;
74 {
75 	int ch;
76 	char *p, *bbp, *ebp, **bxp, **exp, **xp;
77 	int cnt, indouble, insingle, nargs, nflag, nline, xflag;
78 	char **av, *argp;
79 
80 	setlocale(LC_ALL, "");
81 
82 	/*
83 	 * POSIX.2 limits the exec line length to ARG_MAX - 2K.  Running that
84 	 * caused some E2BIG errors, so it was changed to ARG_MAX - 4K.  Given
85 	 * that the smallest argument is 2 bytes in length, this means that
86 	 * the number of arguments is limited to:
87 	 *
88 	 *	 (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2.
89 	 *
90 	 * We arbitrarily limit the number of arguments to 5000.  This is
91 	 * allowed by POSIX.2 as long as the resulting minimum exec line is
92 	 * at least LINE_MAX.  Realloc'ing as necessary is possible, but
93 	 * probably not worthwhile.
94 	 */
95 	nargs = 5000;
96 	nline = ARG_MAX - 4 * 1024;
97 	nflag = xflag = 0;
98 	while ((ch = getopt(argc, argv, "0n:s:tx")) != -1)
99 		switch(ch) {
100 		case '0':
101 			zflag = 1;
102 			break;
103 		case 'n':
104 			nflag = 1;
105 			if ((nargs = atoi(optarg)) <= 0)
106 				errx(1, "illegal argument count");
107 			break;
108 		case 's':
109 			nline = atoi(optarg);
110 			break;
111 		case 't':
112 			tflag = 1;
113 			break;
114 		case 'x':
115 			xflag = 1;
116 			break;
117 		case '?':
118 		default:
119 			usage();
120 	}
121 	argc -= optind;
122 	argv += optind;
123 
124 	if (xflag && !nflag)
125 		usage();
126 
127 	/*
128 	 * Allocate pointers for the utility name, the utility arguments,
129 	 * the maximum arguments to be read from stdin and the trailing
130 	 * NULL.
131 	 */
132 	if (!(av = bxp =
133 	    malloc((u_int)(1 + argc + nargs + 1) * sizeof(char **))))
134 		err(1, "malloc");
135 
136 	/*
137 	 * Use the user's name for the utility as argv[0], just like the
138 	 * shell.  Echo is the default.  Set up pointers for the user's
139 	 * arguments.
140 	 */
141 	if (!*argv)
142 		cnt = strlen(*bxp++ = _PATH_ECHO);
143 	else {
144 		cnt = 0;
145 		do {
146 			cnt += strlen(*bxp++ = *argv) + 1;
147 		} while (*++argv);
148 	}
149 
150 	/*
151 	 * Set up begin/end/traversing pointers into the array.  The -n
152 	 * count doesn't include the trailing NULL pointer, so the malloc
153 	 * added in an extra slot.
154 	 */
155 	exp = (xp = bxp) + nargs;
156 
157 	/*
158 	 * Allocate buffer space for the arguments read from stdin and the
159 	 * trailing NULL.  Buffer space is defined as the default or specified
160 	 * space, minus the length of the utility name and arguments.  Set up
161 	 * begin/end/traversing pointers into the array.  The -s count does
162 	 * include the trailing NULL, so the malloc didn't add in an extra
163 	 * slot.
164 	 */
165 	nline -= cnt;
166 	if (nline <= 0)
167 		errx(1, "insufficient space for command");
168 
169 	if (!(bbp = malloc((u_int)nline + 1)))
170 		err(1, "malloc");
171 	ebp = (argp = p = bbp) + nline - 1;
172 
173 	for (insingle = indouble = 0;;)
174 		switch(ch = getchar()) {
175 		case EOF:
176 			/* No arguments since last exec. */
177 			if (p == bbp)
178 				exit(rval);
179 
180 			/* Nothing since end of last argument. */
181 			if (argp == p) {
182 				*xp = NULL;
183 				run(av);
184 				exit(rval);
185 			}
186 			goto arg1;
187 		case ' ':
188 		case '\t':
189 			/* Quotes escape tabs and spaces. */
190 			if (insingle || indouble || zflag)
191 				goto addch;
192 			goto arg2;
193 		case '\0':
194 			if (zflag)
195 				goto arg2;
196 			goto addch;
197 		case '\n':
198 			if (zflag)
199 				goto addch;
200 			/* Empty lines are skipped. */
201 			if (argp == p)
202 				continue;
203 
204 			/* Quotes do not escape newlines. */
205 arg1:			if (insingle || indouble)
206 				 errx(1, "unterminated quote");
207 
208 arg2:			*p = '\0';
209 			*xp++ = argp;
210 
211 			/*
212 			 * If max'd out on args or buffer, or reached EOF,
213 			 * run the command.  If xflag and max'd out on buffer
214 			 * but not on args, object.
215 			 */
216 			if (xp == exp || p == ebp || ch == EOF) {
217 				if (xflag && xp != exp && p == ebp)
218 					errx(1, "insufficient space for arguments");
219 				*xp = NULL;
220 				run(av);
221 				if (ch == EOF)
222 					exit(rval);
223 				p = bbp;
224 				xp = bxp;
225 			} else
226 				++p;
227 			argp = p;
228 			break;
229 		case '\'':
230 			if (indouble || zflag)
231 				goto addch;
232 			insingle = !insingle;
233 			break;
234 		case '"':
235 			if (insingle || zflag)
236 				goto addch;
237 			indouble = !indouble;
238 			break;
239 		case '\\':
240 			if (zflag)
241 				goto addch;
242 			/* Backslash escapes anything, is escaped by quotes. */
243 			if (!insingle && !indouble && (ch = getchar()) == EOF)
244 				errx(1, "backslash at EOF");
245 			/* FALLTHROUGH */
246 		default:
247 addch:			if (p < ebp) {
248 				*p++ = ch;
249 				break;
250 			}
251 
252 			/* If only one argument, not enough buffer space. */
253 			if (bxp == xp)
254 				errx(1, "insufficient space for argument");
255 			/* Didn't hit argument limit, so if xflag object. */
256 			if (xflag)
257 				errx(1, "insufficient space for arguments");
258 
259 			*xp = NULL;
260 			run(av);
261 			xp = bxp;
262 			cnt = ebp - argp;
263 			memmove(bbp, argp, cnt);
264 			p = (argp = bbp) + cnt;
265 			*p++ = ch;
266 			break;
267 		}
268 	/* NOTREACHED */
269 }
270 
271 void
272 run(argv)
273 	char **argv;
274 {
275 	volatile int noinvoke;
276 	char **p;
277 	pid_t pid;
278 	int status;
279 
280 	if (tflag) {
281 		(void)fprintf(stderr, "%s", *argv);
282 		for (p = argv + 1; *p; ++p)
283 			(void)fprintf(stderr, " %s", *p);
284 		(void)fprintf(stderr, "\n");
285 		(void)fflush(stderr);
286 	}
287 	noinvoke = 0;
288 	switch(pid = vfork()) {
289 	case -1:
290 		err(1, "vfork");
291 	case 0:
292 		execvp(argv[0], argv);
293 		noinvoke = (errno == ENOENT) ? 127 : 126;
294 		warn("%s", argv[0]);;
295 		_exit(1);
296 	}
297 	pid = waitpid(pid, &status, 0);
298 	if (pid == -1)
299 		err(1, "waitpid");
300 
301 	/*
302 	 * If we couldn't invoke the utility or the utility didn't exit
303 	 * properly, quit with 127 or 126 respectively.
304 	 */
305 	if (noinvoke)
306 		exit(noinvoke);
307 
308 	/*
309 	 * According to POSIX, we have to exit if the utility exits with
310 	 * a 255 status, or is interrupted by a signal.   xargs is allowed
311 	 * to return any exit status between 1 and 125 in these cases, but
312 	 * we'll use 124 and 125, the same values used by GNU xargs.
313 	 */
314 	if (WIFEXITED(status)) {
315 		if (WEXITSTATUS (status) == 255) {
316 			warnx ("%s exited with status 255", argv[0]);
317 			exit(124);
318 		} else if (WEXITSTATUS (status) != 0) {
319 			rval = 123;
320 		}
321 	} else if (WIFSIGNALED (status)) {
322 		warnx ("%s terminated by signal %d", argv[0], WTERMSIG(status));
323 		exit(125);
324 	}
325 }
326 
327 void
328 usage()
329 {
330 	(void)fprintf(stderr,
331 "usage: xargs [-0t] [-n number [-x]] [-s size] [utility [argument ...]]\n");
332 	exit(1);
333 }
334