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