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