1 /* $NetBSD: xargs.c,v 1.12 1999/12/22 14:41:01 kleink Exp $ */ 2 3 /*- 4 * Copyright (c) 1990, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * John B. Roll Jr. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #ifndef lint 41 __COPYRIGHT("@(#) Copyright (c) 1990, 1993\n\ 42 The Regents of the University of California. All rights reserved.\n"); 43 #endif /* not lint */ 44 45 #ifndef lint 46 #if 0 47 static char sccsid[] = "@(#)xargs.c 8.1 (Berkeley) 6/6/93"; 48 #endif 49 __RCSID("$NetBSD: xargs.c,v 1.12 1999/12/22 14:41:01 kleink Exp $"); 50 #endif /* not lint */ 51 52 #include <sys/types.h> 53 #include <sys/wait.h> 54 #include <err.h> 55 #include <errno.h> 56 #include <langinfo.h> 57 #include <limits.h> 58 #include <locale.h> 59 #include <paths.h> 60 #include <regex.h> 61 #include <signal.h> 62 #include <stdio.h> 63 #include <stdlib.h> 64 #include <string.h> 65 #include <unistd.h> 66 #include "pathnames.h" 67 68 static int pflag, tflag, zflag, rval; 69 static FILE *promptfile; 70 static regex_t yesexpr; 71 72 static void run __P((char **)); 73 int main __P((int, char **)); 74 static void usage __P((void)); 75 76 int 77 main(argc, argv) 78 int argc; 79 char **argv; 80 { 81 int ch; 82 char *p, *bbp, *ebp, **bxp, **exp, **xp; 83 int cnt, indouble, insingle, nargs, nflag, nline, xflag; 84 char **av, *argp; 85 86 setlocale(LC_ALL, ""); 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 nline = ARG_MAX - 4 * 1024; 103 nflag = xflag = 0; 104 while ((ch = getopt(argc, argv, "0n:ps:tx")) != -1) 105 switch(ch) { 106 case '0': 107 zflag = 1; 108 break; 109 case 'n': 110 nflag = 1; 111 if ((nargs = atoi(optarg)) <= 0) 112 errx(1, "illegal argument count"); 113 break; 114 case 'p': 115 pflag = tflag = 1; 116 break; 117 case 's': 118 nline = atoi(optarg); 119 break; 120 case 't': 121 tflag = 1; 122 break; 123 case 'x': 124 xflag = 1; 125 break; 126 case '?': 127 default: 128 usage(); 129 } 130 argc -= optind; 131 argv += optind; 132 133 if (xflag && !nflag) 134 usage(); 135 136 /* 137 * Allocate pointers for the utility name, the utility arguments, 138 * the maximum arguments to be read from stdin and the trailing 139 * NULL. 140 */ 141 if (!(av = bxp = 142 malloc((u_int)(1 + argc + nargs + 1) * sizeof(char **)))) 143 err(1, "malloc"); 144 145 /* 146 * Use the user's name for the utility as argv[0], just like the 147 * shell. Echo is the default. Set up pointers for the user's 148 * arguments. 149 */ 150 if (!*argv) 151 cnt = strlen(*bxp++ = _PATH_ECHO); 152 else { 153 cnt = 0; 154 do { 155 cnt += strlen(*bxp++ = *argv) + 1; 156 } while (*++argv); 157 } 158 159 /* 160 * Set up begin/end/traversing pointers into the array. The -n 161 * count doesn't include the trailing NULL pointer, so the malloc 162 * added in an extra slot. 163 */ 164 exp = (xp = bxp) + nargs; 165 166 /* 167 * Allocate buffer space for the arguments read from stdin and the 168 * trailing NULL. Buffer space is defined as the default or specified 169 * space, minus the length of the utility name and arguments. Set up 170 * begin/end/traversing pointers into the array. The -s count does 171 * include the trailing NULL, so the malloc didn't add in an extra 172 * slot. 173 */ 174 nline -= cnt; 175 if (nline <= 0) 176 errx(1, "insufficient space for command"); 177 178 if (!(bbp = malloc((u_int)nline + 1))) 179 err(1, "malloc"); 180 ebp = (argp = p = bbp) + nline - 1; 181 182 if (pflag) { 183 int error; 184 185 if ((promptfile = fopen(_PATH_TTY, "r")) == NULL) 186 err(1, "prompt mode: cannot open input"); 187 if ((error = regcomp(&yesexpr, nl_langinfo(YESEXPR), REG_NOSUB)) 188 != 0) { 189 char msg[NL_TEXTMAX]; 190 191 (void)regerror(error, NULL, msg, sizeof (msg)); 192 err(1, "cannot compile yesexpr: %s", msg); 193 } 194 } 195 196 for (insingle = indouble = 0;;) 197 switch(ch = getchar()) { 198 case EOF: 199 /* No arguments since last exec. */ 200 if (p == bbp) 201 exit(rval); 202 203 /* Nothing since end of last argument. */ 204 if (argp == p) { 205 *xp = NULL; 206 run(av); 207 exit(rval); 208 } 209 goto arg1; 210 case ' ': 211 case '\t': 212 /* Quotes escape tabs and spaces. */ 213 if (insingle || indouble || zflag) 214 goto addch; 215 goto arg2; 216 case '\0': 217 if (zflag) 218 goto arg2; 219 goto addch; 220 case '\n': 221 if (zflag) 222 goto addch; 223 /* Empty lines are skipped. */ 224 if (argp == p) 225 continue; 226 227 /* Quotes do not escape newlines. */ 228 arg1: if (insingle || indouble) 229 errx(1, "unterminated quote"); 230 231 arg2: *p = '\0'; 232 *xp++ = argp; 233 234 /* 235 * If max'd out on args or buffer, or reached EOF, 236 * run the command. If xflag and max'd out on buffer 237 * but not on args, object. 238 */ 239 if (xp == exp || p == ebp || ch == EOF) { 240 if (xflag && xp != exp && p == ebp) 241 errx(1, "insufficient space for arguments"); 242 *xp = NULL; 243 run(av); 244 if (ch == EOF) 245 exit(rval); 246 p = bbp; 247 xp = bxp; 248 } else 249 ++p; 250 argp = p; 251 break; 252 case '\'': 253 if (indouble || zflag) 254 goto addch; 255 insingle = !insingle; 256 break; 257 case '"': 258 if (insingle || zflag) 259 goto addch; 260 indouble = !indouble; 261 break; 262 case '\\': 263 if (zflag) 264 goto addch; 265 /* Backslash escapes anything, is escaped by quotes. */ 266 if (!insingle && !indouble && (ch = getchar()) == EOF) 267 errx(1, "backslash at EOF"); 268 /* FALLTHROUGH */ 269 default: 270 addch: if (p < ebp) { 271 *p++ = ch; 272 break; 273 } 274 275 /* If only one argument, not enough buffer space. */ 276 if (bxp == xp) 277 errx(1, "insufficient space for argument"); 278 /* Didn't hit argument limit, so if xflag object. */ 279 if (xflag) 280 errx(1, "insufficient space for arguments"); 281 282 *xp = NULL; 283 run(av); 284 xp = bxp; 285 cnt = ebp - argp; 286 memmove(bbp, argp, cnt); 287 p = (argp = bbp) + cnt; 288 *p++ = ch; 289 break; 290 } 291 /* NOTREACHED */ 292 } 293 294 static void 295 run(argv) 296 char **argv; 297 { 298 volatile int noinvoke; 299 char **p; 300 pid_t pid; 301 int status; 302 303 if (tflag) { 304 (void)fprintf(stderr, "%s", *argv); 305 for (p = argv + 1; *p; ++p) 306 (void)fprintf(stderr, " %s", *p); 307 if (pflag) { 308 char buf[LINE_MAX + 1]; 309 310 (void)fprintf(stderr, "?..."); 311 fflush(stderr); 312 if (fgets(buf, sizeof (buf), promptfile) == NULL) { 313 rval = 1; 314 return; 315 } 316 if (regexec(&yesexpr, buf, 0, NULL, 0) != 0) 317 return; 318 } else { 319 (void)fprintf(stderr, "\n"); 320 } 321 } 322 noinvoke = 0; 323 switch(pid = vfork()) { 324 case -1: 325 err(1, "vfork"); 326 case 0: 327 execvp(argv[0], argv); 328 noinvoke = (errno == ENOENT) ? 127 : 126; 329 warn("%s", argv[0]);; 330 _exit(1); 331 } 332 pid = waitpid(pid, &status, 0); 333 if (pid == -1) 334 err(1, "waitpid"); 335 336 /* 337 * If we couldn't invoke the utility or the utility didn't exit 338 * properly, quit with 127 or 126 respectively. 339 */ 340 if (noinvoke) 341 exit(noinvoke); 342 343 /* 344 * According to POSIX, we have to exit if the utility exits with 345 * a 255 status, or is interrupted by a signal. xargs is allowed 346 * to return any exit status between 1 and 125 in these cases, but 347 * we'll use 124 and 125, the same values used by GNU xargs. 348 */ 349 if (WIFEXITED(status)) { 350 if (WEXITSTATUS (status) == 255) { 351 warnx ("%s exited with status 255", argv[0]); 352 exit(124); 353 } else if (WEXITSTATUS (status) != 0) { 354 rval = 123; 355 } 356 } else if (WIFSIGNALED (status)) { 357 if (WTERMSIG(status) < NSIG) { 358 warnx("%s terminated by SIG%s", argv[0], 359 sys_signame[WTERMSIG(status)]); 360 } else { 361 warnx("%s terminated by signal %d", argv[0], 362 WTERMSIG(status)); 363 } 364 exit(125); 365 } 366 } 367 368 static void 369 usage() 370 { 371 (void)fprintf(stderr, 372 "usage: xargs [-0pt] [-n number [-x]] [-s size] [utility [argument ...]]\n"); 373 exit(1); 374 } 375