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