xref: /dragonfly/usr.bin/xargs/xargs.c (revision 6e5c5008)
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. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $xMach: xargs.c,v 1.6 2002/02/23 05:27:47 tim Exp $
33  *
34  * @(#) Copyright (c) 1990, 1993 The Regents of the University of California.  All rights reserved.
35  * @(#)xargs.c	8.1 (Berkeley) 6/6/93
36  * $FreeBSD: src/usr.bin/xargs/xargs.c,v 1.9.2.6 2003/06/01 21:40:35 mux Exp $
37  */
38 
39 #include <sys/types.h>
40 #include <sys/wait.h>
41 
42 #include <err.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <langinfo.h>
46 #include <limits.h>
47 #include <locale.h>
48 #include <paths.h>
49 #include <regex.h>
50 #include <stdint.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55 
56 #include "pathnames.h"
57 
58 static void	parse_input(int, char *[]);
59 static void	prerun(int, char *[]);
60 #ifndef BOOTSTRAPPING
61 static int	prompt(void);
62 #endif
63 static void	run(char **);
64 static void	usage(void);
65 void		strnsubst(char **, const char *, const char *, size_t);
66 static void	waitchildren(const char *, int);
67 
68 static char echo[] = _PATH_ECHO;
69 static char **av, **bxp, **ep, **expx, **xp;
70 static char *argp, *bbp, *ebp, *inpline, *p, *replstr;
71 static const char *eofstr;
72 static int count, insingle, indouble, oflag, pflag, tflag, Rflag, rval, zflag;
73 static int cnt, Iflag, jfound, Lflag, wasquoted, xflag;
74 static int curprocs, maxprocs;
75 static volatile int childerr;
76 
77 extern char **environ;
78 
79 int
80 main(int argc, char *argv[])
81 {
82 	char *tmp;
83 	long arg_max;
84 	int ch, Jflag, nargs, nflag, nline;
85 	size_t linelen;
86 
87 	inpline = replstr = NULL;
88 	ep = environ;
89 	eofstr = "";
90 	Jflag = nflag = 0;
91 
92 	(void)setlocale(LC_MESSAGES, "");
93 
94 	/*
95 	 * POSIX.2 limits the exec line length to ARG_MAX - 2K.  Running that
96 	 * caused some E2BIG errors, so it was changed to ARG_MAX - 4K.  Given
97 	 * that the smallest argument is 2 bytes in length, this means that
98 	 * the number of arguments is limited to:
99 	 *
100 	 *	 (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2.
101 	 *
102 	 * We arbitrarily limit the number of arguments to 5000.  This is
103 	 * allowed by POSIX.2 as long as the resulting minimum exec line is
104 	 * at least LINE_MAX.  Realloc'ing as necessary is possible, but
105 	 * probably not worthwhile.
106 	 */
107 	nargs = 5000;
108 	if ((arg_max = sysconf(_SC_ARG_MAX)) == -1)
109 		errx(1, "sysconf(_SC_ARG_MAX) failed");
110 	if (arg_max > INT32_MAX) {
111 		/* cap arg_max to int32_t */
112 		arg_max = INT32_MAX;
113 	}
114 	nline = arg_max - 4 * 1024;
115 	while (*ep != NULL) {
116 		/* 1 byte for each '\0' */
117 		nline -= strlen(*ep++) + 1 + sizeof(*ep);
118 	}
119 
120 	maxprocs = 1;
121 	while ((ch = getopt(argc, argv, "0E:I:J:L:n:oprP:R:s:tx")) != -1)
122 		switch(ch) {
123 		case 'E':
124 			eofstr = optarg;
125 			break;
126 		case 'I':
127 			Jflag = 0;
128 			Iflag = 1;
129 			Lflag = 1;
130 			replstr = optarg;
131 			break;
132 		case 'J':
133 			Iflag = 0;
134 			Jflag = 1;
135 			replstr = optarg;
136 			break;
137 		case 'L':
138 			Lflag = strtol(optarg, &tmp, 10);
139 			if (*tmp != 0 || *optarg == 0)
140 				errx(1, "illegal argument count");
141 			break;
142 		case 'n':
143 			nflag = 1;
144 			if ((nargs = atoi(optarg)) <= 0)
145 				errx(1, "illegal argument count");
146 			break;
147 		case 'o':
148 			oflag = 1;
149 			break;
150 		case 'p':
151 #ifndef BOOTSTRAPPING
152 			pflag = 1;
153 #endif
154 			break;
155 		case 'P':
156 			if ((maxprocs = atoi(optarg)) <= 0)
157 				errx(1, "max.processes must be >0");
158 			break;
159 		case 'R':
160 			if ((Rflag = atoi(optarg)) <= 0)
161 				errx(1, "illegal number of replacements");
162 			break;
163 		case 'r':
164 			/* GNU compatibility */
165 			/* --no-run-if-empty (this is our default) */
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 	expx = (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 == expx || p > ebp || ch == EOF ||
336 		    (Lflag <= count && xflag) || foundeof) {
337 			if (xflag && xp != expx && 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 			repls--;
451 		} else {
452 			if ((*tmp = strdup(*tmp)) == NULL)
453 				errx(1, "strdup failed");
454 			tmp++;
455 		}
456 	}
457 
458 	/*
459 	 * Run it.
460 	 */
461 	*tmp = NULL;
462 	run(tmp2);
463 
464 	/*
465 	 * Walk from the tail to the head, free along the way.
466 	 */
467 	for (; tmp2 != tmp; tmp--)
468 		free(*tmp);
469 	/*
470 	 * Now free the list itself.
471 	 */
472 	free(tmp2);
473 
474 	/*
475 	 * Free the input line buffer, if we have one.
476 	 */
477 	if (inpline != NULL) {
478 		free(inpline);
479 		inpline = NULL;
480 	}
481 }
482 
483 static void
484 run(char **argv)
485 {
486 	char **avec;
487 	pid_t pid;
488 
489 	/*
490 	 * If the user wants to be notified of each command before it is
491 	 * executed, notify them.  If they want the notification to be
492 	 * followed by a prompt, then prompt them.
493 	 */
494 	if (tflag || pflag) {
495 		(void)fprintf(stderr, "%s", *argv);
496 		for (avec = argv + 1; *avec != NULL; ++avec)
497 			(void)fprintf(stderr, " %s", *avec);
498 		/*
499 		 * If the user has asked to be prompted, do so.
500 		 */
501 #ifndef BOOTSTRAPPING
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 #endif
518 		(void)fprintf(stderr, "\n");
519 		(void)fflush(stderr);
520 	}
521 #ifndef BOOTSTRAPPING
522 exec:
523 #endif
524 	childerr = 0;
525 	switch(pid = vfork()) {
526 	case -1:
527 		err(1, "vfork");
528 	case 0:
529 		close(0);
530 		if (oflag) {
531 			if (open("/dev/tty", O_RDONLY) == -1)
532 				err(1, "open /dev/tty");
533 		} else {
534 			if (open("/dev/null", O_RDONLY) == -1)
535 				err(1, "open /dev/null");
536 		}
537 		execvp(argv[0], argv);
538 		childerr = errno;
539 		_exit(1);
540 	}
541 	curprocs++;
542 	waitchildren(*argv, 0);
543 }
544 
545 /*
546  * Handle child processes.
547  */
548 static void
549 waitchildren(const char *name, int waitall)
550 {
551 	pid_t pid;
552 	int status;
553 
554 	while ((pid = wait3(&status, !waitall && curprocs < maxprocs ?
555 		WNOHANG : 0, NULL)) > 0) {
556 		curprocs--;
557 
558 		/* If we couldn't invoke the utility, exit. */
559 		if (childerr != 0) {
560 			errno = childerr;
561 			err(errno == ENOENT ? 127 : 126, "%s", name);
562 		}
563 
564 		/*
565 		 * If utility signaled or exited with a value of 255,
566 		 * exit 1-125.
567 		 */
568 		if (WIFSIGNALED(status) || WEXITSTATUS(status) == 255)
569 			exit(1);
570 		if (WEXITSTATUS(status))
571 			rval = 1;
572         }
573 	if (pid == -1 && errno != ECHILD)
574 		err(1, "wait3");
575 }
576 
577 /*
578  * Prompt the user about running a command.
579  */
580 #ifndef BOOTSTRAPPING
581 static int
582 prompt(void)
583 {
584 	regex_t cre;
585 	size_t rsize;
586 	int match;
587 	char *response;
588 	FILE *ttyfp;
589 
590 	if ((ttyfp = fopen(_PATH_TTY, "r")) == NULL)
591 		return (2);	/* Indicate that the TTY failed to open. */
592 	(void)fprintf(stderr, "?...");
593 	(void)fflush(stderr);
594 	if ((response = fgetln(ttyfp, &rsize)) == NULL ||
595 	    regcomp(&cre,
596 		nl_langinfo(YESEXPR),
597 		REG_BASIC) != 0) {
598 		(void)fclose(ttyfp);
599 		return (0);
600 	}
601 	match = regexec(&cre, response, 0, NULL, 0);
602 	(void)fclose(ttyfp);
603 	regfree(&cre);
604 	return (match == 0);
605 }
606 #endif
607 
608 static void
609 usage(void)
610 {
611 	fprintf(stderr,
612 "usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements]] [-J replstr]\n"
613 "             [-L number] [-n number [-x] [-P maxprocs] [-s size]\n"
614 "             [utility [argument ...]]\n");
615 	exit(1);
616 }
617