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