xref: /illumos-gate/usr/src/cmd/xargs/xargs.c (revision 826ac02a)
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
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 *
508 addarg(char *arg)
509 {
510 	linesize += (strlen(arg) + 1);
511 	return (arg);
512 }
513 
514 
515 static void
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 *
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
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
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 *
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
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
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
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
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
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