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