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