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