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