]> git.saurik.com Git - apple/shell_cmds.git/blob - xargs/xargs.c
shell_cmds-17.1.tar.gz
[apple/shell_cmds.git] / xargs / xargs.c
1 /* $NetBSD: xargs.c,v 1.10 1998/04/14 09:26:33 fair Exp $ */
2
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. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __COPYRIGHT("@(#) Copyright (c) 1990, 1993\n\
42 The Regents of the University of California. All rights reserved.\n");
43 #endif /* not lint */
44
45 #ifndef lint
46 #if 0
47 static char sccsid[] = "@(#)xargs.c 8.1 (Berkeley) 6/6/93";
48 #endif
49 __RCSID("$NetBSD: xargs.c,v 1.10 1998/04/14 09:26:33 fair Exp $");
50 #endif /* not lint */
51
52 #include <sys/types.h>
53 #include <sys/wait.h>
54 #include <errno.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59 #include <limits.h>
60 #include <locale.h>
61 #include <signal.h>
62 #include <err.h>
63 #include "pathnames.h"
64
65 int tflag, zflag, rval;
66
67 void run __P((char **));
68 int main __P((int, char **));
69 void usage __P((void));
70
71 int
72 main(argc, argv)
73 int argc;
74 char **argv;
75 {
76 int ch;
77 char *p, *bbp, *ebp, **bxp, **exp, **xp;
78 int cnt, indouble, insingle, nargs, nflag, nline, xflag;
79 char **av, *argp;
80
81 setlocale(LC_ALL, "");
82
83 /*
84 * POSIX.2 limits the exec line length to ARG_MAX - 2K. Running that
85 * caused some E2BIG errors, so it was changed to ARG_MAX - 4K. Given
86 * that the smallest argument is 2 bytes in length, this means that
87 * the number of arguments is limited to:
88 *
89 * (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2.
90 *
91 * We arbitrarily limit the number of arguments to 5000. This is
92 * allowed by POSIX.2 as long as the resulting minimum exec line is
93 * at least LINE_MAX. Realloc'ing as necessary is possible, but
94 * probably not worthwhile.
95 */
96 nargs = 5000;
97 nline = ARG_MAX - 4 * 1024;
98 nflag = xflag = 0;
99 while ((ch = getopt(argc, argv, "0n:s:tx")) != -1)
100 switch(ch) {
101 case '0':
102 zflag = 1;
103 break;
104 case 'n':
105 nflag = 1;
106 if ((nargs = atoi(optarg)) <= 0)
107 errx(1, "illegal argument count");
108 break;
109 case 's':
110 nline = atoi(optarg);
111 break;
112 case 't':
113 tflag = 1;
114 break;
115 case 'x':
116 xflag = 1;
117 break;
118 case '?':
119 default:
120 usage();
121 }
122 argc -= optind;
123 argv += optind;
124
125 if (xflag && !nflag)
126 usage();
127
128 /*
129 * Allocate pointers for the utility name, the utility arguments,
130 * the maximum arguments to be read from stdin and the trailing
131 * NULL.
132 */
133 if (!(av = bxp =
134 malloc((u_int)(1 + argc + nargs + 1) * sizeof(char **))))
135 err(1, "malloc");
136
137 /*
138 * Use the user's name for the utility as argv[0], just like the
139 * shell. Echo is the default. Set up pointers for the user's
140 * arguments.
141 */
142 if (!*argv)
143 cnt = strlen(*bxp++ = _PATH_ECHO);
144 else {
145 cnt = 0;
146 do {
147 cnt += strlen(*bxp++ = *argv) + 1;
148 } while (*++argv);
149 }
150
151 /*
152 * Set up begin/end/traversing pointers into the array. The -n
153 * count doesn't include the trailing NULL pointer, so the malloc
154 * added in an extra slot.
155 */
156 exp = (xp = bxp) + nargs;
157
158 /*
159 * Allocate buffer space for the arguments read from stdin and the
160 * trailing NULL. Buffer space is defined as the default or specified
161 * space, minus the length of the utility name and arguments. Set up
162 * begin/end/traversing pointers into the array. The -s count does
163 * include the trailing NULL, so the malloc didn't add in an extra
164 * slot.
165 */
166 nline -= cnt;
167 if (nline <= 0)
168 errx(1, "insufficient space for command");
169
170 if (!(bbp = malloc((u_int)nline + 1)))
171 err(1, "malloc");
172 ebp = (argp = p = bbp) + nline - 1;
173
174 for (insingle = indouble = 0;;)
175 switch(ch = getchar()) {
176 case EOF:
177 /* No arguments since last exec. */
178 if (p == bbp)
179 exit(rval);
180
181 /* Nothing since end of last argument. */
182 if (argp == p) {
183 *xp = NULL;
184 run(av);
185 exit(rval);
186 }
187 goto arg1;
188 case ' ':
189 case '\t':
190 /* Quotes escape tabs and spaces. */
191 if (insingle || indouble || zflag)
192 goto addch;
193 goto arg2;
194 case '\0':
195 if (zflag)
196 goto arg2;
197 goto addch;
198 case '\n':
199 if (zflag)
200 goto addch;
201 /* Empty lines are skipped. */
202 if (argp == p)
203 continue;
204
205 /* Quotes do not escape newlines. */
206 arg1: if (insingle || indouble)
207 errx(1, "unterminated quote");
208
209 arg2: *p = '\0';
210 *xp++ = argp;
211
212 /*
213 * If max'd out on args or buffer, or reached EOF,
214 * run the command. If xflag and max'd out on buffer
215 * but not on args, object.
216 */
217 if (xp == exp || p == ebp || ch == EOF) {
218 if (xflag && xp != exp && p == ebp)
219 errx(1, "insufficient space for arguments");
220 *xp = NULL;
221 run(av);
222 if (ch == EOF)
223 exit(rval);
224 p = bbp;
225 xp = bxp;
226 } else
227 ++p;
228 argp = p;
229 break;
230 case '\'':
231 if (indouble || zflag)
232 goto addch;
233 insingle = !insingle;
234 break;
235 case '"':
236 if (insingle || zflag)
237 goto addch;
238 indouble = !indouble;
239 break;
240 case '\\':
241 if (zflag)
242 goto addch;
243 /* Backslash escapes anything, is escaped by quotes. */
244 if (!insingle && !indouble && (ch = getchar()) == EOF)
245 errx(1, "backslash at EOF");
246 /* FALLTHROUGH */
247 default:
248 addch: if (p < ebp) {
249 *p++ = ch;
250 break;
251 }
252
253 /* If only one argument, not enough buffer space. */
254 if (bxp == xp)
255 errx(1, "insufficient space for argument");
256 /* Didn't hit argument limit, so if xflag object. */
257 if (xflag)
258 errx(1, "insufficient space for arguments");
259
260 *xp = NULL;
261 run(av);
262 xp = bxp;
263 cnt = ebp - argp;
264 memmove(bbp, argp, cnt);
265 p = (argp = bbp) + cnt;
266 *p++ = ch;
267 break;
268 }
269 /* NOTREACHED */
270 }
271
272 void
273 run(argv)
274 char **argv;
275 {
276 volatile int noinvoke;
277 char **p;
278 pid_t pid;
279 int status;
280
281 if (tflag) {
282 (void)fprintf(stderr, "%s", *argv);
283 for (p = argv + 1; *p; ++p)
284 (void)fprintf(stderr, " %s", *p);
285 (void)fprintf(stderr, "\n");
286 (void)fflush(stderr);
287 }
288 noinvoke = 0;
289 switch(pid = vfork()) {
290 case -1:
291 err(1, "vfork");
292 case 0:
293 execvp(argv[0], argv);
294 noinvoke = (errno == ENOENT) ? 127 : 126;
295 warn("%s", argv[0]);;
296 _exit(1);
297 }
298 pid = waitpid(pid, &status, 0);
299 if (pid == -1)
300 err(1, "waitpid");
301
302 /*
303 * If we couldn't invoke the utility or the utility didn't exit
304 * properly, quit with 127 or 126 respectively.
305 */
306 if (noinvoke)
307 exit(noinvoke);
308
309 /*
310 * According to POSIX, we have to exit if the utility exits with
311 * a 255 status, or is interrupted by a signal. xargs is allowed
312 * to return any exit status between 1 and 125 in these cases, but
313 * we'll use 124 and 125, the same values used by GNU xargs.
314 */
315 if (WIFEXITED(status)) {
316 if (WEXITSTATUS (status) == 255) {
317 warnx ("%s exited with status 255", argv[0]);
318 exit(124);
319 } else if (WEXITSTATUS (status) != 0) {
320 rval = 123;
321 }
322 } else if (WIFSIGNALED (status)) {
323 #ifdef __APPLE__
324 if (WTERMSIG(status) < NSIG) {
325 #else
326 if (WTERMSIG(status) < _NSIG) {
327 #endif
328 warnx("%s terminated by SIG%s", argv[0],
329 sys_signame[WTERMSIG(status)]);
330 } else {
331 warnx("%s terminated by signal %d", argv[0],
332 WTERMSIG(status));
333 }
334 exit(125);
335 }
336 }
337
338 void
339 usage()
340 {
341 (void)fprintf(stderr,
342 "usage: xargs [-0t] [-n number [-x]] [-s size] [utility [argument ...]]\n");
343 exit(1);
344 }