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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 #include <stdio.h> 33 #include <sys/types.h> 34 #include <sys/wait.h> 35 #include <unistd.h> 36 #include <fcntl.h> 37 #include <string.h> 38 #include <stdarg.h> 39 #include <stdlib.h> 40 #include <limits.h> 41 #include <wchar.h> 42 #include <locale.h> 43 #include <langinfo.h> 44 #include <stropts.h> 45 #include <poll.h> 46 #include <errno.h> 47 #include <stdarg.h> 48 #include "getresponse.h" 49 50 #define HEAD 0 51 #define TAIL 1 52 #define FALSE 0 53 #define TRUE 1 54 #define MAXSBUF 255 55 #define MAXIBUF 512 56 #define MAXINSERTS 5 57 #define BUFSIZE LINE_MAX 58 #define MAXARGS 255 59 #define INSPAT_STR "{}" /* default replstr string for -[Ii] */ 60 #define FORK_RETRY 5 61 62 #define QBUF_STARTLEN 255 /* start size of growable string buffer */ 63 #define QBUF_INC 100 /* how much to grow a growable string by */ 64 65 static wctype_t blank; 66 static char *arglist[MAXARGS+1]; 67 static char argbuf[BUFSIZE+1]; 68 static char *next = argbuf; 69 static char *lastarg = ""; 70 static char **ARGV = arglist; 71 static char *LEOF = "_"; 72 static char *INSPAT = INSPAT_STR; 73 static char ins_buf[MAXIBUF]; 74 static char *p_ibuf; 75 76 static struct inserts { 77 char **p_ARGV; /* where to put newarg ptr in arg list */ 78 char *p_skel; /* ptr to arg template */ 79 } saveargv[MAXINSERTS]; 80 81 static off_t file_offset = 0; 82 static int PROMPT = -1; 83 static int BUFLIM = BUFSIZE; 84 static int N_ARGS = 0; 85 static int N_args = 0; 86 static int N_lines = 0; 87 static int DASHX = FALSE; 88 static int MORE = TRUE; 89 static int PER_LINE = FALSE; 90 static int ERR = FALSE; 91 static int OK = TRUE; 92 static int LEGAL = FALSE; 93 static int TRACE = FALSE; 94 static int INSERT = FALSE; 95 static int linesize = 0; 96 static int ibufsize = 0; 97 static int exitstat = 0; /* our exit status */ 98 static int mac; /* modified argc, after parsing */ 99 static char **mav; /* modified argv, after parsing */ 100 static int n_inserts; /* # of insertions. */ 101 static int inquote = 0; /* processing a quoted string */ 102 static int save_index = 0; 103 104 /* 105 * the pio structure is used to save any pending input before the 106 * user replies to a prompt. the pending input is saved here, 107 * for the appropriate processing later. 108 */ 109 typedef struct pio { 110 struct pio *next; /* next in stack */ 111 char *start; /* starting addr of the buffer */ 112 char *cur; /* ptr to current char in buf */ 113 size_t length; /* number of bytes remaining */ 114 } pio; 115 116 static pio *queued_data = NULL; 117 118 /* our usage message: */ 119 #define USAGEMSG "Usage: xargs: [-t] [-p] [-e[eofstr]] [-E eofstr] "\ 120 "[-I replstr] [-i[replstr]] [-L #] [-l[#]] [-n # [-x]] [-s size] "\ 121 "[cmd [args ...]]\n" 122 123 static int echoargs(); 124 static int getchr(void); 125 static wchar_t getwchr(void); 126 static void ungetwchr(wchar_t); 127 static int lcall(char *sub, char **subargs); 128 static int xindex(char *as1, char *as2); 129 static void addibuf(struct inserts *p); 130 static void ermsg(char *messages, ...); 131 static char *addarg(char *arg); 132 static char *checklen(char *arg); 133 static size_t store_wchr(char **, size_t *, size_t, wchar_t); 134 static char *getarg(); 135 static char *insert(char *pattern, char *subst); 136 static void usage(); 137 static void parseargs(); 138 static void saveinput(); 139 140 int 141 main(int argc, char **argv) 142 { 143 int j; 144 struct inserts *psave; 145 int c; 146 int initsize; 147 char *cmdname, *initbuf, **initlist; 148 149 150 /* initialization */ 151 blank = wctype("blank"); 152 n_inserts = 0; 153 psave = saveargv; 154 (void) setlocale(LC_ALL, ""); 155 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 156 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 157 #endif 158 (void) textdomain(TEXT_DOMAIN); 159 if (init_yes() < 0) { 160 ermsg(gettext(ERR_MSG_INIT_YES), strerror(errno)); 161 exit(1); 162 } 163 164 parseargs(argc, argv); 165 166 /* handling all of xargs arguments: */ 167 while ((c = getopt(mac, mav, "tpe:E:I:i:L:l:n:s:x")) != EOF) { 168 switch (c) { 169 case 't': /* -t: turn trace mode on */ 170 TRACE = TRUE; 171 break; 172 173 case 'p': /* -p: turn on prompt mode. */ 174 if ((PROMPT = open("/dev/tty", O_RDONLY)) == -1) { 175 perror(gettext("can't read from tty for -p")); 176 } else { 177 TRACE = TRUE; 178 } 179 break; 180 181 case 'e': 182 /* 183 * -e[eofstr]: set/disable end-of-file. 184 * N.B. that an argument *isn't* required here; but 185 * parseargs forced an argument if not was given. The 186 * forced argument is the default... 187 */ 188 LEOF = optarg; /* can be empty */ 189 break; 190 191 case 'E': 192 /* 193 * -E eofstr: change end-of-file string. 194 * eofstr *is* required here, but can be empty: 195 */ 196 LEOF = optarg; 197 break; 198 199 case 'I': 200 /* -I replstr: Insert mode. replstr *is* required. */ 201 INSERT = PER_LINE = LEGAL = TRUE; 202 N_ARGS = 0; 203 INSPAT = optarg; 204 if (*optarg == '\0') { 205 ermsg(gettext( 206 "Option requires an argument: -%c\n"), c); 207 } 208 break; 209 210 case 'i': 211 /* 212 * -i [replstr]: insert mode, with *optional* replstr. 213 * N.B. that an argument *isn't* required here; if 214 * it's not given, then the string INSPAT_STR will 215 * be assumed. 216 * 217 * Since getopts(3C) doesn't handle the case of an 218 * optional variable argument at all, we have to 219 * parse this by hand: 220 */ 221 222 INSERT = PER_LINE = LEGAL = TRUE; 223 N_ARGS = 0; 224 if ((optarg != NULL) && (*optarg != '\0')) { 225 INSPAT = optarg; 226 } else { 227 /* 228 * here, there is no next argument. so 229 * we reset INSPAT to the INSPAT_STR. 230 * we *have* to do this, as -i/I may have 231 * been given previously, and XCU4 requires 232 * that only "the last one specified takes 233 * effect". 234 */ 235 INSPAT = INSPAT_STR; 236 } 237 break; 238 239 case 'L': 240 /* 241 * -L number: # of times cmd is executed 242 * number *is* required here: 243 */ 244 PER_LINE = TRUE; 245 N_ARGS = 0; 246 INSERT = FALSE; 247 if ((PER_LINE = atoi(optarg)) <= 0) { 248 ermsg(gettext("#lines must be positive " 249 "int: %s\n"), optarg); 250 } 251 break; 252 253 case 'l': 254 /* 255 * -l [number]: # of times cmd is executed 256 * N.B. that an argument *isn't* required here; if 257 * it's not given, then 1 is assumed. 258 * 259 * parseargs handles the optional arg processing. 260 */ 261 262 PER_LINE = LEGAL = TRUE; /* initialization */ 263 N_ARGS = 0; 264 INSERT = FALSE; 265 266 if ((optarg != NULL) && (*optarg != '\0')) { 267 if ((PER_LINE = atoi(optarg)) <= 0) 268 PER_LINE = 1; 269 } 270 break; 271 272 case 'n': /* -n number: # stdin args */ 273 /* 274 * -n number: # stdin args. 275 * number *is* required here: 276 */ 277 if ((N_ARGS = atoi(optarg)) <= 0) { 278 ermsg(gettext("#args must be positive " 279 "int: %s\n"), optarg); 280 } else { 281 LEGAL = DASHX || N_ARGS == 1; 282 INSERT = PER_LINE = FALSE; 283 } 284 break; 285 286 case 's': /* -s size: set max size of each arg list */ 287 BUFLIM = atoi(optarg); 288 if (BUFLIM > BUFSIZE || BUFLIM <= 0) { 289 ermsg(gettext( 290 "0 < max-cmd-line-size <= %d: " 291 "%s\n"), BUFSIZE, optarg); 292 } 293 break; 294 295 case 'x': /* -x: terminate if args > size limit */ 296 DASHX = LEGAL = TRUE; 297 break; 298 299 default: 300 /* 301 * bad argument. complain and get ready to die. 302 */ 303 ERR = TRUE; 304 usage(); 305 306 exit(2); 307 break; 308 } 309 } 310 311 /* 312 * if anything called ermsg(), something screwed up, so 313 * we exit early. 314 */ 315 if (OK == FALSE) { 316 ERR = TRUE; 317 usage(); 318 exit(2); 319 } 320 321 /* 322 * we're finished handling xargs's options, so now pick up 323 * the command name (if any), and it's options. 324 */ 325 326 327 mac -= optind; /* dec arg count by what we've processed */ 328 mav += optind; /* inc to current mav */ 329 330 if (mac <= 0) { /* if there're no more args to process, */ 331 cmdname = "/usr/bin/echo"; /* our default command */ 332 *ARGV++ = addarg(cmdname); /* use the default cmd. */ 333 } else { /* otherwise keep parsing rest of the string. */ 334 /* 335 * note that we can't use getopts(3C), and *must* parse 336 * this by hand, as we don't know apriori what options the 337 * command will take. 338 */ 339 cmdname = *mav; /* get the command name */ 340 341 342 /* pick up the remaining args from the command line: */ 343 while ((OK == TRUE) && (mac-- > 0)) { 344 /* 345 * while we haven't crapped out, and there's 346 * work to do: 347 */ 348 if (INSERT && ! ERR) { 349 if (xindex(*mav, INSPAT) != -1) { 350 if (++n_inserts > MAXINSERTS) { 351 ermsg(gettext("too many args " 352 "with %s\n"), INSPAT); 353 ERR = TRUE; 354 } 355 psave->p_ARGV = ARGV; 356 (psave++)->p_skel = *mav; 357 } 358 } 359 *ARGV++ = addarg(*mav++); 360 } 361 } 362 363 /* pick up args from standard input */ 364 365 initbuf = next; 366 initlist = ARGV; 367 initsize = linesize; 368 369 while (OK && MORE) { 370 N_args = 0; 371 N_lines = 0; 372 next = initbuf; 373 ARGV = initlist; 374 linesize = initsize; 375 if (*lastarg) { 376 *ARGV++ = addarg(lastarg); 377 lastarg = ""; 378 } 379 380 while (((*ARGV++ = getarg()) != NULL) && OK) { 381 if ((ARGV - arglist) == MAXARGS) { 382 save_index = ARGV - arglist; 383 break; 384 } 385 } 386 if ((save_index == MAXARGS) && !MORE && (N_args == 0)) { 387 /* there were no more args after filling arglist */ 388 exit(exitstat); 389 } 390 391 /* insert arg if requested */ 392 393 if (!ERR && INSERT) { 394 if ((!MORE) && (N_lines == 0)) { 395 exit(exitstat); 396 } 397 /* no more input lines */ 398 p_ibuf = ins_buf; 399 ARGV--; 400 j = ibufsize = 0; 401 for (psave = saveargv; ++j <= n_inserts; ++psave) { 402 addibuf(psave); 403 if (ERR) 404 break; 405 } 406 } 407 *ARGV = 0; 408 409 if (n_inserts > 0) { 410 int t_ninserts; 411 412 /* 413 * if we've done any insertions, re-calculate the 414 * linesize. bomb out if we've exceeded our length. 415 */ 416 t_ninserts = n_inserts; 417 n_inserts = 0; /* inserts have been done */ 418 linesize = 0; /* recalculate this */ 419 420 /* for each current argument in the list: */ 421 for (ARGV = arglist; *ARGV != NULL; ARGV++) { 422 /* recalculate everything. */ 423 if (checklen(*ARGV) != 0) { 424 if (N_ARGS && (N_args >= N_ARGS)) { 425 N_lines = N_args = 0; 426 OK = FALSE; 427 ERR = TRUE; 428 } 429 } 430 } 431 n_inserts = t_ninserts; 432 } 433 434 /* exec command */ 435 436 if (!ERR) { 437 if (!MORE && 438 (PER_LINE && N_lines == 0 || N_ARGS && N_args == 0)) 439 exit(exitstat); 440 OK = TRUE; 441 j = TRACE ? echoargs() : TRUE; 442 if (j) { 443 /* 444 * for xcu4, all invocations of cmdname must 445 * return 0, in order for us to return 0. 446 * so if we have a non-zero status here, 447 * quit immediately. 448 */ 449 if ((exitstat |= lcall(cmdname, arglist)) == 0) 450 continue; 451 } 452 } 453 } 454 455 (void) lseek(0, file_offset, SEEK_SET); 456 if (OK) { 457 return (exitstat); 458 } else { 459 /* 460 * if exitstat was set, to match XCU4 complience, 461 * return that value, otherwise, return 1. 462 */ 463 return (exitstat ? exitstat : 1); 464 } 465 } 466 467 static void 468 queue(char *buffer, int len, int where) 469 { 470 pio *new, *element; 471 472 if ((new = malloc(sizeof (pio))) == NULL) { 473 perror(gettext("xargs: Memory allocation failure")); 474 exit(1); 475 } 476 new->cur = new->start = buffer; 477 new->length = len; 478 479 if (where == TAIL) { 480 new->next = NULL; 481 if (queued_data == NULL) { 482 queued_data = new; 483 } else { 484 element = queued_data; 485 while (element->next != NULL) { 486 element = element->next; 487 } 488 element->next = new; 489 } 490 } else { 491 file_offset -= len; 492 new->next = queued_data; 493 queued_data = new; 494 } 495 } 496 497 static char * 498 checklen(char *arg) 499 { 500 int oklen; 501 502 oklen = TRUE; 503 linesize += strlen(arg) + 1; 504 if (linesize >= BUFLIM) { 505 /* 506 * we skip this if there're inserts. we'll handle the 507 * argument counting after all the insertions have 508 * been done. 509 */ 510 if (n_inserts == 0) { 511 lastarg = arg; 512 oklen = OK = FALSE; 513 514 if (LEGAL) { 515 ERR = TRUE; 516 ermsg(gettext("arg list too long\n")); 517 } else if (N_args > 1) { 518 N_args = 1; 519 } else { 520 ermsg(gettext("a single arg was greater than " 521 "the max arglist size of %d characters\n"), 522 BUFLIM); 523 ERR = TRUE; 524 } 525 } 526 } 527 return (oklen ? arg : 0); 528 } 529 530 static char * 531 addarg(char *arg) 532 { 533 if (checklen(arg) != 0) { 534 (void) strcpy(next, arg); 535 arg = next; 536 next += strlen(arg) + 1; 537 return (arg); 538 } 539 return ((char *)0); 540 } 541 542 /* 543 * store_wchr() : append a wchar_t to a char buffer, resize buffer if required. 544 * 545 * Given a pointer to the beginning of a string buffer, the length of the 546 * buffer and an offset indicating the next place to write within that 547 * buffer, the passed wchar_t will be appended to the buffer if there is 548 * enough space. If there is not enough space, an attempt to reallocate the 549 * buffer will be made and if successful the passed pointer and size will be 550 * updated to describe the reallocated block. Returns the new value for 551 * 'offset' (it will be incremented by the number of bytes written). 552 */ 553 static size_t 554 store_wchr(char **buffer, size_t *buflen, size_t offset, wchar_t c) 555 { 556 int bytes; 557 558 /* 559 * Make sure that there is enough room in the buffer to store the 560 * maximum length of c. 561 */ 562 if ((offset + MB_CUR_MAX) > *buflen) { 563 /* 564 * Not enough room so attempt to reallocate. Add 'MB_CUR_MAX' to 565 * buffer length to ensure that there is always enough room to 566 * store 'c' if realloc succeeds, no matter what QBUF_INC is 567 * defined as. 568 */ 569 *buflen += (QBUF_INC + MB_CUR_MAX); 570 if ((*buffer = realloc(*buffer, *buflen)) == NULL) { 571 perror(gettext("xargs: Memory allocation failure")); 572 exit(1); 573 } 574 } 575 /* store bytes from wchar into buffer */ 576 bytes = wctomb(*buffer + offset, c); 577 if (bytes == -1) { 578 /* char was invalid */ 579 bytes = 1; 580 *(*buffer + offset) = (char)c; 581 } 582 583 /* return new value for offset */ 584 return (offset + bytes); 585 } 586 587 static char * 588 getarg() 589 { 590 int bytes; 591 wchar_t c; 592 char *arg; 593 char *retarg, *requeue_buf; 594 size_t requeue_offset = 0, requeue_len; 595 char mbc[MB_LEN_MAX]; 596 597 while (iswspace(c = getwchr()) || c == '\n') 598 ; 599 600 if (c == '\0') { 601 MORE = FALSE; 602 return (0); 603 } 604 605 /* 606 * While we are reading in an argument, it is possible that we will 607 * reach the maximum length of the overflow buffer and we'll have to 608 * requeue what we have read so far. To handle this we allocate an 609 * initial buffer here which will keep an unprocessed copy of the data 610 * that we read in (this buffer will grow as required). 611 */ 612 requeue_len = (size_t)QBUF_STARTLEN; 613 if ((requeue_buf = (char *)malloc(requeue_len)) == NULL) { 614 perror(gettext("xargs: Memory allocation failure")); 615 exit(1); 616 } 617 618 for (arg = next; ; c = getwchr()) { 619 bytes = wctomb(mbc, c); 620 621 /* 622 * Store the char that we have read before processing it in case 623 * the current argument needs to be requeued. 624 */ 625 requeue_offset = store_wchr(&requeue_buf, &requeue_len, 626 requeue_offset, c); 627 628 /* Check for overflow the input buffer */ 629 if ((next + ((bytes == -1) ? 1 : bytes)) >= &argbuf[BUFLIM]) { 630 /* 631 * It's only an error if there are no Args in buffer 632 * already. 633 */ 634 if ((N_ARGS || PER_LINE) && LEGAL) { 635 ERR = TRUE; 636 ermsg(gettext("Argument list too long\n")); 637 free(requeue_buf); 638 return (0); 639 } else if (N_args == 0) { 640 lastarg = ""; 641 ERR = TRUE; 642 ermsg(gettext("A single arg was greater than " 643 "the max arglist size of %d characters\n"), 644 BUFSIZE); 645 free(requeue_buf); 646 return (0); 647 } 648 /* 649 * Otherwise we put back the current argument 650 * and use what we have collected so far... 651 */ 652 queue(requeue_buf, requeue_offset, HEAD); 653 /* reset inquote because we have requeued the quotes */ 654 inquote = 0; 655 return (NULL); 656 } 657 658 659 if (iswctype(c, blank) && inquote == 0) { 660 if (INSERT) { 661 if (bytes == -1) { 662 *next++ = (char)c; 663 } else { 664 (void) wctomb(next, c); 665 next += bytes; 666 } 667 continue; 668 } 669 670 /* skip over trailing whitespace till next arg */ 671 while (iswctype((c = getwchr()), blank) && 672 (c != '\n') && (c != '\0')) 673 ; 674 675 /* 676 * if there was space till end of line then the last 677 * character was really a newline... 678 */ 679 if (c == L'\n' || c == L'\0') { 680 ungetwchr(L'\n'); 681 } else { 682 /* later code needs to know this was a space */ 683 ungetwchr(c); 684 c = L' '; 685 } 686 goto end_arg; 687 } 688 switch (c) { 689 case L'\0': 690 case L'\n': 691 if (inquote) { 692 *next++ = '\0'; 693 ermsg(gettext("Missing quote: %s\n"), arg); 694 ERR = TRUE; 695 free(requeue_buf); 696 return (0); 697 } 698 699 N_lines++; 700 end_arg: *next++ = '\0'; 701 /* we finished without requeuing so free requeue_buf */ 702 free(requeue_buf); 703 if ((strcmp(arg, LEOF) == 0 && *LEOF != '\0') || 704 (c == '\0' && strlen(arg) == 0)) { 705 MORE = FALSE; 706 /* absorb the rest of the line */ 707 if ((c != '\n') && (c != '\0')) 708 while (c = getwchr()) 709 if ((c == '\n') || (c == '\0')) 710 break; 711 if (strcmp(arg, LEOF) == 0 && *LEOF != '\0') { 712 /* 713 * Encountered EOF string. 714 * Don't read any more lines. 715 */ 716 N_lines = 0; 717 } 718 return (0); 719 } else { 720 ++N_args; 721 if (retarg = checklen(arg)) { 722 if ((PER_LINE && 723 N_lines >= PER_LINE && 724 (c == '\0' || c == '\n')) || 725 (N_ARGS && N_args >= N_ARGS)) { 726 N_lines = N_args = 0; 727 lastarg = ""; 728 OK = FALSE; 729 } 730 } 731 return (retarg); 732 } 733 734 case '"': 735 if (inquote == 1) /* in single quoted string */ 736 goto is_default; 737 if (inquote == 2) /* terminating double quote */ 738 inquote = 0; 739 else /* starting quoted string */ 740 inquote = 2; 741 break; 742 743 case '\'': 744 if (inquote == 2) /* in double quoted string */ 745 goto is_default; 746 if (inquote == 1) /* terminating single quote */ 747 inquote = 0; 748 else /* starting quoted string */ 749 inquote = 1; 750 break; 751 752 case L'\\': 753 /* 754 * Any unquoted character can be escaped by 755 * preceding it with a backslash. 756 */ 757 if (inquote == 0) { 758 c = getwchr(); 759 /* store quoted char for potential requeueing */ 760 requeue_offset = store_wchr(&requeue_buf, 761 &requeue_len, requeue_offset, c); 762 } 763 764 default: 765 is_default: if (bytes == -1) { 766 *next++ = (char)c; 767 } else { 768 (void) wctomb(next, c); 769 next += bytes; 770 } 771 break; 772 } 773 } 774 } 775 776 777 /* 778 * ermsg(): print out an error message, and indicate failure globally. 779 * 780 * Assumes that message has already been gettext()'d. It would be 781 * nice if we could just do the gettext() here, but we can't, since 782 * since xgettext(1M) wouldn't be able to pick up our error message. 783 */ 784 /* PRINTFLIKE1 */ 785 static void 786 ermsg(char *messages, ...) 787 { 788 va_list ap; 789 790 va_start(ap, messages); 791 792 (void) fprintf(stderr, "xargs: "); 793 (void) vfprintf(stderr, messages, ap); 794 795 va_end(ap); 796 OK = FALSE; 797 } 798 799 static int 800 echoargs() 801 { 802 char **anarg; 803 char **tanarg; /* tmp ptr */ 804 int i; 805 char reply[LINE_MAX]; 806 807 tanarg = anarg = arglist-1; 808 809 /* 810 * write out each argument, separated by a space. the tanarg 811 * nonsense is for xcu4 testsuite compliance - so that an 812 * extra space isn't echoed after the last argument. 813 */ 814 while (*++anarg) { /* while there's an argument */ 815 ++tanarg; /* follow anarg */ 816 (void) write(2, *anarg, strlen(*anarg)); 817 818 if (*++tanarg) { /* if there's another argument: */ 819 (void) write(2, " ", 1); /* add a space */ 820 --tanarg; /* reset back to anarg */ 821 } 822 } 823 if (PROMPT == -1) { 824 (void) write(2, "\n", 1); 825 return (TRUE); 826 } 827 828 /* 829 * at this point, there may be unexpected input pending on stdin, 830 * if one has used the -n flag. this presents a problem, because 831 * if we simply do a read(), we'll get the extra input, instead 832 * of our desired y/n input. so, we see if there's any extra 833 * input, and if there is, then we will store it. 834 */ 835 saveinput(); 836 837 (void) write(2, "?...", 4); /* ask the user for input */ 838 839 for (i = 0; i < LINE_MAX && read(PROMPT, &reply[i], 1) > 0; i++) { 840 if (reply[i] == '\n') { 841 if (i == 0) 842 return (FALSE); 843 break; 844 } 845 } 846 reply[i] = 0; 847 848 /* flush remainder of line if necessary */ 849 if (i == LINE_MAX) { 850 char bitbucket; 851 852 while ((read(PROMPT, &bitbucket, 1) > 0) && (bitbucket != '\n')) 853 ; 854 } 855 856 return (yes_check(reply)); 857 } 858 859 860 static char * 861 insert(char *pattern, char *subst) 862 { 863 static char buffer[MAXSBUF+1]; 864 int len, ipatlen; 865 char *pat; 866 char *bufend; 867 char *pbuf; 868 869 len = strlen(subst); 870 ipatlen = strlen(INSPAT) - 1; 871 pat = pattern - 1; 872 pbuf = buffer; 873 bufend = &buffer[MAXSBUF]; 874 875 while (*++pat) { 876 if (xindex(pat, INSPAT) == 0) { 877 if (pbuf + len >= bufend) { 878 break; 879 } else { 880 (void) strcpy(pbuf, subst); 881 pat += ipatlen; 882 pbuf += len; 883 } 884 } else { 885 *pbuf++ = *pat; 886 if (pbuf >= bufend) 887 break; 888 } 889 } 890 891 if (!*pat) { 892 *pbuf = '\0'; 893 return (buffer); 894 } else { 895 ermsg(gettext("Maximum argument size with insertion via %s's " 896 "exceeded\n"), INSPAT); 897 ERR = TRUE; 898 return (0); 899 } 900 } 901 902 903 static void 904 addibuf(struct inserts *p) 905 { 906 char *newarg, *skel, *sub; 907 int l; 908 909 skel = p->p_skel; 910 sub = *ARGV; 911 linesize -= strlen(skel) + 1; 912 newarg = insert(skel, sub); 913 if (ERR) 914 return; 915 916 if (checklen(newarg)) { 917 if ((ibufsize += (l = strlen(newarg) + 1)) > MAXIBUF) { 918 ermsg(gettext("Insert buffer overflow\n")); 919 ERR = TRUE; 920 } 921 (void) strcpy(p_ibuf, newarg); 922 *(p->p_ARGV) = p_ibuf; 923 p_ibuf += l; 924 } 925 } 926 927 928 /* 929 * getchr(): get the next character. 930 * description: 931 * we get the next character from pio.structure, if there's a character 932 * to get. this may happen when we've had to flush stdin=/dev/tty, 933 * but still wanted to preserve the characters for later processing. 934 * 935 * otherwise we just get the character from stdin. 936 */ 937 static int 938 getchr(void) 939 { 940 char c; 941 942 do { 943 if (queued_data == NULL) { 944 char *buffer; 945 int len; 946 947 if ((buffer = malloc(BUFSIZE)) == NULL) { 948 perror(gettext( 949 "xargs: Memory allocation failure")); 950 exit(1); 951 } 952 953 if ((len = read(0, buffer, BUFSIZE)) == 0) 954 return (0); 955 if (len == -1) { 956 perror(gettext("xargs: Read failure")); 957 exit(1); 958 } 959 960 queue(buffer, len, TAIL); 961 } 962 963 file_offset++; 964 c = *queued_data->cur++; /* get the next character */ 965 if (--queued_data->length == 0) { /* at the end of buffer? */ 966 pio *nxt = queued_data->next; 967 968 free(queued_data->start); 969 free(queued_data); 970 queued_data = nxt; 971 } 972 } while (c == '\0'); 973 return (c); 974 } 975 976 977 static wchar_t 978 getwchr(void) 979 { 980 int i; 981 wchar_t wch; 982 unsigned char buffer[MB_LEN_MAX + 1]; 983 984 for (i = 0; i < (int)MB_CUR_MAX; ) { 985 if ((buffer[i++] = getchr()) == NULL) { 986 /* We have reached EOF */ 987 if (i == 1) { 988 /* TRUE EOF has been reached */ 989 return (NULL); 990 } 991 /* 992 * We have some characters in our buffer still so it 993 * must be an invalid character right before EOF. 994 */ 995 break; 996 } 997 998 /* If this succeeds then we are done */ 999 if (mbtowc(&wch, (char *)buffer, i) != -1) 1000 return (wch); 1001 } 1002 1003 /* 1004 * We have now encountered an illegal character sequence. 1005 * There is nothing much we can do at this point but 1006 * return an error. If we attempt to recover we may in fact 1007 * return garbage as arguments, from the customer's point 1008 * of view. After all what if they are feeding us a file 1009 * generated in another locale? 1010 */ 1011 errno = EILSEQ; 1012 perror(gettext("xargs: Corrupt input file")); 1013 exit(1); 1014 /* NOTREACHED */ 1015 } 1016 1017 1018 static void 1019 ungetwchr(wchar_t wch) 1020 { 1021 char *buffer; 1022 int bytes; 1023 1024 if ((buffer = malloc(MB_LEN_MAX)) == NULL) { 1025 perror(gettext("xargs: Memory allocation failure")); 1026 exit(1); 1027 } 1028 bytes = wctomb(buffer, wch); 1029 queue(buffer, bytes, HEAD); 1030 } 1031 1032 1033 static int 1034 lcall(char *sub, char **subargs) 1035 { 1036 int retcode, retry = 0; 1037 pid_t iwait, child; 1038 1039 for (; ; ) { 1040 switch (child = fork()) { 1041 default: 1042 while ((iwait = wait(&retcode)) != child && 1043 iwait != (pid_t)-1) 1044 ; 1045 if (iwait == (pid_t)-1) { 1046 perror(gettext("xargs: Wait failure")); 1047 exit(122); 1048 /* NOTREACHED */ 1049 } 1050 if (WIFSIGNALED(retcode)) { 1051 ermsg(gettext("Child killed with signal %d\n"), 1052 WTERMSIG(retcode)); 1053 exit(125); 1054 /* NOTREACHED */ 1055 } 1056 if ((WEXITSTATUS(retcode) & 0377) == 0377) { 1057 ermsg(gettext("Command could not continue " 1058 "processing data\n")); 1059 exit(124); 1060 /* NOTREACHED */ 1061 } 1062 return (WEXITSTATUS(retcode)); 1063 case 0: 1064 (void) execvp(sub, subargs); 1065 perror(gettext("xargs: Could not exec command")); 1066 if (errno == EACCES) 1067 exit(126); 1068 exit(127); 1069 /* NOTREACHED */ 1070 case -1: 1071 if (errno != EAGAIN && retry++ < FORK_RETRY) { 1072 perror(gettext("xargs: Could not fork child")); 1073 exit(123); 1074 } 1075 (void) sleep(1); 1076 } 1077 } 1078 } 1079 1080 1081 /* 1082 * If `s2' is a substring of `s1' return the offset of the first 1083 * occurrence of `s2' in `s1', else return -1. 1084 */ 1085 static int 1086 xindex(char *as1, char *as2) 1087 { 1088 char *s1, *s2, c; 1089 int offset; 1090 1091 s1 = as1; 1092 s2 = as2; 1093 c = *s2; 1094 1095 while (*s1) { 1096 if (*s1++ == c) { 1097 offset = s1 - as1 - 1; 1098 s2++; 1099 while ((c = *s2++) == *s1++ && c) 1100 ; 1101 if (c == 0) 1102 return (offset); 1103 s1 = offset + as1 + 1; 1104 s2 = as2; 1105 c = *s2; 1106 } 1107 } 1108 return (-1); 1109 } 1110 1111 1112 static void 1113 usage() 1114 { 1115 ermsg(gettext(USAGEMSG)); 1116 OK = FALSE; 1117 } 1118 1119 1120 1121 /* 1122 * parseargs(): modify the args 1123 * since the -e, -i and -l flags all take optional subarguments, 1124 * and getopts(3C) is clueless about this nonsense, we change the 1125 * our local argument count and strings to separate this out, 1126 * and make it easier to handle via getopts(3c). 1127 * 1128 * -e -> "-e "" 1129 * -e3 -> "-e "3" 1130 * -Estr -> "-E "str" 1131 * -i -> "-i "{}" 1132 * -irep -> "-i "rep" 1133 * -l -> "-i "1" 1134 * -l10 -> "-i "10" 1135 * 1136 * since the -e, -i and -l flags all take optional subarguments, 1137 */ 1138 static void 1139 parseargs(int ac, char **av) 1140 { 1141 int i; /* current argument */ 1142 int cflag; /* 0 = not processing cmd arg */ 1143 1144 if ((mav = malloc((ac * 2 + 1) * sizeof (char *))) == NULL) { 1145 perror(gettext("xargs: Memory allocation failure")); 1146 exit(1); 1147 } 1148 1149 /* for each argument, see if we need to change things: */ 1150 for (i = mac = cflag = 0; (av[i] != NULL) && i < ac; i++, mac++) { 1151 if ((mav[mac] = strdup(av[i])) == NULL) { 1152 perror(gettext("xargs: Memory allocation failure")); 1153 exit(1); 1154 } 1155 1156 /* -- has been found or argument list is fully processes */ 1157 if (cflag) 1158 continue; 1159 1160 /* 1161 * if we're doing special processing, and we've got a flag 1162 */ 1163 else if ((av[i][0] == '-') && (av[i][1] != NULL)) { 1164 char *def; 1165 1166 switch (av[i][1]) { 1167 case 'e': 1168 def = ""; /* -e with no arg turns off eof */ 1169 goto process_special; 1170 case 'i': 1171 def = INSPAT_STR; 1172 goto process_special; 1173 case 'l': 1174 def = "1"; 1175 process_special: 1176 /* 1177 * if there's no sub-option, we *must* add 1178 * a default one. this is because xargs must 1179 * be able to distinguish between a valid 1180 * suboption, and a command name. 1181 */ 1182 if (av[i][2] == NULL) { 1183 mav[++mac] = strdup(def); 1184 } else { 1185 /* clear out our version: */ 1186 mav[mac][2] = NULL; 1187 mav[++mac] = strdup(&av[i][2]); 1188 } 1189 if (mav[mac] == NULL) { 1190 perror(gettext("xargs: Memory" 1191 " allocation failure")); 1192 exit(1); 1193 } 1194 break; 1195 1196 /* flags with required subarguments: */ 1197 1198 /* 1199 * there are two separate cases here. either the 1200 * flag can have the normal XCU4 handling 1201 * (of the form: -X subargument); or it can have 1202 * the old solaris 2.[0-4] handling (of the 1203 * form: -Xsubargument). in order to maintain 1204 * backwards compatibility, we must support the 1205 * latter case. we handle the latter possibility 1206 * first so both the old solaris way of handling 1207 * and the new XCU4 way of handling things are allowed. 1208 */ 1209 case 'n': /* FALLTHROUGH */ 1210 case 's': /* FALLTHROUGH */ 1211 case 'E': /* FALLTHROUGH */ 1212 case 'I': /* FALLTHROUGH */ 1213 case 'L': 1214 /* 1215 * if the second character isn't null, then 1216 * the user has specified the old syntax. 1217 * we move the subargument into our 1218 * mod'd argument list. 1219 */ 1220 if (av[i][2] != NULL) { 1221 /* first clean things up: */ 1222 mav[mac][2] = NULL; 1223 1224 /* now add the separation: */ 1225 ++mac; /* inc to next mod'd arg */ 1226 if ((mav[mac] = strdup(&av[i][2])) == 1227 NULL) { 1228 perror(gettext("xargs: Memory" 1229 " allocation failure")); 1230 exit(1); 1231 } 1232 break; 1233 } 1234 i++; 1235 mac++; 1236 1237 if (av[i] == NULL) { 1238 mav[mac] = NULL; 1239 return; 1240 } 1241 if ((mav[mac] = strdup(av[i])) == NULL) { 1242 perror(gettext("xargs: Memory" 1243 " allocation failure")); 1244 exit(1); 1245 } 1246 break; 1247 1248 /* flags */ 1249 case 'p' : 1250 case 't' : 1251 case 'x' : 1252 break; 1253 1254 case '-' : 1255 default: 1256 /* 1257 * here we've hit the cmd argument. so 1258 * we'll stop special processing, as the 1259 * cmd may have a "-i" etc., argument, 1260 * and we don't want to add a "" to it. 1261 */ 1262 cflag = 1; 1263 break; 1264 } 1265 } else if (i > 0) { /* if we're not the 1st arg */ 1266 /* 1267 * if it's not a flag, then it *must* be the cmd. 1268 * set cflag, so we don't mishandle the -[eil] flags. 1269 */ 1270 cflag = 1; 1271 } 1272 } 1273 1274 mav[mac] = NULL; 1275 } 1276 1277 1278 /* 1279 * saveinput(): pick up any pending input, so it can be processed later. 1280 * 1281 * description: 1282 * the purpose of this routine is to allow us to handle the user 1283 * typing in a 'y' or 'n', when there's existing characters already 1284 * in stdin. this happens when one gives the "-n" option along with 1285 * "-p". the problem occurs when the user first types in more arguments 1286 * than specified by the -n number. echoargs() wants to read stdin 1287 * in order to get the user's response, but if there's already stuff 1288 * there, echoargs() won't read the proper character. 1289 * 1290 * the solution provided by this routine is to pick up all characters 1291 * (if any), and store them for later processing. 1292 */ 1293 1294 void 1295 saveinput() 1296 { 1297 char *buffer; /* ptr to the floating data buffer */ 1298 struct strpeek speek; /* to see what's on the queue */ 1299 struct strpeek *ps; 1300 1301 /* if we're not in -p mode, skip */ 1302 if (PROMPT == -1) { 1303 return; 1304 } 1305 1306 1307 /* now see if there's any activity pending: */ 1308 ps = &speek; 1309 ps->ctlbuf.maxlen = 0; 1310 ps->ctlbuf.len = 0; 1311 ps->ctlbuf.buf = NULL; 1312 ps->flags = 0; 1313 ps->databuf.maxlen = MAX_INPUT; 1314 ps->databuf.len = 0; 1315 if ((buffer = malloc((size_t)MAX_INPUT)) == NULL) { 1316 perror(gettext("xargs: Memory allocation failure")); 1317 exit(1); 1318 } 1319 ps->databuf.buf = (char *)buffer; 1320 1321 if (ioctl(PROMPT, I_PEEK, ps) == -1) { 1322 perror(gettext("xargs: I_PEEK failure")); 1323 exit(1); 1324 } 1325 1326 if (ps->databuf.len > 0) { 1327 int len; 1328 1329 if ((len = read(PROMPT, buffer, ps->databuf.len)) == -1) { 1330 perror(gettext("xargs: read failure")); 1331 exit(1); 1332 } 1333 queue(buffer, len, TAIL); 1334 } 1335 } 1336