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