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