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