source: trunk/minix/commands/simple/xargs.c@ 20

Last change on this file since 20 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 10.4 KB
Line 
1/*-
2 * Copyright (c) 1990 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * John B. Roll Jr.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38char copyright[] =
39"@(#) Copyright (c) 1990 The Regents of the University of California.\n\
40 All rights reserved.\n";
41#endif /* not lint */
42
43#ifndef lint
44static char sccsid[] = "@(#)xargs.c 5.11 (Berkeley) 6/19/91";
45#endif /* not lint */
46
47#include <sys/types.h>
48#include <sys/wait.h>
49#include <errno.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <unistd.h>
54#include <limits.h>
55#include <fcntl.h>
56#include <stdarg.h>
57#if __minix
58#define _PATH_ECHO "/bin/echo"
59#else
60#include "pathnames.h"
61#endif
62
63#ifndef ARG_MAX
64#define ARG_MAX (sizeof(int) == 2 ? 4096 : 128 * 1024)
65#endif
66
67int exit_status = 0;
68int tflag;
69void err(const char *, ...);
70void run(char **argv);
71void usage(void);
72
73int main(int argc, char **argv)
74{
75 extern int optind;
76 extern char *optarg;
77 register int ch;
78 register char *p, *bbp, *ebp, **bxp, **exp, **xp;
79 int cnt, indouble, insingle, nargs, nflag, nline, xflag, zflag;
80 char **av, *argp;
81
82 /*
83 * POSIX.2 limits the exec line length to ARG_MAX - 2K. Running that
84 * caused some E2BIG errors, so it was changed to ARG_MAX - 4K. Given
85 * that the smallest argument is 2 bytes in length, this means that
86 * the number of arguments is limited to:
87 *
88 * (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2.
89 *
90 * We arbitrarily limit the number of arguments to 5000. This is
91 * allowed by POSIX.2 as long as the resulting minimum exec line is
92 * at least LINE_MAX. Realloc'ing as necessary is possible, but
93 * probably not worthwhile.
94 */
95#if !__minix || __minix_vmd
96 nargs = 5000;
97 nline = ARG_MAX - 4 * 1024;
98#else
99 /* Things are more cramped under standard Minix. */
100 nargs = 80 * sizeof(int);
101 nline = ARG_MAX - 512 * sizeof(int);
102#endif
103 nflag = xflag = zflag = 0;
104 while ((ch = getopt(argc, argv, "n:s:tx0")) != EOF)
105 switch(ch) {
106 case 'n':
107 nflag = 1;
108 if ((nargs = atoi(optarg)) <= 0)
109 err("illegal argument count");
110 break;
111 case 's':
112 nline = atoi(optarg);
113 break;
114 case 't':
115 tflag = 1;
116 break;
117 case 'x':
118 xflag = 1;
119 break;
120 case '0':
121 zflag = 1;
122 break;
123 case '?':
124 default:
125 usage();
126 }
127 argc -= optind;
128 argv += optind;
129
130 if (xflag && !nflag)
131 usage();
132
133 /*
134 * Allocate pointers for the utility name, the utility arguments,
135 * the maximum arguments to be read from stdin and the trailing
136 * NULL.
137 */
138 if (!(av = bxp =
139 malloc((u_int)(1 + argc + nargs + 1) * sizeof(char **))))
140 err("%s", strerror(errno));
141
142 /*
143 * Use the user's name for the utility as argv[0], just like the
144 * shell. Echo is the default. Set up pointers for the user's
145 * arguments.
146 */
147 if (!*argv)
148 cnt = strlen(*bxp++ = _PATH_ECHO);
149 else {
150 cnt = 0;
151 do {
152 cnt += strlen(*bxp++ = *argv) + 1;
153 } while (*++argv);
154 }
155
156 /*
157 * Set up begin/end/traversing pointers into the array. The -n
158 * count doesn't include the trailing NULL pointer, so the malloc
159 * added in an extra slot.
160 */
161 exp = (xp = bxp) + nargs;
162
163 /*
164 * Allocate buffer space for the arguments read from stdin and the
165 * trailing NULL. Buffer space is defined as the default or specified
166 * space, minus the length of the utility name and arguments. Set up
167 * begin/end/traversing pointers into the array. The -s count does
168 * include the trailing NULL, so the malloc didn't add in an extra
169 * slot.
170 */
171 nline -= cnt;
172 if (nline <= 0)
173 err("insufficient space for command");
174
175 if (!(bbp = malloc((u_int)nline + 1)))
176 err("%s", strerror(errno));
177 ebp = (argp = p = bbp) + nline - 1;
178
179 if (zflag) {
180 /* Read pathnames terminated by null bytes as produced by
181 * find ... -print0. No comments in this code, see further
182 * below.
183 */
184 for (;;)
185 switch(ch = getchar()) {
186 case EOF:
187 if (p == bbp)
188 exit(exit_status);
189
190 if (argp == p) {
191 *xp = NULL;
192 run(av);
193 exit(exit_status);
194 }
195 /*FALL THROUGH*/
196 case '\0':
197 if (argp == p)
198 continue;
199
200 *p = '\0';
201 *xp++ = argp;
202
203 if (xp == exp || p == ebp || ch == EOF) {
204 if (xflag && xp != exp && p == ebp)
205 err(
206 "insufficient space for arguments");
207 *xp = NULL;
208 run(av);
209 if (ch == EOF)
210 exit(exit_status);
211 p = bbp;
212 xp = bxp;
213 } else
214 ++p;
215 argp = p;
216 break;
217 default:
218 if (p < ebp) {
219 *p++ = ch;
220 break;
221 }
222
223 if (bxp == xp)
224 err("insufficient space for argument");
225 if (xflag)
226 err("insufficient space for arguments");
227
228 *xp = NULL;
229 run(av);
230 xp = bxp;
231 cnt = ebp - argp;
232 bcopy(argp, bbp, cnt);
233 p = (argp = bbp) + cnt;
234 *p++ = ch;
235 break;
236 }
237 /* NOTREACHED */
238 }
239
240 for (insingle = indouble = 0;;)
241 switch(ch = getchar()) {
242 case EOF:
243 /* No arguments since last exec. */
244 if (p == bbp)
245 exit(exit_status);
246
247 /* Nothing since end of last argument. */
248 if (argp == p) {
249 *xp = NULL;
250 run(av);
251 exit(exit_status);
252 }
253 goto arg1;
254 case ' ':
255 case '\t':
256 /* Quotes escape tabs and spaces. */
257 if (insingle || indouble)
258 goto addch;
259 goto arg2;
260 case '\n':
261 /* Empty lines are skipped. */
262 if (argp == p)
263 continue;
264
265 /* Quotes do not escape newlines. */
266arg1: if (insingle || indouble)
267 err("unterminated quote");
268
269arg2: *p = '\0';
270 *xp++ = argp;
271
272 /*
273 * If max'd out on args or buffer, or reached EOF,
274 * run the command. If xflag and max'd out on buffer
275 * but not on args, object.
276 */
277 if (xp == exp || p == ebp || ch == EOF) {
278 if (xflag && xp != exp && p == ebp)
279 err("insufficient space for arguments");
280 *xp = NULL;
281 run(av);
282 if (ch == EOF)
283 exit(exit_status);
284 p = bbp;
285 xp = bxp;
286 } else
287 ++p;
288 argp = p;
289 break;
290 case '\'':
291 if (indouble)
292 goto addch;
293 insingle = !insingle;
294 break;
295 case '"':
296 if (insingle)
297 goto addch;
298 indouble = !indouble;
299 break;
300 case '\\':
301 /* Backslash escapes anything, is escaped by quotes. */
302 if (!insingle && !indouble && (ch = getchar()) == EOF)
303 err("backslash at EOF");
304 /* FALLTHROUGH */
305 default:
306addch: if (p < ebp) {
307 *p++ = ch;
308 break;
309 }
310
311 /* If only one argument, not enough buffer space. */
312 if (bxp == xp)
313 err("insufficient space for argument");
314 /* Didn't hit argument limit, so if xflag object. */
315 if (xflag)
316 err("insufficient space for arguments");
317
318 *xp = NULL;
319 run(av);
320 xp = bxp;
321 cnt = ebp - argp;
322 bcopy(argp, bbp, cnt);
323 p = (argp = bbp) + cnt;
324 *p++ = ch;
325 break;
326 }
327 /* NOTREACHED */
328}
329
330void run(char **argv)
331{
332 register char **p;
333 pid_t pid;
334 int noinvoke;
335 int status;
336 int pfd[2];
337
338 if (tflag) {
339 (void)fprintf(stderr, "%s", *argv);
340 for (p = argv + 1; *p; ++p)
341 (void)fprintf(stderr, " %s", *p);
342 (void)fprintf(stderr, "\n");
343 (void)fflush(stderr);
344 }
345 if (pipe(pfd) < 0) err("pipe: %s", strerror(errno));
346
347 switch(pid = fork()) {
348 case -1:
349 err("fork: %s", strerror(errno));
350 case 0:
351 close(pfd[0]);
352 fcntl(pfd[1], F_SETFD, fcntl(pfd[1], F_GETFD) | FD_CLOEXEC);
353
354 execvp(argv[0], argv);
355 noinvoke = (errno == ENOENT) ? 127 : 126;
356 (void)fprintf(stderr,
357 "xargs: %s exec failed: %s.\n", argv[0], strerror(errno));
358
359 /* Modern way of returning noinvoke instead of a dirty vfork()
360 * trick: (kjb)
361 */
362 write(pfd[1], &noinvoke, sizeof(noinvoke));
363 _exit(-1);
364 }
365 close(pfd[1]);
366 if (read(pfd[0], &noinvoke, sizeof(noinvoke)) < sizeof(noinvoke))
367 noinvoke = 0;
368 close(pfd[0]);
369
370 pid = waitpid(pid, &status, 0);
371 if (pid == -1)
372 err("waitpid: %s", strerror(errno));
373
374 /*
375 * If we couldn't invoke the utility or the utility didn't exit
376 * properly, quit with 127 or 126 respectively.
377 */
378 if (noinvoke)
379 exit(noinvoke);
380
381 /*
382 * According to POSIX, we have to exit if the utility exits with
383 * a 255 status, or is interrupted by a signal. xargs is allowed
384 * to return any exit status between 1 and 125 in these cases, but
385 * we'll use 124 and 125, the same values used by GNU xargs.
386 */
387 if (WIFEXITED(status)) {
388 if (WEXITSTATUS (status) == 255) {
389 fprintf (stderr, "xargs: %s exited with status 255\n",
390 argv[0]);
391 exit(124);
392 } else if (WEXITSTATUS (status) != 0) {
393 exit_status = 123;
394 }
395 } else if (WIFSTOPPED (status)) {
396 fprintf (stderr, "xargs: %s terminated by signal %d\n",
397 argv[0], WSTOPSIG (status));
398 exit(125);
399 } else if (WIFSIGNALED (status)) {
400 fprintf (stderr, "xargs: %s terminated by signal %d\n",
401 argv[0], WTERMSIG (status));
402 exit(125);
403 }
404}
405
406void usage(void)
407{
408 (void)fprintf(stderr,
409"usage: xargs [-t0] [[-x] -n number] [-s size] [utility [argument ...]]\n");
410 exit(1);
411}
412
413void err(const char *fmt, ...)
414{
415 va_list ap;
416
417 va_start(ap, fmt);
418 (void)fprintf(stderr, "xargs: ");
419 (void)vfprintf(stderr, fmt, ap);
420 va_end(ap);
421 (void)fprintf(stderr, "\n");
422 exit(1);
423 /* NOTREACHED */
424}
Note: See TracBrowser for help on using the repository browser.