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