update from Rick Macklem
[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 *
8 * %sccs.include.redist.c%
9 */
10
11#ifndef lint
12char 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 18static 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 31int fflag, tflag;
9ae6b4c3
KB
32void err __P((const char *, ...));
33void run(), usage();
c599959c
KB
34
35main(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. */
163arg1: if (insingle || indouble)
164 err("unterminated quote");
165
dc8c5f0c 166arg2: *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 203addch: 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
227void
228run(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 269void
c599959c
KB
270usage()
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
283void
284#if __STDC__
285err(const char *fmt, ...)
286#else
287err(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}