xref: /freebsd/usr.bin/xargs/xargs.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
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. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * $xMach: xargs.c,v 1.6 2002/02/23 05:27:47 tim Exp $
35  */
36 
37 #if 0
38 #ifndef lint
39 static const char copyright[] =
40 "@(#) Copyright (c) 1990, 1993\n\
41 	The Regents of the University of California.  All rights reserved.\n";
42 #endif /* not lint */
43 
44 #ifndef lint
45 static char sccsid[] = "@(#)xargs.c	8.1 (Berkeley) 6/6/93";
46 #endif /* not lint */
47 #endif
48 #include <sys/cdefs.h>
49 #include <sys/types.h>
50 #include <sys/wait.h>
51 #include <sys/time.h>
52 #include <sys/limits.h>
53 #include <sys/resource.h>
54 #include <err.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <getopt.h>
58 #include <langinfo.h>
59 #include <locale.h>
60 #include <paths.h>
61 #include <regex.h>
62 #include <stdbool.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <unistd.h>
67 
68 #include "pathnames.h"
69 
70 static void	parse_input(int, char *[]);
71 static void	prerun(int, char *[]);
72 static int	prompt(void);
73 static void	run(char **);
74 static void	usage(void);
75 bool		strnsubst(char **, const char *, const char *, size_t);
76 static pid_t	xwait(int block, int *status);
77 static void	xexit(const char *, const int);
78 static void	waitchildren(const char *, int);
79 static void	pids_init(void);
80 static int	pids_empty(void);
81 static int	pids_full(void);
82 static void	pids_add(pid_t pid);
83 static int	pids_remove(pid_t pid);
84 static int	findslot(pid_t pid);
85 static int	findfreeslot(void);
86 static void	clearslot(int slot);
87 
88 static char echo[] = _PATH_ECHO;
89 static char **av, **bxp, **ep, **endxp, **xp;
90 static char *argp, *bbp, *ebp, *inpline, *p, *replstr;
91 static const char *eofstr;
92 static long eoflen;
93 static int count, insingle, indouble, oflag, pflag, tflag, Rflag, rval, zflag;
94 static int cnt, Iflag, jfound, Lflag, Sflag, wasquoted, xflag;
95 static int curprocs, maxprocs;
96 static pid_t *childpids;
97 
98 static volatile int childerr;
99 
100 extern char **environ;
101 
102 static const char *optstr = "+0E:I:J:L:n:oP:pR:S:s:rtx";
103 
104 static const struct option long_options[] =
105 {
106 	{"exit",		no_argument,		NULL,	'x'},
107 	{"interactive",		no_argument,		NULL,	'p'},
108 	{"max-args",		required_argument,	NULL,	'n'},
109 	{"max-chars",		required_argument,	NULL,	's'},
110 	{"max-procs",		required_argument,	NULL,	'P'},
111 	{"no-run-if-empty",	no_argument,		NULL,	'r'},
112 	{"null",		no_argument,		NULL,	'0'},
113 	{"verbose",		no_argument,		NULL,	't'},
114 
115 	{NULL,			no_argument,		NULL,	0},
116 };
117 
118 int
119 main(int argc, char *argv[])
120 {
121 	long arg_max;
122 	int ch, Jflag, nargs, nflag, nline;
123 	size_t linelen;
124 	struct rlimit rl;
125 	const char *errstr;
126 
127 	inpline = replstr = NULL;
128 	ep = environ;
129 	eofstr = "";
130 	eoflen = 0;
131 	Jflag = nflag = 0;
132 
133 	(void)setlocale(LC_ALL, "");
134 
135 	/*
136 	 * POSIX.2 limits the exec line length to ARG_MAX - 2K.  Running that
137 	 * caused some E2BIG errors, so it was changed to ARG_MAX - 4K.  Given
138 	 * that the smallest argument is 2 bytes in length, this means that
139 	 * the number of arguments is limited to:
140 	 *
141 	 *	 (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2.
142 	 *
143 	 * We arbitrarily limit the number of arguments to 5000.  This is
144 	 * allowed by POSIX.2 as long as the resulting minimum exec line is
145 	 * at least LINE_MAX.  Realloc'ing as necessary is possible, but
146 	 * probably not worthwhile.
147 	 */
148 	nargs = 5000;
149 	if ((arg_max = sysconf(_SC_ARG_MAX)) == -1)
150 		errx(1, "sysconf(_SC_ARG_MAX) failed");
151 	nline = arg_max - 4 * 1024;
152 	while (*ep != NULL) {
153 		/* 1 byte for each '\0' */
154 		nline -= strlen(*ep++) + 1 + sizeof(*ep);
155 	}
156 	maxprocs = 1;
157 	while ((ch = getopt_long(argc, argv, optstr, long_options, NULL)) != -1)
158 		switch (ch) {
159 		case 'E':
160 			eofstr = optarg;
161 			eoflen = strlen(eofstr);
162 			break;
163 		case 'I':
164 			Jflag = 0;
165 			Iflag = 1;
166 			Lflag = 1;
167 			replstr = optarg;
168 			break;
169 		case 'J':
170 			Iflag = 0;
171 			Jflag = 1;
172 			replstr = optarg;
173 			break;
174 		case 'L':
175 			Lflag = (int)strtonum(optarg, 1, INT_MAX, &errstr);
176 			if (errstr)
177 				errx(1, "-%c %s: %s", ch, optarg, errstr);
178 			break;
179 		case 'n':
180 			nflag = 1;
181 			nargs = (int)strtonum(optarg, 1, INT_MAX, &errstr);
182 			if (errstr)
183 				errx(1, "-%c %s: %s", ch, optarg, errstr);
184 			break;
185 		case 'o':
186 			oflag = 1;
187 			break;
188 		case 'P':
189 			maxprocs = (int)strtonum(optarg, 0, INT_MAX, &errstr);
190 			if (errstr)
191 				errx(1, "-%c %s: %s", ch, optarg, errstr);
192 			if (getrlimit(RLIMIT_NPROC, &rl) != 0)
193 				errx(1, "getrlimit failed");
194 			if (maxprocs == 0 || maxprocs > rl.rlim_cur)
195 				maxprocs = rl.rlim_cur;
196 			break;
197 		case 'p':
198 			pflag = 1;
199 			break;
200 		case 'R':
201 			Rflag = (int)strtonum(optarg, INT_MIN, INT_MAX, &errstr);
202 			if (errstr)
203 				errx(1, "-%c %s: %s", ch, optarg, errstr);
204 			if (!Rflag)
205 				errx(1, "-%c %s: %s", ch, optarg, "must be non-zero");
206 			break;
207 		case 'r':
208 			/* GNU compatibility */
209 			break;
210 		case 'S':
211 			Sflag = (int)strtonum(optarg, 0, INT_MAX, &errstr);
212 			if (errstr)
213 				errx(1, "-%c %s: %s", ch, optarg, errstr);
214 			break;
215 		case 's':
216 			nline = (int)strtonum(optarg, 0, INT_MAX, &errstr);
217 			if (errstr)
218 				errx(1, "-%c %s: %s", ch, optarg, errstr);
219 			break;
220 		case 't':
221 			tflag = 1;
222 			break;
223 		case 'x':
224 			xflag = 1;
225 			break;
226 		case '0':
227 			zflag = 1;
228 			break;
229 		case '?':
230 		default:
231 			usage();
232 	}
233 	argc -= optind;
234 	argv += optind;
235 
236 	if (!Iflag && Rflag)
237 		usage();
238 	if (!Iflag && Sflag)
239 		usage();
240 	if (Iflag && !Rflag)
241 		Rflag = 5;
242 	if (Iflag && !Sflag)
243 		Sflag = 255;
244 	if (xflag && !nflag)
245 		usage();
246 	if (Iflag || Lflag)
247 		xflag = 1;
248 	if (replstr != NULL && *replstr == '\0')
249 		errx(1, "replstr may not be empty");
250 
251 	pids_init();
252 
253 	/*
254 	 * Allocate pointers for the utility name, the utility arguments,
255 	 * the maximum arguments to be read from stdin and the trailing
256 	 * NULL.
257 	 */
258 	linelen = 1 + argc + (size_t)nargs + 1;
259 	if ((av = bxp = malloc(linelen * sizeof(char *))) == NULL)
260 		errx(1, "malloc failed");
261 
262 	/*
263 	 * Use the user's name for the utility as argv[0], just like the
264 	 * shell.  Echo is the default.  Set up pointers for the user's
265 	 * arguments.
266 	 */
267 	if (*argv == NULL)
268 		cnt = strlen(*bxp++ = echo);
269 	else {
270 		do {
271 			if (Jflag && strcmp(*argv, replstr) == 0) {
272 				char **avj;
273 				jfound = 1;
274 				argv++;
275 				for (avj = argv; *avj; avj++)
276 					cnt += strlen(*avj) + 1;
277 				break;
278 			}
279 			cnt += strlen(*bxp++ = *argv) + 1;
280 		} while (*++argv != NULL);
281 	}
282 
283 	/*
284 	 * Set up begin/end/traversing pointers into the array.  The -n
285 	 * count doesn't include the trailing NULL pointer, so the malloc
286 	 * added in an extra slot.
287 	 */
288 	endxp = (xp = bxp) + nargs;
289 
290 	/*
291 	 * Allocate buffer space for the arguments read from stdin and the
292 	 * trailing NULL.  Buffer space is defined as the default or specified
293 	 * space, minus the length of the utility name and arguments.  Set up
294 	 * begin/end/traversing pointers into the array.  The -s count does
295 	 * include the trailing NULL, so the malloc didn't add in an extra
296 	 * slot.
297 	 */
298 	nline -= cnt;
299 	if (nline <= 0)
300 		errx(1, "insufficient space for command");
301 
302 	if ((bbp = malloc((size_t)(nline + 1))) == NULL)
303 		errx(1, "malloc failed");
304 	ebp = (argp = p = bbp) + nline - 1;
305 	for (;;)
306 		parse_input(argc, argv);
307 }
308 
309 static void
310 parse_input(int argc, char *argv[])
311 {
312 	int ch, foundeof;
313 	char **avj;
314 
315 	foundeof = 0;
316 
317 	switch (ch = getchar()) {
318 	case EOF:
319 		/* No arguments since last exec. */
320 		if (p == bbp) {
321 			waitchildren(*av, 1);
322 			exit(rval);
323 		}
324 		goto arg1;
325 	case ' ':
326 	case '\t':
327 		/* Quotes escape tabs and spaces. */
328 		if (insingle || indouble || zflag)
329 			goto addch;
330 		goto arg2;
331 	case '\0':
332 		if (zflag) {
333 			/*
334 			 * Increment 'count', so that nulls will be treated
335 			 * as end-of-line, as well as end-of-argument.  This
336 			 * is needed so -0 works properly with -I and -L.
337 			 */
338 			count++;
339 			goto arg2;
340 		}
341 		goto addch;
342 	case '\n':
343 		if (zflag)
344 			goto addch;
345 		count++;	    /* Indicate end-of-line (used by -L) */
346 
347 		/* Quotes do not escape newlines. */
348 arg1:		if (insingle || indouble) {
349 			warnx("unterminated quote");
350 			xexit(*av, 1);
351 		}
352 arg2:
353 		foundeof = eoflen != 0 && p - argp == eoflen &&
354 		    strncmp(argp, eofstr, eoflen) == 0;
355 
356 		/* Do not make empty args unless they are quoted */
357 		if ((argp != p || wasquoted) && !foundeof) {
358 			*p++ = '\0';
359 			*xp++ = argp;
360 			if (Iflag) {
361 				size_t curlen;
362 
363 				if (inpline == NULL)
364 					curlen = 0;
365 				else {
366 					/*
367 					 * If this string is not zero
368 					 * length, append a space for
369 					 * separation before the next
370 					 * argument.
371 					 */
372 					if ((curlen = strlen(inpline)))
373 						strcat(inpline, " ");
374 				}
375 				curlen++;
376 				/*
377 				 * Allocate enough to hold what we will
378 				 * be holding in a second, and to append
379 				 * a space next time through, if we have
380 				 * to.
381 				 */
382 				inpline = realloc(inpline, curlen + 2 +
383 				    strlen(argp));
384 				if (inpline == NULL) {
385 					warnx("realloc failed");
386 					xexit(*av, 1);
387 				}
388 				if (curlen == 1)
389 					strcpy(inpline, argp);
390 				else
391 					strcat(inpline, argp);
392 			}
393 		}
394 
395 		/*
396 		 * If max'd out on args or buffer, or reached EOF,
397 		 * run the command.  If xflag and max'd out on buffer
398 		 * but not on args, object.  Having reached the limit
399 		 * of input lines, as specified by -L is the same as
400 		 * maxing out on arguments.
401 		 */
402 		if (xp == endxp || p > ebp || ch == EOF ||
403 		    (Lflag <= count && xflag) || foundeof) {
404 			if (xflag && xp != endxp && p > ebp) {
405 				warnx("insufficient space for arguments");
406 				xexit(*av, 1);
407 			}
408 			if (jfound) {
409 				for (avj = argv; *avj; avj++)
410 					*xp++ = *avj;
411 			}
412 			prerun(argc, av);
413 			if (ch == EOF || foundeof) {
414 				waitchildren(*av, 1);
415 				exit(rval);
416 			}
417 			p = bbp;
418 			xp = bxp;
419 			count = 0;
420 		}
421 		argp = p;
422 		wasquoted = 0;
423 		break;
424 	case '\'':
425 		if (indouble || zflag)
426 			goto addch;
427 		insingle = !insingle;
428 		wasquoted = 1;
429 		break;
430 	case '"':
431 		if (insingle || zflag)
432 			goto addch;
433 		indouble = !indouble;
434 		wasquoted = 1;
435 		break;
436 	case '\\':
437 		if (zflag)
438 			goto addch;
439 		/* Backslash escapes anything, is escaped by quotes. */
440 		if (!insingle && !indouble && (ch = getchar()) == EOF) {
441 			warnx("backslash at EOF");
442 			xexit(*av, 1);
443 		}
444 		/* FALLTHROUGH */
445 	default:
446 addch:		if (p < ebp) {
447 			*p++ = ch;
448 			break;
449 		}
450 
451 		/* If only one argument, not enough buffer space. */
452 		if (bxp == xp) {
453 			warnx("insufficient space for argument");
454 			xexit(*av, 1);
455 		}
456 		/* Didn't hit argument limit, so if xflag object. */
457 		if (xflag) {
458 			warnx("insufficient space for arguments");
459 			xexit(*av, 1);
460 		}
461 
462 		if (jfound) {
463 			for (avj = argv; *avj; avj++)
464 				*xp++ = *avj;
465 		}
466 		prerun(argc, av);
467 		xp = bxp;
468 		cnt = ebp - argp;
469 		memcpy(bbp, argp, (size_t)cnt);
470 		p = (argp = bbp) + cnt;
471 		*p++ = ch;
472 		break;
473 	}
474 }
475 
476 /*
477  * Do things necessary before run()'ing, such as -I substitution,
478  * and then call run().
479  */
480 static void
481 prerun(int argc, char *argv[])
482 {
483 	char **tmp, **tmp2, **avj;
484 	int repls;
485 
486 	repls = Rflag;
487 
488 	if (argc == 0 || repls == 0) {
489 		*xp = NULL;
490 		run(argv);
491 		return;
492 	}
493 
494 	avj = argv;
495 
496 	/*
497 	 * Allocate memory to hold the argument list, and
498 	 * a NULL at the tail.
499 	 */
500 	tmp = malloc((argc + 1) * sizeof(char *));
501 	if (tmp == NULL) {
502 		warnx("malloc failed");
503 		xexit(*argv, 1);
504 	}
505 	tmp2 = tmp;
506 
507 	/*
508 	 * Save the first argument and iterate over it, we
509 	 * cannot do strnsubst() to it.
510 	 */
511 	if ((*tmp++ = strdup(*avj++)) == NULL) {
512 		warnx("strdup failed");
513 		xexit(*argv, 1);
514 	}
515 
516 	/*
517 	 * For each argument to utility, if we have not used up
518 	 * the number of replacements we are allowed to do, and
519 	 * if the argument contains at least one occurrence of
520 	 * replstr, call strnsubst(), else just save the string.
521 	 * Iterations over elements of avj and tmp are done
522 	 * where appropriate.
523 	 */
524 	while (--argc) {
525 		*tmp = *avj++;
526 		if (repls && strstr(*tmp, replstr) != NULL) {
527 			if (strnsubst(tmp++, replstr, inpline, (size_t)Sflag)) {
528 				warnx("command line cannot be assembled, too long");
529 				xexit(*argv, 1);
530 			}
531 			if (repls > 0)
532 				repls--;
533 		} else {
534 			if ((*tmp = strdup(*tmp)) == NULL) {
535 				warnx("strdup failed");
536 				xexit(*argv, 1);
537 			}
538 			tmp++;
539 		}
540 	}
541 
542 	/*
543 	 * Run it.
544 	 */
545 	*tmp = NULL;
546 	run(tmp2);
547 
548 	/*
549 	 * Walk from the tail to the head, free along the way.
550 	 */
551 	for (; tmp2 != tmp; tmp--)
552 		free(*tmp);
553 	/*
554 	 * Now free the list itself.
555 	 */
556 	free(tmp2);
557 
558 	/*
559 	 * Free the input line buffer, if we have one.
560 	 */
561 	if (inpline != NULL) {
562 		free(inpline);
563 		inpline = NULL;
564 	}
565 }
566 
567 static void
568 run(char **argv)
569 {
570 	pid_t pid;
571 	int fd;
572 	char **avec;
573 
574 	/*
575 	 * If the user wants to be notified of each command before it is
576 	 * executed, notify them.  If they want the notification to be
577 	 * followed by a prompt, then prompt them.
578 	 */
579 	if (tflag || pflag) {
580 		(void)fprintf(stderr, "%s", *argv);
581 		for (avec = argv + 1; *avec != NULL; ++avec)
582 			(void)fprintf(stderr, " %s", *avec);
583 		/*
584 		 * If the user has asked to be prompted, do so.
585 		 */
586 		if (pflag)
587 			/*
588 			 * If they asked not to exec, return without execution
589 			 * but if they asked to, go to the execution.  If we
590 			 * could not open their tty, break the switch and drop
591 			 * back to -t behaviour.
592 			 */
593 			switch (prompt()) {
594 			case 0:
595 				return;
596 			case 1:
597 				goto exec;
598 			case 2:
599 				break;
600 			}
601 		(void)fprintf(stderr, "\n");
602 		(void)fflush(stderr);
603 	}
604 exec:
605 	childerr = 0;
606 	switch (pid = vfork()) {
607 	case -1:
608 		warn("vfork");
609 		xexit(*argv, 1);
610 	case 0:
611 		if (oflag) {
612 			if ((fd = open(_PATH_TTY, O_RDONLY)) == -1)
613 				err(1, "can't open /dev/tty");
614 		} else {
615 			fd = open(_PATH_DEVNULL, O_RDONLY);
616 		}
617 		if (fd > STDIN_FILENO) {
618 			if (dup2(fd, STDIN_FILENO) != 0)
619 				err(1, "can't dup2 to stdin");
620 			close(fd);
621 		}
622 		execvp(argv[0], argv);
623 		childerr = errno;
624 		_exit(1);
625 	}
626 	pids_add(pid);
627 	waitchildren(*argv, 0);
628 }
629 
630 /*
631  * Wait for a tracked child to exit and return its pid and exit status.
632  *
633  * Ignores (discards) all untracked child processes.
634  * Returns -1 and sets errno to ECHILD if no tracked children exist.
635  * If block is set, waits indefinitely for a child process to exit.
636  * If block is not set and no children have exited, returns 0 immediately.
637  */
638 static pid_t
639 xwait(int block, int *status) {
640 	pid_t pid;
641 
642 	if (pids_empty()) {
643 		errno = ECHILD;
644 		return (-1);
645 	}
646 
647 	while ((pid = waitpid(-1, status, block ? 0 : WNOHANG)) > 0)
648 		if (pids_remove(pid))
649 			break;
650 
651 	return (pid);
652 }
653 
654 static void
655 xexit(const char *name, const int exit_code) {
656 	waitchildren(name, 1);
657 	exit(exit_code);
658 }
659 
660 static void
661 waitchildren(const char *name, int waitall)
662 {
663 	pid_t pid;
664 	int status;
665 	int cause_exit = 0;
666 
667 	while ((pid = xwait(waitall || pids_full(), &status)) > 0) {
668 		/*
669 		 * If we couldn't invoke the utility or if utility exited
670 		 * because of a signal or with a value of 255, warn (per
671 		 * POSIX), and then wait until all other children have
672 		 * exited before exiting 1-125. POSIX requires us to stop
673 		 * reading if child exits because of a signal or with 255,
674 		 * but it does not require us to exit immediately; waiting
675 		 * is preferable to orphaning.
676 		 */
677 		if (childerr != 0 && cause_exit == 0) {
678 			errno = childerr;
679 			waitall = 1;
680 			cause_exit = errno == ENOENT ? 127 : 126;
681 			warn("%s", name);
682 		} else if (WIFSIGNALED(status)) {
683 			waitall = cause_exit = 1;
684 			warnx("%s: terminated with signal %d; aborting",
685 			    name, WTERMSIG(status));
686 		} else if (WEXITSTATUS(status) == 255) {
687 			waitall = cause_exit = 1;
688 			warnx("%s: exited with status 255; aborting", name);
689 		} else if (WEXITSTATUS(status))
690  			rval = 1;
691 	}
692 
693  	if (cause_exit)
694 		exit(cause_exit);
695 	if (pid == -1 && errno != ECHILD)
696 		err(1, "waitpid");
697 }
698 
699 #define	NOPID	(0)
700 
701 static void
702 pids_init(void)
703 {
704 	int i;
705 
706 	if ((childpids = malloc(maxprocs * sizeof(*childpids))) == NULL)
707 		errx(1, "malloc failed");
708 
709 	for (i = 0; i < maxprocs; i++)
710 		clearslot(i);
711 }
712 
713 static int
714 pids_empty(void)
715 {
716 
717 	return (curprocs == 0);
718 }
719 
720 static int
721 pids_full(void)
722 {
723 
724 	return (curprocs >= maxprocs);
725 }
726 
727 static void
728 pids_add(pid_t pid)
729 {
730 	int slot;
731 
732 	slot = findfreeslot();
733 	childpids[slot] = pid;
734 	curprocs++;
735 }
736 
737 static int
738 pids_remove(pid_t pid)
739 {
740 	int slot;
741 
742 	if ((slot = findslot(pid)) < 0)
743 		return (0);
744 
745 	clearslot(slot);
746 	curprocs--;
747 	return (1);
748 }
749 
750 static int
751 findfreeslot(void)
752 {
753 	int slot;
754 
755 	if ((slot = findslot(NOPID)) < 0)
756 		errx(1, "internal error: no free pid slot");
757 	return (slot);
758 }
759 
760 static int
761 findslot(pid_t pid)
762 {
763 	int slot;
764 
765 	for (slot = 0; slot < maxprocs; slot++)
766 		if (childpids[slot] == pid)
767 			return (slot);
768 	return (-1);
769 }
770 
771 static void
772 clearslot(int slot)
773 {
774 
775 	childpids[slot] = NOPID;
776 }
777 
778 /*
779  * Prompt the user about running a command.
780  */
781 static int
782 prompt(void)
783 {
784 	regex_t cre;
785 	size_t rsize;
786 	int match;
787 	char *response;
788 	FILE *ttyfp;
789 
790 	if ((ttyfp = fopen(_PATH_TTY, "r")) == NULL)
791 		return (2);	/* Indicate that the TTY failed to open. */
792 	(void)fprintf(stderr, "?...");
793 	(void)fflush(stderr);
794 	if ((response = fgetln(ttyfp, &rsize)) == NULL ||
795 	    regcomp(&cre, nl_langinfo(YESEXPR), REG_EXTENDED) != 0) {
796 		(void)fclose(ttyfp);
797 		return (0);
798 	}
799 	response[rsize - 1] = '\0';
800 	match = regexec(&cre, response, 0, NULL, 0);
801 	(void)fclose(ttyfp);
802 	regfree(&cre);
803 	return (match == 0);
804 }
805 
806 static void
807 usage(void)
808 {
809 
810 	fprintf(stderr,
811 "usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements] [-S replsize]]\n"
812 "             [-J replstr] [-L number] [-n number [-x]] [-P maxprocs]\n"
813 "             [-s size] [utility [argument ...]]\n");
814 	exit(1);
815 }
816