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