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