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