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 12 char 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 18 static char sccsid[] = "@(#)xargs.c 5.11 (Berkeley) 06/19/91"; 19 #endif /* not lint */ 20 21 #include <sys/types.h> 22 #include <sys/wait.h> 23 #include <errno.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <unistd.h> 28 #include <limits.h> 29 #include "pathnames.h" 30 31 int fflag, tflag; 32 void err __P((const char *, ...)); 33 void run(), usage(); 34 35 main(argc, argv) 36 int argc; 37 char **argv; 38 { 39 extern int optind; 40 extern char *optarg; 41 register int ch; 42 register char *p, *bbp, *ebp, **bxp, **exp, **xp; 43 int cnt, indouble, insingle, nargs, nflag, nline, xflag; 44 char **av, *argp; 45 46 /* 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: 51 * 52 * (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2. 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; 60 nline = ARG_MAX - 4 * 1024; 61 nflag = xflag = 0; 62 while ((ch = getopt(argc, argv, "fn:s:tx")) != EOF) 63 switch(ch) { 64 case 'f': 65 fflag = 1; 66 break; 67 case 'n': 68 nflag = 1; 69 if ((nargs = atoi(optarg)) <= 0) 70 err("illegal argument count"); 71 break; 72 case 's': 73 nline = atoi(optarg); 74 break; 75 case 't': 76 tflag = 1; 77 break; 78 case 'x': 79 xflag = 1; 80 break; 81 case '?': 82 default: 83 usage(); 84 } 85 argc -= optind; 86 argv += optind; 87 88 if (xflag && !nflag) 89 usage(); 90 91 /* 92 * Allocate pointers for the utility name, the utility arguments, 93 * the maximum arguments to be read from stdin and the trailing 94 * NULL. 95 */ 96 if (!(av = bxp = 97 malloc((u_int)(1 + argc + nargs + 1) * sizeof(char **)))) 98 err("%s", strerror(errno)); 99 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 */ 105 if (!*argv) 106 cnt = strlen(*bxp++ = _PATH_ECHO); 107 else { 108 cnt = 0; 109 do { 110 cnt += strlen(*bxp++ = *argv) + 1; 111 } while (*++argv); 112 } 113 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; 136 137 for (insingle = indouble = 0;;) 138 switch(ch = getchar()) { 139 case EOF: 140 /* No arguments since last exec. */ 141 if (p == bbp) 142 exit(0); 143 144 /* Nothing since end of last argument. */ 145 if (argp == p) { 146 *xp = NULL; 147 run(av); 148 exit(0); 149 } 150 goto arg1; 151 case ' ': 152 case '\t': 153 /* Quotes escape tabs and spaces. */ 154 if (insingle || indouble) 155 goto addch; 156 goto arg2; 157 case '\n': 158 /* Empty lines are skipped. */ 159 if (argp == p) 160 continue; 161 162 /* Quotes do not escape newlines. */ 163 arg1: if (insingle || indouble) 164 err("unterminated quote"); 165 166 arg2: *p = '\0'; 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"); 177 *xp = NULL; 178 run(av); 179 if (ch == EOF) 180 exit(0); 181 p = bbp; 182 xp = bxp; 183 } else 184 ++p; 185 argp = p; 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 '\\': 198 /* Backslash escapes anything, is escaped by quotes. */ 199 if (!insingle && !indouble && (ch = getchar()) == EOF) 200 err("backslash at EOF"); 201 /* FALLTHROUGH */ 202 default: 203 addch: if (p < ebp) { 204 *p++ = ch; 205 break; 206 } 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 215 *xp = NULL; 216 run(av); 217 xp = bxp; 218 cnt = ebp - argp; 219 bcopy(argp, bbp, cnt); 220 p = (argp = bbp) + cnt; 221 *p++ = ch; 222 break; 223 } 224 /* NOTREACHED */ 225 } 226 227 void 228 run(argv) 229 char **argv; 230 { 231 register char **p; 232 pid_t pid; 233 volatile int noinvoke; 234 int status; 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 } 243 noinvoke = 0; 244 switch(pid = vfork()) { 245 case -1: 246 err("vfork: %s", strerror(errno)); 247 case 0: 248 execvp(argv[0], argv); 249 (void)fprintf(stderr, 250 "xargs: %s: %s.\n", argv[0], strerror(errno)); 251 noinvoke = 1; 252 _exit(1); 253 } 254 pid = waitpid(pid, &status, 0); 255 if (pid == -1) 256 err("waitpid: %s", strerror(errno)); 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)); 267 } 268 269 void 270 usage() 271 { 272 (void)fprintf(stderr, 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 283 void 284 #if __STDC__ 285 err(const char *fmt, ...) 286 #else 287 err(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"); 302 exit(1); 303 /* NOTREACHED */ 304 } 305