1 /*-
2 * Copyright (c) 1990, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * John B. Roll Jr.
7 *
8 * %sccs.include.redist.c%
9 */
10
11 #ifndef lint
12 static char copyright[] =
13 "@(#) Copyright (c) 1990, 1993\n\
14 The Regents of the University of California. All rights reserved.\n";
15 #endif /* not lint */
16
17 #ifndef lint
18 static char sccsid[] = "@(#)xargs.c 8.1 (Berkeley) 06/06/93";
19 #endif /* not lint */
20
21 #include <sys/types.h>
22 #include <sys/wait.h>
23 #include <errno.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <limits.h>
29 #include "pathnames.h"
30
31 int tflag, rval;
32
33 void err __P((const char *, ...));
34 void run __P((char **));
35 void usage __P((void));
36
main(argc,argv)37 main(argc, argv)
38 int argc;
39 char **argv;
40 {
41 register int ch;
42 register char *p, *bbp, *ebp, **bxp, **exp, **xp;
43 int cnt, indouble, insingle, nargs, nflag, nline, xflag;
44 char **av, *argp;
45
46 /*
47 * POSIX.2 limits the exec line length to ARG_MAX - 2K. Running that
48 * caused some E2BIG errors, so it was changed to ARG_MAX - 4K. Given
49 * that the smallest argument is 2 bytes in length, this means that
50 * the number of arguments is limited to:
51 *
52 * (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2.
53 *
54 * We arbitrarily limit the number of arguments to 5000. This is
55 * allowed by POSIX.2 as long as the resulting minimum exec line is
56 * at least LINE_MAX. Realloc'ing as necessary is possible, but
57 * probably not worthwhile.
58 */
59 nargs = 5000;
60 nline = ARG_MAX - 4 * 1024;
61 nflag = xflag = 0;
62 while ((ch = getopt(argc, argv, "n:s:tx")) != EOF)
63 switch(ch) {
64 case 'n':
65 nflag = 1;
66 if ((nargs = atoi(optarg)) <= 0)
67 err("illegal argument count");
68 break;
69 case 's':
70 nline = atoi(optarg);
71 break;
72 case 't':
73 tflag = 1;
74 break;
75 case 'x':
76 xflag = 1;
77 break;
78 case '?':
79 default:
80 usage();
81 }
82 argc -= optind;
83 argv += optind;
84
85 if (xflag && !nflag)
86 usage();
87
88 /*
89 * Allocate pointers for the utility name, the utility arguments,
90 * the maximum arguments to be read from stdin and the trailing
91 * NULL.
92 */
93 if (!(av = bxp =
94 malloc((u_int)(1 + argc + nargs + 1) * sizeof(char **))))
95 err("%s", strerror(errno));
96
97 /*
98 * Use the user's name for the utility as argv[0], just like the
99 * shell. Echo is the default. Set up pointers for the user's
100 * arguments.
101 */
102 if (!*argv)
103 cnt = strlen(*bxp++ = _PATH_ECHO);
104 else {
105 cnt = 0;
106 do {
107 cnt += strlen(*bxp++ = *argv) + 1;
108 } while (*++argv);
109 }
110
111 /*
112 * Set up begin/end/traversing pointers into the array. The -n
113 * count doesn't include the trailing NULL pointer, so the malloc
114 * added in an extra slot.
115 */
116 exp = (xp = bxp) + nargs;
117
118 /*
119 * Allocate buffer space for the arguments read from stdin and the
120 * trailing NULL. Buffer space is defined as the default or specified
121 * space, minus the length of the utility name and arguments. Set up
122 * begin/end/traversing pointers into the array. The -s count does
123 * include the trailing NULL, so the malloc didn't add in an extra
124 * slot.
125 */
126 nline -= cnt;
127 if (nline <= 0)
128 err("insufficient space for command");
129
130 if (!(bbp = malloc((u_int)nline + 1)))
131 err("%s", strerror(errno));
132 ebp = (argp = p = bbp) + nline - 1;
133
134 for (insingle = indouble = 0;;)
135 switch(ch = getchar()) {
136 case EOF:
137 /* No arguments since last exec. */
138 if (p == bbp)
139 exit(rval);
140
141 /* Nothing since end of last argument. */
142 if (argp == p) {
143 *xp = NULL;
144 run(av);
145 exit(rval);
146 }
147 goto arg1;
148 case ' ':
149 case '\t':
150 /* Quotes escape tabs and spaces. */
151 if (insingle || indouble)
152 goto addch;
153 goto arg2;
154 case '\n':
155 /* Empty lines are skipped. */
156 if (argp == p)
157 continue;
158
159 /* Quotes do not escape newlines. */
160 arg1: if (insingle || indouble)
161 err("unterminated quote");
162
163 arg2: *p = '\0';
164 *xp++ = argp;
165
166 /*
167 * If max'd out on args or buffer, or reached EOF,
168 * run the command. If xflag and max'd out on buffer
169 * but not on args, object.
170 */
171 if (xp == exp || p == ebp || ch == EOF) {
172 if (xflag && xp != exp && p == ebp)
173 err("insufficient space for arguments");
174 *xp = NULL;
175 run(av);
176 if (ch == EOF)
177 exit(rval);
178 p = bbp;
179 xp = bxp;
180 } else
181 ++p;
182 argp = p;
183 break;
184 case '\'':
185 if (indouble)
186 goto addch;
187 insingle = !insingle;
188 break;
189 case '"':
190 if (insingle)
191 goto addch;
192 indouble = !indouble;
193 break;
194 case '\\':
195 /* Backslash escapes anything, is escaped by quotes. */
196 if (!insingle && !indouble && (ch = getchar()) == EOF)
197 err("backslash at EOF");
198 /* FALLTHROUGH */
199 default:
200 addch: if (p < ebp) {
201 *p++ = ch;
202 break;
203 }
204
205 /* If only one argument, not enough buffer space. */
206 if (bxp == xp)
207 err("insufficient space for argument");
208 /* Didn't hit argument limit, so if xflag object. */
209 if (xflag)
210 err("insufficient space for arguments");
211
212 *xp = NULL;
213 run(av);
214 xp = bxp;
215 cnt = ebp - argp;
216 bcopy(argp, bbp, cnt);
217 p = (argp = bbp) + cnt;
218 *p++ = ch;
219 break;
220 }
221 /* NOTREACHED */
222 }
223
224 void
run(argv)225 run(argv)
226 char **argv;
227 {
228 volatile int noinvoke;
229 register char **p;
230 pid_t pid;
231 int status;
232
233 if (tflag) {
234 (void)fprintf(stderr, "%s", *argv);
235 for (p = argv + 1; *p; ++p)
236 (void)fprintf(stderr, " %s", *p);
237 (void)fprintf(stderr, "\n");
238 (void)fflush(stderr);
239 }
240 noinvoke = 0;
241 switch(pid = vfork()) {
242 case -1:
243 err("vfork: %s", strerror(errno));
244 case 0:
245 execvp(argv[0], argv);
246 (void)fprintf(stderr,
247 "xargs: %s: %s\n", argv[0], strerror(errno));
248 noinvoke = 1;
249 _exit(1);
250 }
251 pid = waitpid(pid, &status, 0);
252 if (pid == -1)
253 err("waitpid: %s", strerror(errno));
254 /* If we couldn't invoke the utility, exit 127. */
255 if (noinvoke)
256 exit(127);
257 /* If utility signaled or exited with a value of 255, exit 1-125. */
258 if (WIFSIGNALED(status) || WEXITSTATUS(status) == 255)
259 exit(1);
260 if (WEXITSTATUS(status))
261 rval = 1;
262 }
263
264 void
usage()265 usage()
266 {
267 (void)fprintf(stderr,
268 "usage: xargs [-t] [-n number [-x]] [-s size] [utility [argument ...]]\n");
269 exit(1);
270 }
271
272 #if __STDC__
273 #include <stdarg.h>
274 #else
275 #include <varargs.h>
276 #endif
277
278 void
279 #if __STDC__
err(const char * fmt,...)280 err(const char *fmt, ...)
281 #else
282 err(fmt, va_alist)
283 char *fmt;
284 va_dcl
285 #endif
286 {
287 va_list ap;
288 #if __STDC__
289 va_start(ap, fmt);
290 #else
291 va_start(ap);
292 #endif
293 (void)fprintf(stderr, "xargs: ");
294 (void)vfprintf(stderr, fmt, ap);
295 va_end(ap);
296 (void)fprintf(stderr, "\n");
297 exit(1);
298 /* NOTREACHED */
299 }
300