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