]>
git.cameronkatri.com Git - apple_cmds.git/blob - shell_cmds/xargs/xargs.c
2 * SPDX-License-Identifier: BSD-3-Clause
4 * Copyright (c) 1990, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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
34 * $xMach: xargs.c,v 1.6 2002/02/23 05:27:47 tim Exp $
39 static const char copyright
[] =
40 "@(#) Copyright (c) 1990, 1993\n\
41 The Regents of the University of California. All rights reserved.\n";
45 static char sccsid
[] = "@(#)xargs.c 8.1 (Berkeley) 6/6/93";
48 #include <sys/cdefs.h>
49 __FBSDID("$FreeBSD$");
51 #include <sys/types.h>
54 #include <sys/param.h>
55 #include <sys/resource.h>
68 #include "pathnames.h"
71 #include <get_compat.h>
73 #define COMPAT_MODE(a,b) (1)
74 #endif /* __APPLE__ */
76 static void parse_input(int, char *[]);
77 static void prerun(int, char *[]);
78 static int prompt(void);
79 static void run(char **);
80 static void usage(void);
81 void strnsubst(char **, const char *, const char *, size_t);
82 static pid_t
xwait(int block
, int *status
);
83 static void xexit(const char *, const int);
84 static void waitchildren(const char *, int);
85 static void pids_init(void);
86 static int pids_empty(void);
87 static int pids_full(void);
88 static void pids_add(pid_t pid
);
89 static int pids_remove(pid_t pid
);
90 static int findslot(pid_t pid
);
91 static int findfreeslot(void);
92 static void clearslot(int slot
);
94 static int last_was_newline
= 1;
95 static int last_was_blank
= 0;
97 static char echo
[] = _PATH_ECHO
;
98 static char **av
, **bxp
, **ep
, **endxp
, **xp
;
99 static char *argp
, *bbp
, *ebp
, *inpline
, *p
, *replstr
;
100 static const char *eofstr
;
101 static int count
, insingle
, indouble
, oflag
, pflag
, tflag
, Rflag
, rval
, zflag
;
102 static int cnt
, Iflag
, jfound
, Lflag
, Sflag
, wasquoted
, xflag
;
103 static int curprocs
, maxprocs
;
104 static size_t pad9314053
;
105 static pid_t
*childpids
;
107 static volatile int childerr
;
109 extern char **environ
;
112 main(int argc
, char *argv
[])
115 int ch
, Jflag
, nflag
, nline
;
122 inpline
= replstr
= NULL
;
127 (void)setlocale(LC_ALL
, "");
130 * POSIX.2 limits the exec line length to ARG_MAX - 2K. Running that
131 * caused some E2BIG errors, so it was changed to ARG_MAX - 4K. Given
132 * that the smallest argument is 2 bytes in length, this means that
133 * the number of arguments is limited to:
135 * (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2.
137 * We arbitrarily limit the number of arguments to 5000. This is
138 * allowed by POSIX.2 as long as the resulting minimum exec line is
139 * at least LINE_MAX. Realloc'ing as necessary is possible, but
140 * probably not worthwhile.
143 if ((arg_max
= sysconf(_SC_ARG_MAX
)) == -1)
144 errx(1, "sysconf(_SC_ARG_MAX) failed");
145 nline
= arg_max
- MAXPATHLEN
; /* for argv[0] from execvp() */
146 pad9314053
= sizeof(char *); /* reserve for string area rounding */
147 while (*ep
!= NULL
) {
148 /* 1 byte for each '\0' */
149 nline
-= strlen(*ep
++) + 1 + sizeof(*ep
);
153 while ((ch
= getopt(argc
, argv
, "0E:I:J:L:n:oP:pR:S:s:rtx")) != -1)
170 Lflag
= strtonum(optarg
, 0, INT_MAX
, &errstr
);
172 errx(1, "-L %s: %s", optarg
, errstr
);
173 if (COMPAT_MODE("bin/xargs", "Unix2003")) {
174 nflag
= 0; /* Override */
180 if ((nargs
= strtol(optarg
, NULL
, 10)) <= 0)
181 errx(1, "illegal argument count");
182 if (COMPAT_MODE("bin/xargs", "Unix2003")) {
183 Lflag
= 0; /* Override */
190 maxprocs
= strtonum(optarg
, 0, INT_MAX
, &errstr
);
192 errx(1, "-P %s: %s", optarg
, errstr
);
193 if (getrlimit(RLIMIT_NPROC
, &rl
) != 0)
194 errx(1, "getrlimit failed");
195 if (maxprocs
== 0 || maxprocs
> rl
.rlim_cur
)
196 maxprocs
= rl
.rlim_cur
;
202 Rflag
= strtol(optarg
, &endptr
, 10);
204 errx(1, "replacements must be a number");
207 /* GNU compatibility */
210 Sflag
= strtoul(optarg
, &endptr
, 10);
212 errx(1, "replsize must be a number");
215 nline
= strtonum(optarg
, 0, INT_MAX
, &errstr
);
217 errx(1, "-s %s: %s", optarg
, errstr
);
218 pad9314053
= 0; /* assume the -s value is valid */
248 if (replstr
!= NULL
&& *replstr
== '\0')
249 errx(1, "replstr may not be empty");
254 * Allocate pointers for the utility name, the utility arguments,
255 * the maximum arguments to be read from stdin and the trailing
258 linelen
= 1 + argc
+ nargs
+ 1;
259 if ((av
= bxp
= malloc(linelen
* sizeof(char *))) == NULL
)
260 errx(1, "malloc failed");
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
268 cnt
= strlen(*bxp
++ = echo
) + pad9314053
;
271 if (Jflag
&& strcmp(*argv
, replstr
) == 0) {
275 for (avj
= argv
; *avj
; avj
++)
276 cnt
+= strlen(*avj
) + 1 + pad9314053
;
279 cnt
+= strlen(*bxp
++ = *argv
) + 1 + pad9314053
;
280 } while (*++argv
!= NULL
);
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.
288 endxp
= (xp
= bxp
) + nargs
;
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
300 errx(1, "insufficient space for command");
302 if ((bbp
= malloc((size_t)(nline
+ 1))) == NULL
)
303 errx(1, "malloc failed");
304 ebp
= (argp
= p
= bbp
) + nline
- 1;
306 parse_input(argc
, argv
);
310 parse_input(int argc
, char *argv
[])
314 int last_was_backslashed
= 0;
318 switch (ch
= getchar()) {
320 /* No arguments since last exec. */
327 /* Quotes escape tabs and spaces. */
328 if (insingle
|| indouble
|| zflag
)
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.
345 if (COMPAT_MODE("bin/xargs", "Unix2003")) {
346 if (last_was_newline
) {
347 /* don't count empty line */
350 if (!last_was_blank
) {
351 /* only count if NOT continuation line */
357 last_was_newline
= 1;
359 /* Quotes do not escape newlines. */
360 arg1
: if (insingle
|| indouble
) {
361 warnx("unterminated quote");
365 foundeof
= *eofstr
!= '\0' &&
366 strncmp(argp
, eofstr
, p
- argp
) == 0;
369 /* 6591323: -I specifies that it processes the entire line,
370 * so only recognize eofstr at the end of a line. */
371 if (Iflag
&& !last_was_newline
)
374 /* 6591323: Essentially the same as the EOF handling above. */
375 if (foundeof
&& (p
- strlen(eofstr
) == bbp
)) {
376 waitchildren(*argv
, 1);
381 /* Do not make empty args unless they are quoted */
382 if ((argp
!= p
|| wasquoted
) && !foundeof
) {
392 * If this string is not zero
393 * length, append a space for
394 * separation before the next
397 if ((curlen
= strlen(inpline
)))
398 strcat(inpline
, " ");
402 * Allocate enough to hold what we will
403 * be holding in a second, and to append
404 * a space next time through, if we have
407 inpline
= realloc(inpline
, curlen
+ 2 +
409 if (inpline
== NULL
) {
410 warnx("realloc failed");
414 strcpy(inpline
, argp
);
416 strcat(inpline
, argp
);
421 * If max'd out on args or buffer, or reached EOF,
422 * run the command. If xflag and max'd out on buffer
423 * but not on args, object. Having reached the limit
424 * of input lines, as specified by -L is the same as
425 * maxing out on arguments.
427 if (xp
== endxp
|| p
+ (count
* pad9314053
) > ebp
|| ch
== EOF
||
428 (Lflag
<= count
&& xflag
) || foundeof
) {
429 if (xflag
&& xp
!= endxp
&& p
+ (count
* pad9314053
) > ebp
) {
430 warnx("insufficient space for arguments");
434 for (avj
= argv
; *avj
; avj
++)
438 if (ch
== EOF
|| foundeof
)
448 if (indouble
|| zflag
)
450 insingle
= !insingle
;
454 if (insingle
|| zflag
)
456 indouble
= !indouble
;
460 last_was_backslashed
= 1;
463 /* Backslash escapes anything, is escaped by quotes. */
464 if (!insingle
&& !indouble
&& (ch
= getchar()) == EOF
) {
465 warnx("backslash at EOF");
470 addch
: if (p
< ebp
) {
475 /* If only one argument, not enough buffer space. */
477 warnx("insufficient space for argument");
480 /* Didn't hit argument limit, so if xflag object. */
482 warnx("insufficient space for arguments");
487 for (avj
= argv
; *avj
; avj
++)
493 memcpy(bbp
, argp
, (size_t)cnt
);
494 p
= (argp
= bbp
) + cnt
;
500 if (ch
!= '\n' || last_was_backslashed
)
501 last_was_newline
= 0;
505 * Do things necessary before run()'ing, such as -I substitution,
506 * and then call run().
509 prerun(int argc
, char *argv
[])
511 char **tmp
, **tmp2
, **avj
;
516 if (argc
== 0 || repls
== 0) {
525 * Allocate memory to hold the argument list, and
526 * a NULL at the tail.
528 tmp
= malloc((argc
+ 1) * sizeof(char *));
530 warnx("malloc failed");
536 * Save the first argument and iterate over it, we
537 * cannot do strnsubst() to it.
539 if ((*tmp
++ = strdup(*avj
++)) == NULL
) {
540 warnx("strdup failed");
545 * For each argument to utility, if we have not used up
546 * the number of replacements we are allowed to do, and
547 * if the argument contains at least one occurrence of
548 * replstr, call strnsubst(), else just save the string.
549 * Iterations over elements of avj and tmp are done
554 if (repls
&& strstr(*tmp
, replstr
) != NULL
) {
555 strnsubst(tmp
++, replstr
, inpline
, (size_t)Sflag
);
559 if ((*tmp
= strdup(*tmp
)) == NULL
) {
560 warnx("strdup failed");
574 * Walk from the tail to the head, free along the way.
576 for (; tmp2
!= tmp
; tmp
--)
579 * Now free the list itself.
584 * Free the input line buffer, if we have one.
586 if (inpline
!= NULL
) {
600 * If the user wants to be notified of each command before it is
601 * executed, notify them. If they want the notification to be
602 * followed by a prompt, then prompt them.
604 if (tflag
|| pflag
) {
605 (void)fprintf(stderr
, "%s", *argv
);
606 for (avec
= argv
+ 1; *avec
!= NULL
; ++avec
)
607 (void)fprintf(stderr
, " %s", *avec
);
609 * If the user has asked to be prompted, do so.
613 * If they asked not to exec, return without execution
614 * but if they asked to, go to the execution. If we
615 * could not open their tty, break the switch and drop
616 * back to -t behaviour.
626 (void)fprintf(stderr
, "\n");
627 (void)fflush(stderr
);
631 switch (pid
= vfork()) {
637 if ((fd
= open(_PATH_TTY
, O_RDONLY
)) == -1)
638 err(1, "can't open /dev/tty");
640 fd
= open(_PATH_DEVNULL
, O_RDONLY
);
642 if (fd
> STDIN_FILENO
) {
643 if (dup2(fd
, STDIN_FILENO
) != 0)
644 err(1, "can't dup2 to stdin");
647 execvp(argv
[0], argv
);
652 waitchildren(*argv
, 0);
656 * Wait for a tracked child to exit and return its pid and exit status.
658 * Ignores (discards) all untracked child processes.
659 * Returns -1 and sets errno to ECHILD if no tracked children exist.
660 * If block is set, waits indefinitely for a child process to exit.
661 * If block is not set and no children have exited, returns 0 immediately.
664 xwait(int block
, int *status
) {
672 while ((pid
= waitpid(-1, status
, block
? 0 : WNOHANG
)) > 0)
673 if (pids_remove(pid
))
680 xexit(const char *name
, const int exit_code
) {
681 waitchildren(name
, 1);
686 waitchildren(const char *name
, int waitall
)
692 while ((pid
= xwait(waitall
|| pids_full(), &status
)) > 0) {
694 * If we couldn't invoke the utility or if utility exited
695 * because of a signal or with a value of 255, warn (per
696 * POSIX), and then wait until all other children have
697 * exited before exiting 1-125. POSIX requires us to stop
698 * reading if child exits because of a signal or with 255,
699 * but it does not require us to exit immediately; waiting
700 * is preferable to orphaning.
702 if (childerr
!= 0 && cause_exit
== 0) {
705 cause_exit
= errno
== ENOENT
? 127 : 126;
707 } else if (WIFSIGNALED(status
)) {
708 waitall
= cause_exit
= 1;
709 warnx("%s: terminated with signal %d; aborting",
710 name
, WTERMSIG(status
));
711 } else if (WEXITSTATUS(status
) == 255) {
712 waitall
= cause_exit
= 1;
713 warnx("%s: exited with status 255; aborting", name
);
714 } else if (WEXITSTATUS(status
))
720 if (pid
== -1 && errno
!= ECHILD
)
731 if ((childpids
= malloc(maxprocs
* sizeof(*childpids
))) == NULL
)
732 errx(1, "malloc failed");
734 for (i
= 0; i
< maxprocs
; i
++)
742 return (curprocs
== 0);
749 return (curprocs
>= maxprocs
);
757 slot
= findfreeslot();
758 childpids
[slot
] = pid
;
763 pids_remove(pid_t pid
)
767 if ((slot
= findslot(pid
)) < 0)
780 if ((slot
= findslot(NOPID
)) < 0)
781 errx(1, "internal error: no free pid slot");
790 for (slot
= 0; slot
< maxprocs
; slot
++)
791 if (childpids
[slot
] == pid
)
800 childpids
[slot
] = NOPID
;
804 * Prompt the user about running a command.
815 if ((ttyfp
= fopen(_PATH_TTY
, "r")) == NULL
)
816 return (2); /* Indicate that the TTY failed to open. */
817 (void)fprintf(stderr
, "?...");
818 (void)fflush(stderr
);
819 if ((response
= fgetln(ttyfp
, &rsize
)) == NULL
||
820 regcomp(&cre
, nl_langinfo(YESEXPR
), REG_BASIC
) != 0) {
824 response
[rsize
- 1] = '\0';
825 match
= regexec(&cre
, response
, 0, NULL
, 0);
836 "usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements] [-S replsize]]\n"
837 " [-J replstr] [-L number] [-n number [-x]] [-P maxprocs]\n"
838 " [-s size] [utility [argument ...]]\n");