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