>From: phk@flsco.fls.dk (Poul-Henning Kamp/P-HK)
[unix-history] / usr.bin / xargs / xargs.c
CommitLineData
15637ed4
RG
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 "pathnames.h"
56
a9f2ec5e
C
57int exit_status = 0;
58int tflag;
15637ed4
RG
59void err __P((const char *, ...));
60void run(), usage();
61
62main(argc, argv)
63 int argc;
64 char **argv;
65{
66 extern int optind;
67 extern char *optarg;
68 register int ch;
69 register char *p, *bbp, *ebp, **bxp, **exp, **xp;
70 int cnt, indouble, insingle, nargs, nflag, nline, xflag;
71 char **av, *argp;
72
73 /*
74 * POSIX.2 limits the exec line length to ARG_MAX - 2K. Running that
75 * caused some E2BIG errors, so it was changed to ARG_MAX - 4K. Given
76 * that the smallest argument is 2 bytes in length, this means that
77 * the number of arguments is limited to:
78 *
79 * (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2.
80 *
81 * We arbitrarily limit the number of arguments to 5000. This is
82 * allowed by POSIX.2 as long as the resulting minimum exec line is
83 * at least LINE_MAX. Realloc'ing as necessary is possible, but
84 * probably not worthwhile.
85 */
86 nargs = 5000;
87 nline = ARG_MAX - 4 * 1024;
88 nflag = xflag = 0;
a9f2ec5e 89 while ((ch = getopt(argc, argv, "n:s:tx")) != EOF)
15637ed4 90 switch(ch) {
15637ed4
RG
91 case 'n':
92 nflag = 1;
93 if ((nargs = atoi(optarg)) <= 0)
94 err("illegal argument count");
95 break;
96 case 's':
97 nline = atoi(optarg);
98 break;
99 case 't':
100 tflag = 1;
101 break;
102 case 'x':
103 xflag = 1;
104 break;
105 case '?':
106 default:
107 usage();
108 }
109 argc -= optind;
110 argv += optind;
111
112 if (xflag && !nflag)
113 usage();
114
115 /*
116 * Allocate pointers for the utility name, the utility arguments,
117 * the maximum arguments to be read from stdin and the trailing
118 * NULL.
119 */
120 if (!(av = bxp =
121 malloc((u_int)(1 + argc + nargs + 1) * sizeof(char **))))
122 err("%s", strerror(errno));
123
124 /*
125 * Use the user's name for the utility as argv[0], just like the
126 * shell. Echo is the default. Set up pointers for the user's
127 * arguments.
128 */
129 if (!*argv)
130 cnt = strlen(*bxp++ = _PATH_ECHO);
131 else {
132 cnt = 0;
133 do {
134 cnt += strlen(*bxp++ = *argv) + 1;
135 } while (*++argv);
136 }
137
138 /*
139 * Set up begin/end/traversing pointers into the array. The -n
140 * count doesn't include the trailing NULL pointer, so the malloc
141 * added in an extra slot.
142 */
143 exp = (xp = bxp) + nargs;
144
145 /*
146 * Allocate buffer space for the arguments read from stdin and the
147 * trailing NULL. Buffer space is defined as the default or specified
148 * space, minus the length of the utility name and arguments. Set up
149 * begin/end/traversing pointers into the array. The -s count does
150 * include the trailing NULL, so the malloc didn't add in an extra
151 * slot.
152 */
153 nline -= cnt;
154 if (nline <= 0)
155 err("insufficient space for command");
156
157 if (!(bbp = malloc((u_int)nline + 1)))
158 err("%s", strerror(errno));
159 ebp = (argp = p = bbp) + nline - 1;
160
161 for (insingle = indouble = 0;;)
162 switch(ch = getchar()) {
163 case EOF:
164 /* No arguments since last exec. */
165 if (p == bbp)
a9f2ec5e 166 exit(exit_status);
15637ed4
RG
167
168 /* Nothing since end of last argument. */
169 if (argp == p) {
170 *xp = NULL;
171 run(av);
a9f2ec5e 172 exit(exit_status);
15637ed4
RG
173 }
174 goto arg1;
175 case ' ':
176 case '\t':
177 /* Quotes escape tabs and spaces. */
178 if (insingle || indouble)
179 goto addch;
180 goto arg2;
181 case '\n':
182 /* Empty lines are skipped. */
183 if (argp == p)
184 continue;
185
186 /* Quotes do not escape newlines. */
187arg1: if (insingle || indouble)
188 err("unterminated quote");
189
190arg2: *p = '\0';
191 *xp++ = argp;
192
193 /*
194 * If max'd out on args or buffer, or reached EOF,
195 * run the command. If xflag and max'd out on buffer
196 * but not on args, object.
197 */
198 if (xp == exp || p == ebp || ch == EOF) {
199 if (xflag && xp != exp && p == ebp)
200 err("insufficient space for arguments");
201 *xp = NULL;
202 run(av);
203 if (ch == EOF)
a9f2ec5e 204 exit(exit_status);
15637ed4
RG
205 p = bbp;
206 xp = bxp;
207 } else
208 ++p;
209 argp = p;
210 break;
211 case '\'':
212 if (indouble)
213 goto addch;
214 insingle = !insingle;
215 break;
216 case '"':
217 if (insingle)
218 goto addch;
219 indouble = !indouble;
220 break;
221 case '\\':
222 /* Backslash escapes anything, is escaped by quotes. */
223 if (!insingle && !indouble && (ch = getchar()) == EOF)
224 err("backslash at EOF");
225 /* FALLTHROUGH */
226 default:
227addch: if (p < ebp) {
228 *p++ = ch;
229 break;
230 }
231
232 /* If only one argument, not enough buffer space. */
233 if (bxp == xp)
234 err("insufficient space for argument");
235 /* Didn't hit argument limit, so if xflag object. */
236 if (xflag)
237 err("insufficient space for arguments");
238
239 *xp = NULL;
240 run(av);
241 xp = bxp;
242 cnt = ebp - argp;
243 bcopy(argp, bbp, cnt);
244 p = (argp = bbp) + cnt;
245 *p++ = ch;
246 break;
247 }
248 /* NOTREACHED */
249}
250
251void
252run(argv)
253 char **argv;
254{
255 register char **p;
256 pid_t pid;
257 volatile int noinvoke;
258 int status;
259
260 if (tflag) {
261 (void)fprintf(stderr, "%s", *argv);
262 for (p = argv + 1; *p; ++p)
263 (void)fprintf(stderr, " %s", *p);
264 (void)fprintf(stderr, "\n");
265 (void)fflush(stderr);
266 }
267 noinvoke = 0;
268 switch(pid = vfork()) {
269 case -1:
270 err("vfork: %s", strerror(errno));
271 case 0:
272 execvp(argv[0], argv);
a9f2ec5e 273 noinvoke = (errno == ENOENT) ? 127 : 126;
15637ed4
RG
274 (void)fprintf(stderr,
275 "xargs: %s: %s.\n", argv[0], strerror(errno));
15637ed4
RG
276 _exit(1);
277 }
278 pid = waitpid(pid, &status, 0);
279 if (pid == -1)
280 err("waitpid: %s", strerror(errno));
a9f2ec5e 281
15637ed4
RG
282 /*
283 * If we couldn't invoke the utility or the utility didn't exit
a9f2ec5e 284 * properly, quit with 127 or 126 respectively.
15637ed4 285 */
a9f2ec5e
C
286 if (noinvoke)
287 exit(noinvoke);
288
289 /*
290 * According to POSIX, we have to exit if the utility exits with
291 * a 255 status, or is interrupted by a signal. xargs is allowed
292 * to return any exit status between 1 and 125 in these cases, but
293 * we'll use 124 and 125, the same values used by GNU xargs.
294 */
295 if (WIFEXITED(status)) {
296 if (WEXITSTATUS (status) == 255) {
297 fprintf (stderr, "xargs: %s exited with status 255\n",
298 argv[0]);
299 exit(124);
300 } else if (WEXITSTATUS (status) != 0) {
301 exit_status = 123;
302 }
303 } else if (WIFSTOPPED (status)) {
304 fprintf (stderr, "xargs: %s terminated by signal %d\n",
305 argv[0], WSTOPSIG (status));
306 exit(125);
307 } else if (WIFSIGNALED (status)) {
308 fprintf (stderr, "xargs: %s terminated by signal %d\n",
309 argv[0], WTERMSIG (status));
310 exit(125);
311 }
15637ed4
RG
312}
313
314void
315usage()
316{
317 (void)fprintf(stderr,
a9f2ec5e 318"usage: xargs [-t] [[-x] -n number] [-s size] [utility [argument ...]]\n");
15637ed4
RG
319 exit(1);
320}
321
322#if __STDC__
323#include <stdarg.h>
324#else
325#include <varargs.h>
326#endif
327
328void
329#if __STDC__
330err(const char *fmt, ...)
331#else
332err(fmt, va_alist)
333 char *fmt;
334 va_dcl
335#endif
336{
337 va_list ap;
338#if __STDC__
339 va_start(ap, fmt);
340#else
341 va_start(ap);
342#endif
343 (void)fprintf(stderr, "xargs: ");
344 (void)vfprintf(stderr, fmt, ap);
345 va_end(ap);
346 (void)fprintf(stderr, "\n");
347 exit(1);
348 /* NOTREACHED */
349}