1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2014 Garrett D'Amore <garrett@damore.org> 23 * Copyright 2012 DEY Storage Systems, Inc. 24 * 25 * Portions of this file developed by DEY Storage Systems, Inc. are licensed 26 * under the terms of the Common Development and Distribution License (CDDL) 27 * version 1.0 only. The use of subsequent versions of the License are 28 * is specifically prohibited unless those terms are not in conflict with 29 * version 1.0 of the License. You can find this license on-line at 30 * http://www.illumos.org/license/CDDL 31 */ 32 /* 33 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 34 * Use is subject to license terms. 35 */ 36 37 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 38 /* All Rights Reserved */ 39 40 41 #include <stdio.h> 42 #include <sys/types.h> 43 #include <sys/wait.h> 44 #include <unistd.h> 45 #include <fcntl.h> 46 #include <string.h> 47 #include <stdarg.h> 48 #include <stdlib.h> 49 #include <limits.h> 50 #include <wchar.h> 51 #include <locale.h> 52 #include <langinfo.h> 53 #include <stropts.h> 54 #include <poll.h> 55 #include <errno.h> 56 #include <stdarg.h> 57 #include "getresponse.h" 58 59 #define HEAD 0 60 #define TAIL 1 61 #define FALSE 0 62 #define TRUE 1 63 #define MAXSBUF 255 64 #define MAXIBUF 512 65 #define MAXINSERTS 5 66 #define BUFSIZE LINE_MAX 67 #define MAXARGS 255 68 #define INSPAT_STR "{}" /* default replstr string for -[Ii] */ 69 #define FORK_RETRY 5 70 71 #define QBUF_STARTLEN 255 /* start size of growable string buffer */ 72 #define QBUF_INC 100 /* how much to grow a growable string by */ 73 74 /* We use these macros to help make formatting look "consistent" */ 75 #define EMSG(s) ermsg(gettext(s "\n")) 76 #define EMSG2(s, a) ermsg(gettext(s "\n"), a) 77 #define PERR(s) perror(gettext("xargs: " s)) 78 79 /* Some common error messages */ 80 81 #define LIST2LONG "Argument list too long" 82 #define ARG2LONG "A single argument was greater than %d bytes" 83 #define MALLOCFAIL "Memory allocation failure" 84 #define CORRUPTFILE "Corrupt input file" 85 #define WAITFAIL "Wait failure" 86 #define CHILDSIG "Child killed with signal %d" 87 #define CHILDFAIL "Command could not continue processing data" 88 #define FORKFAIL "Could not fork child" 89 #define EXECFAIL "Could not exec command" 90 #define MISSQUOTE "Missing quote" 91 #define BADESCAPE "Incomplete escape" 92 #define IBUFOVERFLOW "Insert buffer overflow" 93 94 #define _(x) gettext(x) 95 96 static wctype_t blank; 97 static char *arglist[MAXARGS+1]; 98 static char argbuf[BUFSIZE * 2 + 1]; 99 static char lastarg[BUFSIZE + 1]; 100 static char **ARGV = arglist; 101 static char *LEOF = "_"; 102 static char *INSPAT = INSPAT_STR; 103 static char ins_buf[MAXIBUF]; 104 static char *p_ibuf; 105 106 static struct inserts { 107 char **p_ARGV; /* where to put newarg ptr in arg list */ 108 char *p_skel; /* ptr to arg template */ 109 } saveargv[MAXINSERTS]; 110 111 static int PROMPT = -1; 112 static int BUFLIM = BUFSIZE; 113 static int N_ARGS = 0; 114 static int N_args = 0; 115 static int N_lines = 0; 116 static int DASHX = FALSE; 117 static int MORE = TRUE; 118 static int PER_LINE = FALSE; 119 static int LINE_CONT = FALSE; 120 static int EAT_LEAD = FALSE; 121 static int ERR = FALSE; 122 static int OK = TRUE; 123 static int LEGAL = FALSE; 124 static int TRACE = FALSE; 125 static int INSERT = FALSE; 126 static int ZERO = FALSE; 127 static int linesize = 0; 128 static int ibufsize = 0; 129 static int exitstat = 0; /* our exit status */ 130 static int mac; /* modified argc, after parsing */ 131 static char **mav; /* modified argv, after parsing */ 132 static int n_inserts; /* # of insertions. */ 133 134 /* our usage message: */ 135 #define USAGEMSG "Usage: xargs: [-t] [-p] [-0] [-e[eofstr]] [-E eofstr] "\ 136 "[-I replstr] [-i[replstr]] [-L #] [-l[#]] [-n # [-x]] [-s size] "\ 137 "[cmd [args ...]]\n" 138 139 static int echoargs(); 140 static wint_t getwchr(char *, size_t *); 141 static int lcall(char *sub, char **subargs); 142 static void addibuf(struct inserts *p); 143 static void ermsg(char *messages, ...); 144 static char *addarg(char *arg); 145 static void store_str(char **, char *, size_t); 146 static char *getarg(char *); 147 static char *insert(char *pattern, char *subst); 148 static void usage(); 149 static void parseargs(); 150 151 int 152 main(int argc, char **argv) 153 { 154 int j; 155 struct inserts *psave; 156 int c; 157 int initsize; 158 char *cmdname, **initlist; 159 char *arg; 160 char *next; 161 162 /* initialization */ 163 blank = wctype("blank"); 164 n_inserts = 0; 165 psave = saveargv; 166 (void) setlocale(LC_ALL, ""); 167 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 168 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 169 #endif 170 (void) textdomain(TEXT_DOMAIN); 171 if (init_yes() < 0) { 172 ermsg(_(ERR_MSG_INIT_YES), strerror(errno)); 173 exit(1); 174 } 175 176 parseargs(argc, argv); 177 178 /* handling all of xargs arguments: */ 179 while ((c = getopt(mac, mav, "0tpe:E:I:i:L:l:n:s:x")) != EOF) { 180 switch (c) { 181 case '0': 182 ZERO = TRUE; 183 break; 184 185 case 't': /* -t: turn trace mode on */ 186 TRACE = TRUE; 187 break; 188 189 case 'p': /* -p: turn on prompt mode. */ 190 if ((PROMPT = open("/dev/tty", O_RDONLY)) == -1) { 191 PERR("can't read from tty for -p"); 192 } else { 193 TRACE = TRUE; 194 } 195 break; 196 197 case 'e': 198 /* 199 * -e[eofstr]: set/disable end-of-file. 200 * N.B. that an argument *isn't* required here; but 201 * parseargs forced an argument if not was given. The 202 * forced argument is the default... 203 */ 204 LEOF = optarg; /* can be empty */ 205 break; 206 207 case 'E': 208 /* 209 * -E eofstr: change end-of-file string. 210 * eofstr *is* required here, but can be empty: 211 */ 212 LEOF = optarg; 213 break; 214 215 case 'I': 216 /* -I replstr: Insert mode. replstr *is* required. */ 217 INSERT = PER_LINE = LEGAL = EAT_LEAD = TRUE; 218 LINE_CONT = FALSE; 219 N_ARGS = 0; 220 INSPAT = optarg; 221 if (*optarg == '\0') { 222 ermsg(_("Option requires an argument: -%c\n"), 223 c); 224 } 225 break; 226 227 case 'i': 228 /* 229 * -i [replstr]: insert mode, with *optional* replstr. 230 * N.B. that an argument *isn't* required here; if 231 * it's not given, then the string INSPAT_STR will 232 * be assumed. 233 * 234 * Since getopts(3C) doesn't handle the case of an 235 * optional variable argument at all, we have to 236 * parse this by hand: 237 */ 238 239 INSERT = PER_LINE = LEGAL = EAT_LEAD = TRUE; 240 LINE_CONT = FALSE; 241 N_ARGS = 0; 242 if ((optarg != NULL) && (*optarg != '\0')) { 243 INSPAT = optarg; 244 } else { 245 /* 246 * here, there is no next argument. so 247 * we reset INSPAT to the INSPAT_STR. 248 * we *have* to do this, as -i/I may have 249 * been given previously, and XCU4 requires 250 * that only "the last one specified takes 251 * effect". 252 */ 253 INSPAT = INSPAT_STR; 254 } 255 break; 256 257 case 'L': 258 /* 259 * -L number: # of times cmd is executed 260 * number *is* required here: 261 */ 262 PER_LINE = LINE_CONT = TRUE; 263 N_ARGS = 0; 264 INSERT = EAT_LEAD = FALSE; 265 if ((PER_LINE = atoi(optarg)) <= 0) { 266 ermsg(_("#lines must be positive int: %s\n"), 267 optarg); 268 } 269 break; 270 271 case 'l': 272 /* 273 * -l [number]: # of times cmd is executed 274 * N.B. that an argument *isn't* required here; if 275 * it's not given, then 1 is assumed. 276 * 277 * parseargs handles the optional arg processing. 278 */ 279 280 PER_LINE = LINE_CONT = LEGAL = TRUE; 281 N_ARGS = 0; 282 INSERT = EAT_LEAD = FALSE; 283 284 if ((optarg != NULL) && (*optarg != '\0')) { 285 if ((PER_LINE = atoi(optarg)) <= 0) 286 PER_LINE = 1; 287 } 288 break; 289 290 case 'n': /* -n number: # stdin args */ 291 /* 292 * -n number: # stdin args. 293 * number *is* required here: 294 */ 295 if ((N_ARGS = atoi(optarg)) <= 0) { 296 ermsg(_("#args must be positive int: %s\n"), 297 optarg); 298 } else { 299 LEGAL = DASHX || N_ARGS == 1; 300 INSERT = PER_LINE = LINE_CONT = FALSE; 301 } 302 break; 303 304 case 's': /* -s size: set max size of each arg list */ 305 BUFLIM = atoi(optarg); 306 if (BUFLIM > BUFSIZE || BUFLIM <= 0) { 307 ermsg(_("0 < max-cmd-line-size <= %d: %s\n"), 308 BUFSIZE, optarg); 309 } 310 break; 311 312 case 'x': /* -x: terminate if args > size limit */ 313 DASHX = LEGAL = TRUE; 314 break; 315 316 default: 317 /* 318 * bad argument. complain and get ready to die. 319 */ 320 usage(); 321 exit(2); 322 break; 323 } 324 } 325 326 /* 327 * if anything called ermsg(), something screwed up, so 328 * we exit early. 329 */ 330 if (OK == FALSE) { 331 usage(); 332 exit(2); 333 } 334 335 /* 336 * we're finished handling xargs's options, so now pick up 337 * the command name (if any), and it's options. 338 */ 339 340 341 mac -= optind; /* dec arg count by what we've processed */ 342 mav += optind; /* inc to current mav */ 343 344 if (mac <= 0) { /* if there're no more args to process, */ 345 cmdname = "/usr/bin/echo"; /* our default command */ 346 *ARGV++ = addarg(cmdname); /* use the default cmd. */ 347 } else { /* otherwise keep parsing rest of the string. */ 348 /* 349 * note that we can't use getopts(3C), and *must* parse 350 * this by hand, as we don't know apriori what options the 351 * command will take. 352 */ 353 cmdname = *mav; /* get the command name */ 354 355 356 /* pick up the remaining args from the command line: */ 357 while ((OK == TRUE) && (mac-- > 0)) { 358 /* 359 * while we haven't crapped out, and there's 360 * work to do: 361 */ 362 if (INSERT && ! ERR) { 363 if (strstr(*mav, INSPAT) != NULL) { 364 if (++n_inserts > MAXINSERTS) { 365 ermsg(_("too many args " 366 "with %s\n"), INSPAT); 367 ERR = TRUE; 368 } 369 psave->p_ARGV = ARGV; 370 (psave++)->p_skel = *mav; 371 } 372 } 373 *ARGV++ = addarg(*mav++); 374 } 375 } 376 377 /* pick up args from standard input */ 378 379 initlist = ARGV; 380 initsize = linesize; 381 lastarg[0] = '\0'; 382 383 while (OK) { 384 N_args = 0; 385 N_lines = 0; 386 ARGV = initlist; 387 linesize = initsize; 388 next = argbuf; 389 390 while (MORE || (lastarg[0] != '\0')) { 391 int l; 392 393 if (*lastarg != '\0') { 394 arg = strcpy(next, lastarg); 395 *lastarg = '\0'; 396 } else if ((arg = getarg(next)) == NULL) { 397 break; 398 } 399 400 l = strlen(arg) + 1; 401 linesize += l; 402 next += l; 403 404 /* Inserts are handled specially later. */ 405 if ((n_inserts == 0) && (linesize >= BUFLIM)) { 406 /* 407 * Legal indicates hard fail if the list is 408 * truncated due to size. So fail, or if we 409 * cannot create any list because it would be 410 * too big. 411 */ 412 if (LEGAL || N_args == 0) { 413 EMSG(LIST2LONG); 414 exit(2); 415 /* NOTREACHED */ 416 } 417 418 /* 419 * Otherwise just save argument for later. 420 */ 421 (void) strcpy(lastarg, arg); 422 break; 423 } 424 425 *ARGV++ = arg; 426 427 N_args++; 428 429 if ((PER_LINE && (N_lines >= PER_LINE)) || 430 (N_ARGS && (N_args >= N_ARGS))) { 431 break; 432 } 433 434 435 if ((ARGV - arglist) == MAXARGS) { 436 break; 437 } 438 } 439 440 *ARGV = NULL; 441 if (N_args == 0) { 442 /* Reached the end with no more work. */ 443 exit(exitstat); 444 } 445 446 /* insert arg if requested */ 447 448 if (!ERR && INSERT) { 449 450 p_ibuf = ins_buf; 451 ARGV--; 452 j = ibufsize = 0; 453 for (psave = saveargv; ++j <= n_inserts; ++psave) { 454 addibuf(psave); 455 if (ERR) 456 break; 457 } 458 } 459 *ARGV = NULL; 460 461 if (n_inserts > 0) { 462 /* 463 * if we've done any insertions, re-calculate the 464 * linesize. bomb out if we've exceeded our length. 465 */ 466 linesize = 0; 467 for (ARGV = arglist; *ARGV != NULL; ARGV++) { 468 linesize += strlen(*ARGV) + 1; 469 } 470 if (linesize >= BUFLIM) { 471 EMSG(LIST2LONG); 472 exit(2); 473 /* NOTREACHED */ 474 } 475 } 476 477 /* exec command */ 478 479 if (!ERR) { 480 if (!MORE && 481 (PER_LINE && N_lines == 0 || N_ARGS && N_args == 0)) 482 exit(exitstat); 483 OK = TRUE; 484 j = TRACE ? echoargs() : TRUE; 485 if (j) { 486 /* 487 * for xcu4, all invocations of cmdname must 488 * return 0, in order for us to return 0. 489 * so if we have a non-zero status here, 490 * quit immediately. 491 */ 492 exitstat |= lcall(cmdname, arglist); 493 } 494 } 495 } 496 497 if (OK) 498 return (exitstat); 499 500 /* 501 * if exitstat was set, to match XCU4 complience, 502 * return that value, otherwise, return 1. 503 */ 504 return (exitstat ? exitstat : 1); 505 } 506 507 static char * 508 addarg(char *arg) 509 { 510 linesize += (strlen(arg) + 1); 511 return (arg); 512 } 513 514 515 static void 516 store_str(char **buffer, char *str, size_t len) 517 { 518 (void) memcpy(*buffer, str, len); 519 (*buffer)[len] = '\0'; 520 *buffer += len; 521 } 522 523 524 static char * 525 getarg(char *arg) 526 { 527 char *xarg = arg; 528 wchar_t c = 0; 529 char mbc[MB_LEN_MAX]; 530 size_t len; 531 int escape = 0; 532 int inquote = 0; 533 int last = 0; 534 535 arg[0] = '\0'; 536 537 while (MORE) { 538 539 len = 0; 540 last = c; 541 c = getwchr(mbc, &len); 542 543 if (((arg - xarg) + len) > BUFLIM) { 544 EMSG2(ARG2LONG, BUFLIM); 545 exit(2); 546 ERR = TRUE; 547 return (NULL); 548 } 549 550 switch (c) { 551 case '\n': 552 if (ZERO) { 553 store_str(&arg, mbc, len); 554 continue; 555 } 556 /* 557 * NB: Some other versions rip off all of the trailing 558 * blanks. The spec only claims that this should 559 * be done for a single blank. We follow the spec. 560 */ 561 if (LINE_CONT && iswctype(last, blank)) { 562 len = 0; 563 *arg = 0; 564 continue; 565 } 566 /* FALLTHRU */ 567 568 case '\0': 569 case WEOF: /* Note WEOF == EOF */ 570 571 if (escape) { 572 EMSG(BADESCAPE); 573 ERR = TRUE; 574 return (NULL); 575 } 576 if (inquote) { 577 EMSG(MISSQUOTE); 578 ERR = TRUE; 579 return (NULL); 580 } 581 582 N_lines++; 583 break; 584 585 case '"': 586 if (ZERO || escape || (inquote == 1)) { 587 /* treat it literally */ 588 escape = 0; 589 store_str(&arg, mbc, len); 590 591 } else if (inquote == 2) { 592 /* terminating double quote */ 593 inquote = 0; 594 595 } else { 596 /* starting quoted string */ 597 inquote = 2; 598 } 599 continue; 600 601 case '\'': 602 if (ZERO || escape || (inquote == 2)) { 603 /* treat it literally */ 604 escape = 0; 605 store_str(&arg, mbc, len); 606 607 } else if (inquote == 1) { 608 /* terminating single quote */ 609 inquote = 0; 610 611 } else { 612 /* starting quoted string */ 613 inquote = 1; 614 } 615 continue; 616 617 case '\\': 618 /* 619 * Any unquoted character can be escaped by 620 * preceding it with a backslash. 621 */ 622 if (ZERO || inquote || escape) { 623 escape = 0; 624 store_str(&arg, mbc, len); 625 } else { 626 escape = 1; 627 } 628 continue; 629 630 default: 631 /* most times we will just want to store it */ 632 if (inquote || escape || ZERO || !iswctype(c, blank)) { 633 escape = 0; 634 store_str(&arg, mbc, len); 635 continue; 636 } 637 if (EAT_LEAD && last == 0) { 638 c = 0; /* Roll it back */ 639 continue; 640 } 641 if (PER_LINE) { 642 store_str(&arg, mbc, len); 643 continue; 644 } 645 646 /* unquoted blank without special handling */ 647 break; 648 } 649 650 /* 651 * At this point we are processing a complete argument. 652 */ 653 if (strcmp(xarg, LEOF) == 0 && *LEOF != '\0') { 654 MORE = FALSE; 655 return (NULL); 656 } 657 if (c == WEOF) { 658 MORE = FALSE; 659 } 660 if (xarg[0] == '\0') 661 continue; 662 break; 663 } 664 665 return (xarg[0] == '\0' ? NULL : xarg); 666 } 667 668 /* 669 * ermsg(): print out an error message, and indicate failure globally. 670 * 671 * Assumes that message has already been gettext()'d. It would be 672 * nice if we could just do the gettext() here, but we can't, since 673 * since xgettext(1M) wouldn't be able to pick up our error message. 674 */ 675 /* PRINTFLIKE1 */ 676 static void 677 ermsg(char *messages, ...) 678 { 679 va_list ap; 680 681 va_start(ap, messages); 682 683 (void) fprintf(stderr, "xargs: "); 684 (void) vfprintf(stderr, messages, ap); 685 686 va_end(ap); 687 OK = FALSE; 688 } 689 690 static int 691 echoargs() 692 { 693 char **anarg; 694 char **tanarg; /* tmp ptr */ 695 int i; 696 char reply[LINE_MAX]; 697 698 tanarg = anarg = arglist-1; 699 700 /* 701 * write out each argument, separated by a space. the tanarg 702 * nonsense is for xcu4 testsuite compliance - so that an 703 * extra space isn't echoed after the last argument. 704 */ 705 while (*++anarg) { /* while there's an argument */ 706 ++tanarg; /* follow anarg */ 707 (void) write(2, *anarg, strlen(*anarg)); 708 709 if (*++tanarg) { /* if there's another argument: */ 710 (void) write(2, " ", 1); /* add a space */ 711 --tanarg; /* reset back to anarg */ 712 } 713 } 714 if (PROMPT == -1) { 715 (void) write(2, "\n", 1); 716 return (TRUE); 717 } 718 719 (void) write(2, "?...", 4); /* ask the user for input */ 720 721 for (i = 0; i < LINE_MAX && read(PROMPT, &reply[i], 1) > 0; i++) { 722 if (reply[i] == '\n') { 723 if (i == 0) 724 return (FALSE); 725 break; 726 } 727 } 728 reply[i] = 0; 729 730 /* flush remainder of line if necessary */ 731 if (i == LINE_MAX) { 732 char bitbucket; 733 734 while ((read(PROMPT, &bitbucket, 1) > 0) && (bitbucket != '\n')) 735 ; 736 } 737 738 return (yes_check(reply)); 739 } 740 741 742 static char * 743 insert(char *pattern, char *subst) 744 { 745 static char buffer[MAXSBUF+1]; 746 int len, ipatlen; 747 char *pat; 748 char *bufend; 749 char *pbuf; 750 751 len = strlen(subst); 752 ipatlen = strlen(INSPAT) - 1; 753 pat = pattern - 1; 754 pbuf = buffer; 755 bufend = &buffer[MAXSBUF]; 756 757 while (*++pat) { 758 if (strncmp(pat, INSPAT, ipatlen + 1) == 0) { 759 if (pbuf + len >= bufend) { 760 break; 761 } else { 762 (void) strcpy(pbuf, subst); 763 pat += ipatlen; 764 pbuf += len; 765 } 766 } else { 767 *pbuf++ = *pat; 768 if (pbuf >= bufend) 769 break; 770 } 771 } 772 773 if (!*pat) { 774 *pbuf = '\0'; 775 return (buffer); 776 } else { 777 ermsg(gettext("Maximum argument size with insertion via %s's " 778 "exceeded\n"), INSPAT); 779 ERR = TRUE; 780 return (NULL); 781 } 782 } 783 784 785 static void 786 addibuf(struct inserts *p) 787 { 788 char *newarg, *skel, *sub; 789 int l; 790 791 skel = p->p_skel; 792 sub = *ARGV; 793 newarg = insert(skel, sub); 794 if (ERR) 795 return; 796 797 l = strlen(newarg) + 1; 798 if ((ibufsize += l) > MAXIBUF) { 799 EMSG(IBUFOVERFLOW); 800 ERR = TRUE; 801 } 802 (void) strcpy(p_ibuf, newarg); 803 *(p->p_ARGV) = p_ibuf; 804 p_ibuf += l; 805 } 806 807 808 /* 809 * getwchr(): get the next wide character. 810 * description: 811 * we get the next character from stdin. This returns WEOF if no 812 * character is present. If ZERO is set, it gets a single byte instead 813 * a wide character. 814 */ 815 static wint_t 816 getwchr(char *mbc, size_t *sz) 817 { 818 size_t i; 819 int c; 820 wchar_t wch; 821 822 i = 0; 823 while (i < MB_CUR_MAX) { 824 825 if ((c = fgetc(stdin)) == EOF) { 826 827 if (i == 0) { 828 /* TRUE EOF has been reached */ 829 return (WEOF); 830 } 831 832 /* 833 * We have some characters in our buffer still so it 834 * must be an invalid character right before EOF. 835 */ 836 break; 837 } 838 mbc[i++] = (char)c; 839 840 /* If this succeeds then we are done */ 841 if (ZERO) { 842 *sz = i; 843 return ((char)c); 844 } 845 if (mbtowc(&wch, mbc, i) != -1) { 846 *sz = i; 847 return ((wint_t)wch); 848 } 849 } 850 851 /* 852 * We have now encountered an illegal character sequence. 853 * There is nothing much we can do at this point but 854 * return an error. If we attempt to recover we may in fact 855 * return garbage as arguments, from the customer's point 856 * of view. After all what if they are feeding us a file 857 * generated in another locale? 858 */ 859 errno = EILSEQ; 860 PERR(CORRUPTFILE); 861 exit(1); 862 /* NOTREACHED */ 863 } 864 865 866 static int 867 lcall(char *sub, char **subargs) 868 { 869 int retcode, retry = 0; 870 pid_t iwait, child; 871 872 for (;;) { 873 switch (child = fork()) { 874 default: 875 while ((iwait = wait(&retcode)) != child && 876 iwait != (pid_t)-1) 877 ; 878 if (iwait == (pid_t)-1) { 879 PERR(WAITFAIL); 880 exit(122); 881 /* NOTREACHED */ 882 } 883 if (WIFSIGNALED(retcode)) { 884 EMSG2(CHILDSIG, WTERMSIG(retcode)); 885 exit(125); 886 /* NOTREACHED */ 887 } 888 if ((WEXITSTATUS(retcode) & 0377) == 0377) { 889 EMSG(CHILDFAIL); 890 exit(124); 891 /* NOTREACHED */ 892 } 893 return (WEXITSTATUS(retcode)); 894 case 0: 895 (void) execvp(sub, subargs); 896 PERR(EXECFAIL); 897 if (errno == EACCES) 898 exit(126); 899 exit(127); 900 /* NOTREACHED */ 901 case -1: 902 if (errno != EAGAIN && retry++ < FORK_RETRY) { 903 PERR(FORKFAIL); 904 exit(123); 905 } 906 (void) sleep(1); 907 } 908 } 909 } 910 911 912 static void 913 usage() 914 { 915 ermsg(_(USAGEMSG)); 916 OK = FALSE; 917 } 918 919 920 921 /* 922 * parseargs(): modify the args 923 * since the -e, -i and -l flags all take optional subarguments, 924 * and getopts(3C) is clueless about this nonsense, we change the 925 * our local argument count and strings to separate this out, 926 * and make it easier to handle via getopts(3c). 927 * 928 * -e -> "-e "" 929 * -e3 -> "-e "3" 930 * -Estr -> "-E "str" 931 * -i -> "-i "{}" 932 * -irep -> "-i "rep" 933 * -l -> "-l "1" 934 * -l10 -> "-l "10" 935 * 936 * since the -e, -i and -l flags all take optional subarguments, 937 */ 938 static void 939 parseargs(int ac, char **av) 940 { 941 int i; /* current argument */ 942 int cflag; /* 0 = not processing cmd arg */ 943 944 if ((mav = malloc((ac * 2 + 1) * sizeof (char *))) == NULL) { 945 PERR(MALLOCFAIL); 946 exit(1); 947 } 948 949 /* for each argument, see if we need to change things: */ 950 for (i = mac = cflag = 0; (av[i] != NULL) && i < ac; i++, mac++) { 951 if ((mav[mac] = strdup(av[i])) == NULL) { 952 PERR(MALLOCFAIL); 953 exit(1); 954 } 955 956 /* -- has been found or argument list is fully processes */ 957 if (cflag) 958 continue; 959 960 /* 961 * if we're doing special processing, and we've got a flag 962 */ 963 else if ((av[i][0] == '-') && (av[i][1] != NULL)) { 964 char *def; 965 966 switch (av[i][1]) { 967 case 'e': 968 def = ""; /* -e with no arg turns off eof */ 969 goto process_special; 970 case 'i': 971 def = INSPAT_STR; 972 goto process_special; 973 case 'l': 974 def = "1"; 975 process_special: 976 /* 977 * if there's no sub-option, we *must* add 978 * a default one. this is because xargs must 979 * be able to distinguish between a valid 980 * suboption, and a command name. 981 */ 982 if (av[i][2] == NULL) { 983 mav[++mac] = strdup(def); 984 } else { 985 /* clear out our version: */ 986 mav[mac][2] = NULL; 987 mav[++mac] = strdup(&av[i][2]); 988 } 989 if (mav[mac] == NULL) { 990 PERR(MALLOCFAIL); 991 exit(1); 992 } 993 break; 994 995 /* flags with required subarguments: */ 996 997 /* 998 * there are two separate cases here. either the 999 * flag can have the normal XCU4 handling 1000 * (of the form: -X subargument); or it can have 1001 * the old solaris 2.[0-4] handling (of the 1002 * form: -Xsubargument). in order to maintain 1003 * backwards compatibility, we must support the 1004 * latter case. we handle the latter possibility 1005 * first so both the old solaris way of handling 1006 * and the new XCU4 way of handling things are allowed. 1007 */ 1008 case 'n': /* FALLTHROUGH */ 1009 case 's': /* FALLTHROUGH */ 1010 case 'E': /* FALLTHROUGH */ 1011 case 'I': /* FALLTHROUGH */ 1012 case 'L': 1013 /* 1014 * if the second character isn't null, then 1015 * the user has specified the old syntax. 1016 * we move the subargument into our 1017 * mod'd argument list. 1018 */ 1019 if (av[i][2] != NULL) { 1020 /* first clean things up: */ 1021 mav[mac][2] = NULL; 1022 1023 /* now add the separation: */ 1024 ++mac; /* inc to next mod'd arg */ 1025 if ((mav[mac] = strdup(&av[i][2])) == 1026 NULL) { 1027 PERR(MALLOCFAIL); 1028 exit(1); 1029 } 1030 break; 1031 } 1032 i++; 1033 mac++; 1034 1035 if (av[i] == NULL) { 1036 mav[mac] = NULL; 1037 return; 1038 } 1039 if ((mav[mac] = strdup(av[i])) == NULL) { 1040 PERR(MALLOCFAIL); 1041 exit(1); 1042 } 1043 break; 1044 1045 /* flags */ 1046 case 'p' : 1047 case 't' : 1048 case 'x' : 1049 case '0' : 1050 break; 1051 1052 case '-' : 1053 default: 1054 /* 1055 * here we've hit the cmd argument. so 1056 * we'll stop special processing, as the 1057 * cmd may have a "-i" etc., argument, 1058 * and we don't want to add a "" to it. 1059 */ 1060 cflag = 1; 1061 break; 1062 } 1063 } else if (i > 0) { /* if we're not the 1st arg */ 1064 /* 1065 * if it's not a flag, then it *must* be the cmd. 1066 * set cflag, so we don't mishandle the -[eil] flags. 1067 */ 1068 cflag = 1; 1069 } 1070 } 1071 1072 mav[mac] = NULL; 1073 } 1074