xref: /netbsd-src/bin/sh/expand.c (revision 7330f729ccf0bd976a06f95fad452fe774fc7fd1)
1 /*	$NetBSD: expand.c,v 1.136 2019/10/14 13:34:14 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Kenneth Almquist.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)expand.c	8.5 (Berkeley) 5/15/95";
39 #else
40 __RCSID("$NetBSD: expand.c,v 1.136 2019/10/14 13:34:14 christos Exp $");
41 #endif
42 #endif /* not lint */
43 
44 #include <sys/types.h>
45 #include <sys/time.h>
46 #include <sys/stat.h>
47 #include <errno.h>
48 #include <dirent.h>
49 #include <unistd.h>
50 #include <pwd.h>
51 #include <limits.h>
52 #include <stdlib.h>
53 #include <stdio.h>
54 #include <wctype.h>
55 #include <wchar.h>
56 
57 /*
58  * Routines to expand arguments to commands.  We have to deal with
59  * backquotes, shell variables, and file metacharacters.
60  */
61 
62 #include "shell.h"
63 #include "main.h"
64 #include "nodes.h"
65 #include "eval.h"
66 #include "expand.h"
67 #include "syntax.h"
68 #include "arithmetic.h"
69 #include "parser.h"
70 #include "jobs.h"
71 #include "options.h"
72 #include "builtins.h"
73 #include "var.h"
74 #include "input.h"
75 #include "output.h"
76 #include "memalloc.h"
77 #include "error.h"
78 #include "mystring.h"
79 #include "show.h"
80 
81 /*
82  * Structure specifying which parts of the string should be searched
83  * for IFS characters.
84  */
85 
86 struct ifsregion {
87 	struct ifsregion *next;	/* next region in list */
88 	int begoff;		/* offset of start of region */
89 	int endoff;		/* offset of end of region */
90 	int inquotes;		/* search for nul bytes only */
91 };
92 
93 
94 char *expdest;			/* output of current string */
95 struct nodelist *argbackq;	/* list of back quote expressions */
96 struct ifsregion ifsfirst;	/* first struct in list of ifs regions */
97 struct ifsregion *ifslastp;	/* last struct in list */
98 struct arglist exparg;		/* holds expanded arg list */
99 
100 static int empty_dollar_at;	/* have expanded "$@" to nothing */
101 
102 STATIC const char *argstr(const char *, int);
103 STATIC const char *exptilde(const char *, int);
104 STATIC void expbackq(union node *, int, int);
105 STATIC const char *expari(const char *);
106 STATIC int subevalvar(const char *, const char *, int, int, int);
107 STATIC int subevalvar_trim(const char *, int, int, int, int, int);
108 STATIC const char *evalvar(const char *, int);
109 STATIC int varisset(const char *, int);
110 STATIC void varvalue(const char *, int, int, int);
111 STATIC void recordregion(int, int, int);
112 STATIC void removerecordregions(int);
113 STATIC void ifsbreakup(char *, struct arglist *);
114 STATIC void ifsfree(void);
115 STATIC void expandmeta(struct strlist *, int);
116 STATIC void expmeta(char *, char *);
117 STATIC void addfname(char *);
118 STATIC struct strlist *expsort(struct strlist *);
119 STATIC struct strlist *msort(struct strlist *, int);
120 STATIC int patmatch(const char *, const char *, int);
121 STATIC char *cvtnum(int, char *);
122 static int collate_range_cmp(wchar_t, wchar_t);
123 STATIC void add_args(struct strlist *);
124 STATIC void rmescapes_nl(char *);
125 
126 #ifdef	DEBUG
127 #define	NULLTERM_4_TRACE(p)	STACKSTRNUL(p)
128 #else
129 #define	NULLTERM_4_TRACE(p)	do { /* nothing */ } while (/*CONSTCOND*/0)
130 #endif
131 
132 #define	IS_BORING(_ch)						\
133 	((_ch) == CTLQUOTEMARK || (_ch) == CTLQUOTEEND || (_ch) == CTLNONL)
134 #define	SKIP_BORING(p)						\
135 	do {							\
136 		char _ch;					\
137 								\
138 		while ((_ch = *(p)), IS_BORING(_ch))		\
139 			(p)++;					\
140 	} while (0)
141 
142 /*
143  * Expand shell variables and backquotes inside a here document.
144  */
145 
146 void
147 expandhere(union node *arg, int fd)
148 {
149 
150 	herefd = fd;
151 	expandarg(arg, NULL, 0);
152 	xwrite(fd, stackblock(), expdest - stackblock());
153 }
154 
155 
156 static int
157 collate_range_cmp(wchar_t c1, wchar_t c2)
158 {
159 	wchar_t s1[2], s2[2];
160 
161 	s1[0] = c1;
162 	s1[1] = L'\0';
163 	s2[0] = c2;
164 	s2[1] = L'\0';
165 	return (wcscoll(s1, s2));
166 }
167 
168 /*
169  * Perform variable substitution and command substitution on an argument,
170  * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
171  * perform splitting and file name expansion.  When arglist is NULL, perform
172  * here document expansion.
173  */
174 
175 void
176 expandarg(union node *arg, struct arglist *arglist, int flag)
177 {
178 	struct strlist *sp;
179 	char *p;
180 
181 	CTRACE(DBG_EXPAND, ("expandarg(fl=%#x)\n", flag));
182 	if (fflag)		/* no filename expandsion */
183 		flag &= ~EXP_GLOB;
184 
185 	empty_dollar_at = 0;
186 	argbackq = arg->narg.backquote;
187 	STARTSTACKSTR(expdest);
188 	ifsfirst.next = NULL;
189 	ifslastp = NULL;
190 	line_number = arg->narg.lineno;
191 	argstr(arg->narg.text, flag);
192 	if (arglist == NULL) {
193 		STACKSTRNUL(expdest);
194 		CTRACE(DBG_EXPAND, ("expandarg: no arglist, done (%d) \"%s\"\n",
195 		    expdest - stackblock(), stackblock()));
196 		return;			/* here document expanded */
197 	}
198 	STPUTC('\0', expdest);
199 	CTRACE(DBG_EXPAND, ("expandarg: arglist got (%d) \"%s\"\n",
200 		    expdest - stackblock() - 1, stackblock()));
201 	p = grabstackstr(expdest);
202 	exparg.lastp = &exparg.list;
203 	/*
204 	 * TODO - EXP_REDIR
205 	 */
206 	if (flag & EXP_SPLIT) {
207 		ifsbreakup(p, &exparg);
208 		*exparg.lastp = NULL;
209 		exparg.lastp = &exparg.list;
210 		if (flag & EXP_GLOB)
211 			expandmeta(exparg.list, flag);
212 		else
213 			add_args(exparg.list);
214 #if 0
215 	} else if (flag & EXP_REDIR) {
216 		/* if EXP_REDIR ever happens, it happens here */
217 		/* for now just (below) remove escapes, and leave it alone */
218 #endif
219 	} else {
220 		rmescapes(p);	/* we might have escaped CTL bytes to remove */
221 		sp = stalloc(sizeof(*sp));
222 		sp->text = p;
223 		*exparg.lastp = sp;
224 		exparg.lastp = &sp->next;
225 	}
226 	ifsfree();
227 	*exparg.lastp = NULL;
228 	if (exparg.list) {
229 		*arglist->lastp = exparg.list;
230 		arglist->lastp = exparg.lastp;
231 	}
232 }
233 
234 
235 
236 /*
237  * Perform variable and command substitution.
238  * If EXP_GLOB is set, output CTLESC characters to allow for further processing.
239  * If EXP_SPLIT is set, remember location of result for later,
240  * Otherwise treat $@ like $* since no splitting will be performed.
241  */
242 
243 STATIC const char *
244 argstr(const char *p, int flag)
245 {
246 	char c;
247 	const int quotes = flag & EXP_QNEEDED;		/* do CTLESC */
248 	int firsteq = 1;
249 	int had_dol_at = 0;
250 	int startoff;
251 	const char *ifs = NULL;
252 	int ifs_split = EXP_IFS_SPLIT;
253 
254 	if (flag & EXP_IFS_SPLIT)
255 		ifs = ifsval();
256 
257 	CTRACE(DBG_EXPAND, ("argstr(\"%s\", %#x) quotes=%#x\n", p,flag,quotes));
258 
259 	startoff = expdest - stackblock();
260 	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
261 		p = exptilde(p, flag);
262 	for (;;) {
263 		switch (c = *p++) {
264 		case '\0':
265 			NULLTERM_4_TRACE(expdest);
266 			VTRACE(DBG_EXPAND, ("argstr returning at \"\" "
267 			   "added \"%s\" to expdest\n", stackblock()));
268 			return p - 1;
269 		case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */
270 		case CTLENDARI: /* end of a $(( )) string */
271 			if (had_dol_at && *p == CTLQUOTEEND)
272 				p++;
273 			NULLTERM_4_TRACE(expdest);
274 			VTRACE(DBG_EXPAND, ("argstr returning at \"%.6s\"..."
275 			    " after %2.2X; added \"%s\" to expdest\n",
276 			    p, (c & 0xff), stackblock()));
277 			return p;
278 		case CTLQUOTEMARK:
279 			/* "$@" syntax adherence hack */
280 			if (p[0] == CTLVAR && p[1] & VSQUOTE &&
281 			    p[2] == '@' && p[3] == '=') {
282 				had_dol_at = 1;
283 				break;
284 			}
285 			had_dol_at = 0;
286 			empty_dollar_at = 0;
287 			if ((flag & EXP_SPLIT) != 0)
288 				STPUTC(c, expdest);
289 			ifs_split = 0;
290 			break;
291 		case CTLNONL:
292 			if (flag & EXP_NL)
293 				STPUTC(c, expdest);
294 			line_number++;
295 			break;
296 		case CTLCNL:
297 			STPUTC('\n', expdest);	/* no line_number++ */
298 			break;
299 		case CTLQUOTEEND:
300 			if (empty_dollar_at &&
301 			    expdest - stackblock() > startoff &&
302 			    expdest[-1] == CTLQUOTEMARK)
303 				expdest--;
304 			else if (!had_dol_at && (flag & EXP_SPLIT) != 0)
305 				STPUTC(c, expdest);
306 			ifs_split = EXP_IFS_SPLIT;
307 			had_dol_at = 0;
308 			break;
309 		case CTLESC:
310 			if (quotes || ISCTL(*p))
311 				STPUTC(c, expdest);
312 			c = *p++;
313 			STPUTC(c, expdest);
314 			if (c == '\n')		/* should not happen, but ... */
315 				line_number++;
316 			break;
317 		case CTLVAR: {
318 #ifdef DEBUG
319 			unsigned int pos = expdest - stackblock();
320 			NULLTERM_4_TRACE(expdest);
321 #endif
322 			p = evalvar(p, (flag & ~EXP_IFS_SPLIT) | (flag & ifs_split));
323 			NULLTERM_4_TRACE(expdest);
324 			VTRACE(DBG_EXPAND, ("argstr evalvar "
325 			   "added %zd \"%s\" to expdest\n",
326 			   (size_t)(expdest - (stackblock() + pos)),
327 			   stackblock() + pos));
328 			break;
329 		}
330 		case CTLBACKQ:
331 		case CTLBACKQ|CTLQUOTE: {
332 #ifdef DEBUG
333 			unsigned int pos = expdest - stackblock();
334 #endif
335 			expbackq(argbackq->n, c & CTLQUOTE, flag);
336 			argbackq = argbackq->next;
337 			NULLTERM_4_TRACE(expdest);
338 			VTRACE(DBG_EXPAND, ("argstr expbackq added \"%s\" "
339 			   "to expdest\n", stackblock() + pos));
340 			break;
341 		}
342 		case CTLARI: {
343 #ifdef DEBUG
344 			unsigned int pos = expdest - stackblock();
345 #endif
346 			p = expari(p);
347 			NULLTERM_4_TRACE(expdest);
348 			VTRACE(DBG_EXPAND, ("argstr expari "
349 			   "+ \"%s\" to expdest p=\"%.5s...\"\n",
350 			   stackblock() + pos, p));
351 			break;
352 		}
353 		case ':':
354 		case '=':
355 			/*
356 			 * sort of a hack - expand tildes in variable
357 			 * assignments (after the first '=' and after ':'s).
358 			 */
359 			STPUTC(c, expdest);
360 			if (flag & EXP_VARTILDE && *p == '~') {
361 				if (c == '=') {
362 					if (firsteq)
363 						firsteq = 0;
364 					else
365 						break;
366 				}
367 				p = exptilde(p, flag);
368 			}
369 			break;
370 		default:
371 			if (c == '\n')
372 				line_number++;
373 			STPUTC(c, expdest);
374 			if (flag & ifs_split && strchr(ifs, c) != NULL) {
375 				/* We need to get the output split here... */
376 				recordregion(expdest - stackblock() - 1,
377 						expdest - stackblock(), 0);
378 			}
379 			break;
380 		}
381 	}
382 }
383 
384 STATIC const char *
385 exptilde(const char *p, int flag)
386 {
387 	char c;
388 	const char *startp = p;
389 	struct passwd *pw;
390 	const char *home;
391 	const int quotes = flag & EXP_QNEEDED;
392 	char *user;
393 	struct stackmark smark;
394 #ifdef DEBUG
395 	unsigned int offs = expdest - stackblock();
396 #endif
397 
398 	setstackmark(&smark);
399 	(void) grabstackstr(expdest);
400 	user = stackblock();		/* we will just borrow top of stack */
401 
402 	while ((c = *++p) != '\0') {
403 		switch(c) {
404 		case CTLESC:		/* any of these occurring */
405 		case CTLVAR:		/* means ~ expansion */
406 		case CTLBACKQ:		/* does not happen at all */
407 		case CTLBACKQ | CTLQUOTE:
408 		case CTLARI:		/* just leave original unchanged */
409 		case CTLENDARI:
410 		case CTLQUOTEMARK:
411 		case '\n':
412 			popstackmark(&smark);
413 			return (startp);
414 		case CTLNONL:
415 			continue;
416 		case ':':
417 			if (!posix || flag & EXP_VARTILDE)
418 				goto done;
419 			break;
420 		case CTLENDVAR:
421 		case '/':
422 			goto done;
423 		}
424 		STPUTC(c, user);
425 	}
426  done:
427 	STACKSTRNUL(user);
428 	user = stackblock();		/* to start of collected username */
429 
430 	CTRACE(DBG_EXPAND, ("exptilde, found \"~%s\"", user));
431 	if (*user == '\0') {
432 		home = lookupvar("HOME");
433 		/*
434 		 * if HOME is unset, results are unspecified...
435 		 * we used to just leave the ~ unchanged, but
436 		 * (some) other shells do ... and this seems more useful.
437 		 */
438 		if (home == NULL && (pw = getpwuid(getuid())) != NULL)
439 			home = pw->pw_dir;
440 	} else if ((pw = getpwnam(user)) == NULL) {
441 		/*
442 		 * If user does not exist, results are undefined.
443 		 * so we can abort() here if we want, but let's not!
444 		 */
445 		home = NULL;
446 	} else
447 		home = pw->pw_dir;
448 
449 	VTRACE(DBG_EXPAND, (" ->\"%s\"", home ? home : "<<NULL>>"));
450 	popstackmark(&smark);	/* now expdest is valid again */
451 
452 	/*
453 	 * Posix XCU 2.6.1: The value of $HOME (for ~) or the initial
454 	 *		working directory from getpwnam() for ~user
455 	 * Nothing there about "except if a null string".  So do what it wants.
456 	 */
457 	if (home == NULL /* || *home == '\0' */) {
458 		CTRACE(DBG_EXPAND, (": returning unused \"%s\"\n", startp));
459 		return startp;
460 	} while ((c = *home++) != '\0') {
461 		if ((quotes && NEEDESC(c)) || ISCTL(c))
462 			STPUTC(CTLESC, expdest);
463 		STPUTC(c, expdest);
464 	}
465 	CTRACE(DBG_EXPAND, (": added %d \"%.*s\" returning \"%s\"\n",
466 	      expdest - stackblock() - offs, expdest - stackblock() - offs,
467 	      stackblock() + offs, p));
468 
469 	return (p);
470 }
471 
472 
473 STATIC void
474 removerecordregions(int endoff)
475 {
476 
477 	VTRACE(DBG_EXPAND, ("removerecordregions(%d):", endoff));
478 	if (ifslastp == NULL) {
479 		VTRACE(DBG_EXPAND, (" none\n", endoff));
480 		return;
481 	}
482 
483 	if (ifsfirst.endoff > endoff) {
484 		VTRACE(DBG_EXPAND, (" first(%d)", ifsfirst.endoff));
485 		while (ifsfirst.next != NULL) {
486 			struct ifsregion *ifsp;
487 			INTOFF;
488 			ifsp = ifsfirst.next->next;
489 			ckfree(ifsfirst.next);
490 			ifsfirst.next = ifsp;
491 			INTON;
492 		}
493 		if (ifsfirst.begoff > endoff)
494 			ifslastp = NULL;
495 		else {
496 			VTRACE(DBG_EXPAND,("->(%d,%d)",ifsfirst.begoff,endoff));
497 			ifslastp = &ifsfirst;
498 			ifsfirst.endoff = endoff;
499 		}
500 		VTRACE(DBG_EXPAND, ("\n"));
501 		return;
502 	}
503 
504 	ifslastp = &ifsfirst;
505 	while (ifslastp->next && ifslastp->next->begoff < endoff)
506 		ifslastp=ifslastp->next;
507 	VTRACE(DBG_EXPAND, (" found(%d,%d)", ifslastp->begoff,ifslastp->endoff));
508 	while (ifslastp->next != NULL) {
509 		struct ifsregion *ifsp;
510 		INTOFF;
511 		ifsp = ifslastp->next->next;
512 		ckfree(ifslastp->next);
513 		ifslastp->next = ifsp;
514 		INTON;
515 	}
516 	if (ifslastp->endoff > endoff)
517 		ifslastp->endoff = endoff;
518 	VTRACE(DBG_EXPAND, ("->(%d,%d)", ifslastp->begoff,ifslastp->endoff));
519 }
520 
521 
522 /*
523  * Expand arithmetic expression.
524  *
525  * In this incarnation, we start at the beginning (yes, "Let's start at the
526  * very beginning.  A very good place to start.") and collect the expression
527  * until the end - which means expanding anything contained within.
528  *
529  * Fortunately, argstr() just happens to do that for us...
530  */
531 STATIC const char *
532 expari(const char *p)
533 {
534 	char *q, *start;
535 	intmax_t result;
536 	int adjustment;
537 	int begoff;
538 	int quoted;
539 	struct stackmark smark;
540 
541 	/*	ifsfree(); */
542 
543 	/*
544 	 * SPACE_NEEDED is enough for all possible digits (rounded up)
545 	 * plus possible "-", and the terminating '\0', hence, plus 2
546 	 *
547 	 * The calculation produces the number of bytes needed to
548 	 * represent the biggest possible value, in octal.  We only
549 	 * generate decimal, which takes (often) less digits (never more)
550 	 * so this is safe, if occasionally slightly wasteful.
551 	 */
552 #define SPACE_NEEDED ((int)((sizeof(intmax_t) * CHAR_BIT + 2) / 3 + 2))
553 
554 	quoted = *p++ == '"';
555 	begoff = expdest - stackblock();
556 	VTRACE(DBG_EXPAND, ("expari%s: \"%s\" begoff %d\n",
557 	    quoted ? "(quoted)" : "", p, begoff));
558 
559 	p = argstr(p, EXP_NL);			/* expand $(( )) string */
560 	STPUTC('\0', expdest);
561 	start = stackblock() + begoff;
562 
563 	removerecordregions(begoff);		/* nothing there is kept */
564 	rmescapes_nl(start);		/* convert CRTNONL back into \n's */
565 
566 	setstackmark(&smark);
567 	q = grabstackstr(expdest);	/* keep the expression while eval'ing */
568 	result = arith(start, line_number);
569 	popstackmark(&smark);		/* return the stack to before grab */
570 
571 	start = stackblock() + begoff;		/* block may have moved */
572 	adjustment = expdest - start;
573 	STADJUST(-adjustment, expdest);		/* remove the argstr() result */
574 
575 	CHECKSTRSPACE(SPACE_NEEDED, expdest);	/* nb: stack block might move */
576 	fmtstr(expdest, SPACE_NEEDED, "%"PRIdMAX, result);
577 
578 	for (q = expdest; *q++ != '\0'; )	/* find end of what we added */
579 		;
580 
581 	if (quoted == 0)			/* allow weird splitting */
582 		recordregion(begoff, begoff + q - 1 - expdest, 0);
583 	adjustment = q - expdest - 1;
584 	STADJUST(adjustment, expdest);		/* move expdest to end */
585 	VTRACE(DBG_EXPAND, ("expari: adding %d \"%s\", returning \"%.5s...\"\n",
586 	    adjustment, stackblock() + begoff, p));
587 
588 	return p;
589 }
590 
591 
592 /*
593  * Expand stuff in backwards quotes (these days, any command substitution).
594  */
595 
596 STATIC void
597 expbackq(union node *cmd, int quoted, int flag)
598 {
599 	struct backcmd in;
600 	int i;
601 	char buf[128];
602 	char *p;
603 	char *dest = expdest;	/* expdest may be reused by eval, use an alt */
604 	struct ifsregion saveifs, *savelastp;
605 	struct nodelist *saveargbackq;
606 	char lastc;
607 	int startloc = dest - stackblock();
608 	int saveherefd;
609 	const int quotes = flag & EXP_QNEEDED;
610 	int nnl;
611 	struct stackmark smark;
612 
613 	VTRACE(DBG_EXPAND, ("expbackq( ..., q=%d flag=%#x) have %d\n",
614 	    quoted, flag, startloc));
615 	INTOFF;
616 	saveifs = ifsfirst;
617 	savelastp = ifslastp;
618 	saveargbackq = argbackq;
619 	saveherefd = herefd;
620 	herefd = -1;
621 
622 	setstackmark(&smark);	/* preserve the stack */
623 	p = grabstackstr(dest);	/* save what we have there currently */
624 	evalbackcmd(cmd, &in);	/* evaluate the $( ) tree (using stack) */
625 	popstackmark(&smark);	/* and return stack to when we entered */
626 
627 	ifsfirst = saveifs;
628 	ifslastp = savelastp;
629 	argbackq = saveargbackq;
630 	herefd = saveherefd;
631 
632 	p = in.buf;		/* now extract the results */
633 	nnl = 0;		/* dropping trailing \n's */
634 	for (;;) {
635 		if (--in.nleft < 0) {
636 			if (in.fd < 0)
637 				break;
638 			INTON;
639 			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR)
640 				continue;
641 			INTOFF;
642 			VTRACE(DBG_EXPAND, ("expbackq: read returns %d\n", i));
643 			if (i <= 0)
644 				break;
645 			p = buf;
646 			in.nleft = i - 1;
647 		}
648 		lastc = *p++;
649 		if (lastc != '\0') {
650 			if (lastc == '\n')	/* don't save \n yet */
651 				nnl++;		/* it might be trailing */
652 			else {
653 				/*
654 				 * We have something other than \n
655 				 *
656 				 * Before saving it, we need to insert
657 				 * any \n's that we have just skipped.
658 				 */
659 
660 				/* XXX
661 				 * this hack is just because our
662 				 * CHECKSTRSPACE() is lazy, and only
663 				 * ever grows the stack once, even
664 				 * if that does not allocate the space
665 				 * we requested.  ie: safe for small
666 				 * requests, but not large ones.
667 				 * FIXME someday...
668 				 */
669 				if (nnl < 20) {
670 					CHECKSTRSPACE(nnl + 2, dest);
671 					while (nnl > 0) {
672 						nnl--;
673 						USTPUTC('\n', dest);
674 					}
675 				} else {
676 					/* The slower, safer, way */
677 					while (nnl > 0) {
678 						nnl--;
679 						STPUTC('\n', dest);
680 					}
681 					CHECKSTRSPACE(2, dest);
682 				}
683 				if ((quotes && quoted && NEEDESC(lastc)) ||
684 				    ISCTL(lastc))
685 					USTPUTC(CTLESC, dest);
686 				USTPUTC(lastc, dest);
687 			}
688 		}
689 	}
690 
691 	if (in.fd >= 0)
692 		close(in.fd);
693 	if (in.buf)
694 		ckfree(in.buf);
695 	if (in.jp)
696 		back_exitstatus = waitforjob(in.jp);
697 	if (quoted == 0)
698 		recordregion(startloc, dest - stackblock(), 0);
699 	CTRACE(DBG_EXPAND, ("evalbackq: size=%d: \"%.*s\"\n",
700 		(int)((dest - stackblock()) - startloc),
701 		(int)((dest - stackblock()) - startloc),
702 		stackblock() + startloc));
703 
704 	expdest = dest;		/* all done, expdest is all ours again */
705 	INTON;
706 }
707 
708 
709 STATIC int
710 subevalvar(const char *p, const char *str, int subtype, int startloc,
711     int varflags)
712 {
713 	char *startp;
714 	int saveherefd = herefd;
715 	struct nodelist *saveargbackq = argbackq;
716 	int amount;
717 
718 	herefd = -1;
719 	VTRACE(DBG_EXPAND, ("subevalvar(%d) \"%.20s\" ${%.*s} sloc=%d vf=%x\n",
720 	    subtype, p, p-str, str, startloc, varflags));
721 	argstr(p, subtype == VSASSIGN ? EXP_VARTILDE : EXP_TILDE);
722 	STACKSTRNUL(expdest);
723 	herefd = saveherefd;
724 	argbackq = saveargbackq;
725 	startp = stackblock() + startloc;
726 
727 	switch (subtype) {
728 	case VSASSIGN:
729 		setvar(str, startp, 0);
730 		amount = startp - expdest;	/* remove what argstr added */
731 		STADJUST(amount, expdest);
732 		varflags &= ~VSNUL;	/*XXX Huh?   What's that achieve? */
733 		return 1;			/* go back and eval var again */
734 
735 	case VSQUESTION:
736 		if (*p != CTLENDVAR) {
737 			outfmt(&errout, "%s\n", startp);
738 			error(NULL);
739 		}
740 		error("%.*s: parameter %snot set",
741 		      (int)(p - str - 1),
742 		      str, (varflags & VSNUL) ? "null or "
743 					      : nullstr);
744 		/* NOTREACHED */
745 
746 	default:
747 		abort();
748 	}
749 }
750 
751 STATIC int
752 subevalvar_trim(const char *p, int strloc, int subtype, int startloc,
753     int varflags, int quotes)
754 {
755 	char *startp;
756 	char *str;
757 	char *loc = NULL;
758 	char *q;
759 	int c = 0;
760 	int saveherefd = herefd;
761 	struct nodelist *saveargbackq = argbackq;
762 	int amount;
763 
764 	herefd = -1;
765 	switch (subtype) {
766 	case VSTRIMLEFT:
767 	case VSTRIMLEFTMAX:
768 	case VSTRIMRIGHT:
769 	case VSTRIMRIGHTMAX:
770 		break;
771 	default:
772 		abort();
773 		break;
774 	}
775 
776 	VTRACE(DBG_EXPAND,
777 	("subevalvar_trim(\"%.9s\", STR@%d, SUBT=%d, start@%d, vf=%x, q=%x)\n",
778 		p, strloc, subtype, startloc, varflags, quotes));
779 
780 	argstr(p, (varflags & (VSQUOTE|VSPATQ)) == VSQUOTE ? 0 : EXP_CASE);
781 	STACKSTRNUL(expdest);
782 	herefd = saveherefd;
783 	argbackq = saveargbackq;
784 	startp = stackblock() + startloc;
785 	str = stackblock() + strloc;
786 
787 	switch (subtype) {
788 
789 	case VSTRIMLEFT:
790 		for (loc = startp; loc < str; loc++) {
791 			c = *loc;
792 			*loc = '\0';
793 			if (patmatch(str, startp, quotes))
794 				goto recordleft;
795 			*loc = c;
796 			if (quotes && *loc == CTLESC)
797 				loc++;
798 		}
799 		return 0;
800 
801 	case VSTRIMLEFTMAX:
802 		for (loc = str - 1; loc >= startp;) {
803 			c = *loc;
804 			*loc = '\0';
805 			if (patmatch(str, startp, quotes))
806 				goto recordleft;
807 			*loc = c;
808 			loc--;
809 			if (quotes && loc > startp &&
810 			    *(loc - 1) == CTLESC) {
811 				for (q = startp; q < loc; q++)
812 					if (*q == CTLESC)
813 						q++;
814 				if (q > loc)
815 					loc--;
816 			}
817 		}
818 		return 0;
819 
820 	case VSTRIMRIGHT:
821 		for (loc = str - 1; loc >= startp;) {
822 			if (patmatch(str, loc, quotes))
823 				goto recordright;
824 			loc--;
825 			if (quotes && loc > startp &&
826 			    *(loc - 1) == CTLESC) {
827 				for (q = startp; q < loc; q++)
828 					if (*q == CTLESC)
829 						q++;
830 				if (q > loc)
831 					loc--;
832 			}
833 		}
834 		return 0;
835 
836 	case VSTRIMRIGHTMAX:
837 		for (loc = startp; loc < str - 1; loc++) {
838 			if (patmatch(str, loc, quotes))
839 				goto recordright;
840 			if (quotes && *loc == CTLESC)
841 				loc++;
842 		}
843 		return 0;
844 
845 	default:
846 		abort();
847 	}
848 
849  recordleft:
850 	*loc = c;
851 	amount = ((str - 1) - (loc - startp)) - expdest;
852 	STADJUST(amount, expdest);
853 	while (loc != str - 1)
854 		*startp++ = *loc++;
855 	return 1;
856 
857  recordright:
858 	amount = loc - expdest;
859 	STADJUST(amount, expdest);
860 	STPUTC('\0', expdest);
861 	STADJUST(-1, expdest);
862 	return 1;
863 }
864 
865 
866 /*
867  * Expand a variable, and return a pointer to the next character in the
868  * input string.
869  */
870 
871 STATIC const char *
872 evalvar(const char *p, int flag)
873 {
874 	int subtype;
875 	int varflags;
876 	const char *var;
877 	char *val;
878 	int patloc;
879 	int c;
880 	int set;
881 	int special;
882 	int startloc;
883 	int varlen;
884 	int apply_ifs;
885 	const int quotes = flag & EXP_QNEEDED;
886 
887 	varflags = (unsigned char)*p++;
888 	subtype = varflags & VSTYPE;
889 	var = p;
890 	special = !is_name(*p);
891 	p = strchr(p, '=') + 1;
892 
893 	CTRACE(DBG_EXPAND,
894 	    ("evalvar \"%.*s\", flag=%#X quotes=%#X vf=%#X subtype=%X\n",
895 	    p - var - 1, var, flag, quotes, varflags, subtype));
896 
897  again: /* jump here after setting a variable with ${var=text} */
898 	if (varflags & VSLINENO) {
899 		if (line_num.flags & VUNSET) {
900 			set = 0;
901 			val = NULL;
902 		} else {
903 			set = 1;
904 			special = p - var;
905 			val = NULL;
906 		}
907 	} else if (special) {
908 		set = varisset(var, varflags & VSNUL);
909 		val = NULL;
910 		if (!set && *var == '@')
911 			empty_dollar_at = 1;
912 	} else {
913 		val = lookupvar(var);
914 		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
915 			val = NULL;
916 			set = 0;
917 		} else
918 			set = 1;
919 	}
920 
921 	varlen = 0;
922 	startloc = expdest - stackblock();
923 
924 	if (!set && uflag && *var != '@' && *var != '*') {
925 		switch (subtype) {
926 		case VSNORMAL:
927 		case VSTRIMLEFT:
928 		case VSTRIMLEFTMAX:
929 		case VSTRIMRIGHT:
930 		case VSTRIMRIGHTMAX:
931 		case VSLENGTH:
932 			error("%.*s: parameter not set",
933 			    (int)(p - var - 1), var);
934 			/* NOTREACHED */
935 		}
936 	}
937 
938 #if 0		/* no longer need this $@ evil ... */
939 	if (!set && subtype != VSPLUS && special && *var == '@')
940 		if (startloc > 0 && expdest[-1] == CTLQUOTEMARK)
941 			expdest--, startloc--;
942 #endif
943 
944 	if (set && subtype != VSPLUS) {
945 		/* insert the value of the variable */
946 		if (special) {
947 			if (varflags & VSLINENO) {
948 				/*
949 				 * The LINENO hack (expansion part)
950 				 */
951 				while (--special > 0) {
952 /*						not needed, it is a number...
953 					if (quotes && NEEDESC(*var))
954 						STPUTC(CTLESC, expdest);
955 */
956 					STPUTC(*var++, expdest);
957 				}
958 			} else
959 				varvalue(var, varflags&VSQUOTE, subtype, flag);
960 			if (subtype == VSLENGTH) {
961 				varlen = expdest - stackblock() - startloc;
962 				STADJUST(-varlen, expdest);
963 			}
964 		} else {
965 
966 			if (subtype == VSLENGTH) {
967 				for (; *val; val++)
968 					varlen++;
969 			} else if (quotes && varflags & VSQUOTE) {
970 				/*
971 				 * If we are going to look for magic in the
972 				 * value (quotes is set) and the expansion
973 				 * occurs inside "" (VSQUOTE) then any char
974 				 * that has any potential special meaning
975 				 * needs to have that meaning suppressed,
976 				 * so supply a CTLESC prefix for it.
977 				 */
978 				for (; (c = *val) != '\0'; val++) {
979 					if (NEEDESC(c))
980 						STPUTC(CTLESC, expdest);
981 					STPUTC(c, expdest);
982 				}
983 			} else {
984 				/*
985 				 * We are going to rmescapes() later,
986 				 * so make sure that any data char that
987 				 * might be mistaken for one of our CTLxxx
988 				 * magic chars is protected ... always.
989 				 */
990 				for (; (c = *val) != '\0'; val++) {
991 					if (ISCTL(c))
992 						STPUTC(CTLESC, expdest);
993 					STPUTC(c, expdest);
994 				}
995 			}
996 		}
997 	}
998 
999 
1000 	if (varflags & VSQUOTE) {
1001 		if  (*var == '@' && shellparam.nparam != 1)
1002 		    apply_ifs = 1;
1003 		else {
1004 		    /*
1005 		     * Mark so that we don't apply IFS if we recurse through
1006 		     * here expanding $bar from "${foo-$bar}".
1007 		     */
1008 		    flag |= EXP_IN_QUOTES;
1009 		    apply_ifs = 0;
1010 		}
1011 	} else if (flag & EXP_IN_QUOTES) {
1012 		apply_ifs = 0;
1013 	} else
1014 		apply_ifs = 1;
1015 
1016 	switch (subtype) {
1017 	case VSLENGTH:
1018 		expdest = cvtnum(varlen, expdest);
1019 		break;
1020 
1021 	case VSNORMAL:
1022 		break;
1023 
1024 	case VSPLUS:
1025 		set = !set;
1026 		/* FALLTHROUGH */
1027 	case VSMINUS:
1028 		if (!set) {
1029 			argstr(p, flag | (apply_ifs ? EXP_IFS_SPLIT : 0));
1030 			/*
1031 			 * ${x-a b c} doesn't get split, but removing the
1032 			 * 'apply_ifs = 0' apparently breaks ${1+"$@"}..
1033 			 * ${x-'a b' c} should generate 2 args.
1034 			 */
1035 			if (*p != CTLENDVAR)
1036 			/* We should have marked stuff already */
1037 				apply_ifs = 0;
1038 		}
1039 		break;
1040 
1041 	case VSTRIMLEFT:
1042 	case VSTRIMLEFTMAX:
1043 	case VSTRIMRIGHT:
1044 	case VSTRIMRIGHTMAX:
1045 		if (!set) {
1046 			set = 1;  /* allow argbackq to be advanced if needed */
1047 			break;
1048 		}
1049 		/*
1050 		 * Terminate the string and start recording the pattern
1051 		 * right after it
1052 		 */
1053 		STPUTC('\0', expdest);
1054 		patloc = expdest - stackblock();
1055 		if (subevalvar_trim(p, patloc, subtype, startloc, varflags,
1056 		    quotes) == 0) {
1057 			int amount = (expdest - stackblock() - patloc) + 1;
1058 			STADJUST(-amount, expdest);
1059 		}
1060 		/* Remove any recorded regions beyond start of variable */
1061 		removerecordregions(startloc);
1062 		apply_ifs = 1;
1063 		break;
1064 
1065 	case VSASSIGN:
1066 	case VSQUESTION:
1067 		if (set)
1068 			break;
1069 		if (subevalvar(p, var, subtype, startloc, varflags)) {
1070 			/* if subevalvar() returns, it always returns 1 */
1071 
1072 			varflags &= ~VSNUL;
1073 			/*
1074 			 * Remove any recorded regions beyond
1075 			 * start of variable
1076 			 */
1077 			removerecordregions(startloc);
1078 			goto again;
1079 		}
1080 		apply_ifs = 0;		/* never executed */
1081 		break;
1082 
1083 	default:
1084 		abort();
1085 	}
1086 
1087 	if (apply_ifs)
1088 		recordregion(startloc, expdest - stackblock(),
1089 			     varflags & VSQUOTE);
1090 
1091 	if (subtype != VSNORMAL) {	/* skip to end of alternative */
1092 		int nesting = 1;
1093 		for (;;) {
1094 			if ((c = *p++) == CTLESC)
1095 				p++;
1096 			else if (c == CTLNONL)
1097 				;
1098 			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
1099 				if (set)
1100 					argbackq = argbackq->next;
1101 			} else if (c == CTLVAR) {
1102 				if ((*p++ & VSTYPE) != VSNORMAL)
1103 					nesting++;
1104 			} else if (c == CTLENDVAR) {
1105 				if (--nesting == 0)
1106 					break;
1107 			}
1108 		}
1109 	}
1110 	return p;
1111 }
1112 
1113 
1114 
1115 /*
1116  * Test whether a special parameter is set.
1117  */
1118 
1119 STATIC int
1120 varisset(const char *name, int nulok)
1121 {
1122 	if (*name == '!')
1123 		return backgndpid != -1;
1124 	else if (*name == '@' || *name == '*') {
1125 		if (*shellparam.p == NULL)
1126 			return 0;
1127 
1128 		if (nulok) {
1129 			char **av;
1130 
1131 			for (av = shellparam.p; *av; av++)
1132 				if (**av != '\0')
1133 					return 1;
1134 			return 0;
1135 		}
1136 	} else if (is_digit(*name)) {
1137 		char *ap;
1138 		long num;
1139 
1140 		/*
1141 		 * handle overflow sensibly (the *ap tests should never fail)
1142 		 */
1143 		errno = 0;
1144 		num = strtol(name, &ap, 10);
1145 		if (errno != 0 || (*ap != '\0' && *ap != '='))
1146 			return 0;
1147 
1148 		if (num == 0)
1149 			ap = arg0;
1150 		else if (num > shellparam.nparam)
1151 			return 0;
1152 		else
1153 			ap = shellparam.p[num - 1];
1154 
1155 		if (nulok && (ap == NULL || *ap == '\0'))
1156 			return 0;
1157 	}
1158 	return 1;
1159 }
1160 
1161 
1162 
1163 /*
1164  * Add the value of a specialized variable to the stack string.
1165  */
1166 
1167 STATIC void
1168 varvalue(const char *name, int quoted, int subtype, int flag)
1169 {
1170 	int num;
1171 	char *p;
1172 	int i;
1173 	int sep;
1174 	char **ap;
1175 #ifdef DEBUG
1176 	char *start = expdest;
1177 #endif
1178 
1179 	VTRACE(DBG_EXPAND, ("varvalue(%c%s, sub=%d, fl=%#x)", *name,
1180 	    quoted ? ", quoted" : "", subtype, flag));
1181 
1182 	if (subtype == VSLENGTH)	/* no magic required ... */
1183 		flag &= ~EXP_FULL;
1184 
1185 #define STRTODEST(p) \
1186 	do {\
1187 		if ((flag & EXP_QNEEDED) && quoted) { \
1188 			while (*p) { \
1189 				if (NEEDESC(*p)) \
1190 					STPUTC(CTLESC, expdest); \
1191 				STPUTC(*p++, expdest); \
1192 			} \
1193 		} else \
1194 			while (*p) { \
1195 				if (ISCTL(*p)) \
1196 					STPUTC(CTLESC, expdest); \
1197 				STPUTC(*p++, expdest); \
1198 			} \
1199 	} while (0)
1200 
1201 
1202 	switch (*name) {
1203 	case '$':
1204 		num = rootpid;
1205 		break;
1206 	case '?':
1207 		num = exitstatus;
1208 		break;
1209 	case '#':
1210 		num = shellparam.nparam;
1211 		break;
1212 	case '!':
1213 		num = backgndpid;
1214 		break;
1215 	case '-':
1216 		for (i = 0; i < option_flags; i++) {
1217 			if (optlist[optorder[i]].val)
1218 				STPUTC(optlist[optorder[i]].letter, expdest);
1219 		}
1220 		VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start));
1221 		return;
1222 	case '@':
1223 		if (flag & EXP_SPLIT && quoted) {
1224 			VTRACE(DBG_EXPAND, (": $@ split (%d)\n",
1225 			    shellparam.nparam));
1226 #if 0
1227 		/* GROSS HACK */
1228 			if (shellparam.nparam == 0 &&
1229 				expdest[-1] == CTLQUOTEMARK)
1230 					expdest--;
1231 		/* KCAH SSORG */
1232 #endif
1233 			if (shellparam.nparam == 0)
1234 				empty_dollar_at = 1;
1235 
1236 			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
1237 				if (*p == '\0') {
1238 					/* retain an explicit null string */
1239 					STPUTC(CTLQUOTEMARK, expdest);
1240 					STPUTC(CTLQUOTEEND, expdest);
1241 				} else
1242 					STRTODEST(p);
1243 				if (*ap)
1244 					/* A NUL separates args inside "" */
1245 					STPUTC('\0', expdest);
1246 			}
1247 			return;
1248 		}
1249 		/* fall through */
1250 	case '*':
1251 		sep = ifsval()[0];
1252 		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
1253 			STRTODEST(p);
1254 			if (!*ap)
1255 				break;
1256 			if (sep) {
1257 				if (quoted && (flag & EXP_QNEEDED) &&
1258 				    NEEDESC(sep))
1259 					STPUTC(CTLESC, expdest);
1260 				STPUTC(sep, expdest);
1261 			} else
1262 			    if ((flag & (EXP_SPLIT|EXP_IN_QUOTES)) == EXP_SPLIT
1263 			      && !quoted && **ap != '\0')
1264 				STPUTC('\0', expdest);
1265 		}
1266 		VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start));
1267 		return;
1268 	default:
1269 		if (is_digit(*name)) {
1270 			long lnum;
1271 
1272 			errno = 0;
1273 			lnum = strtol(name, &p, 10);
1274 			if (errno != 0 || (*p != '\0' && *p != '='))
1275 				return;
1276 
1277 			if (lnum == 0)
1278 				p = arg0;
1279 			else if (lnum > 0 && lnum <= shellparam.nparam)
1280 				p = shellparam.p[lnum - 1];
1281 			else
1282 				return;
1283 			STRTODEST(p);
1284 		}
1285 		VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start));
1286 		return;
1287 	}
1288 	/*
1289 	 * only the specials with an int value arrive here
1290 	 */
1291 	VTRACE(DBG_EXPAND, ("(%d)", num));
1292 	expdest = cvtnum(num, expdest);
1293 	VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start));
1294 }
1295 
1296 
1297 
1298 /*
1299  * Record the fact that we have to scan this region of the
1300  * string for IFS characters.
1301  */
1302 
1303 STATIC void
1304 recordregion(int start, int end, int inquotes)
1305 {
1306 	struct ifsregion *ifsp;
1307 
1308 	VTRACE(DBG_EXPAND, ("recordregion(%d,%d,%d)\n", start, end, inquotes));
1309 	if (ifslastp == NULL) {
1310 		ifsp = &ifsfirst;
1311 	} else {
1312 		if (ifslastp->endoff == start
1313 		    && ifslastp->inquotes == inquotes) {
1314 			/* extend previous area */
1315 			ifslastp->endoff = end;
1316 			return;
1317 		}
1318 		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
1319 		ifslastp->next = ifsp;
1320 	}
1321 	ifslastp = ifsp;
1322 	ifslastp->next = NULL;
1323 	ifslastp->begoff = start;
1324 	ifslastp->endoff = end;
1325 	ifslastp->inquotes = inquotes;
1326 }
1327 
1328 
1329 
1330 /*
1331  * Break the argument string into pieces based upon IFS and add the
1332  * strings to the argument list.  The regions of the string to be
1333  * searched for IFS characters have been stored by recordregion.
1334  */
1335 STATIC void
1336 ifsbreakup(char *string, struct arglist *arglist)
1337 {
1338 	struct ifsregion *ifsp;
1339 	struct strlist *sp;
1340 	char *start;
1341 	char *p;
1342 	char *q;
1343 	const char *ifs;
1344 	const char *ifsspc;
1345 	int had_param_ch = 0;
1346 
1347 	start = string;
1348 
1349 	VTRACE(DBG_EXPAND, ("ifsbreakup(\"%s\")", string)); /* misses \0's */
1350 	if (ifslastp == NULL) {
1351 		/* Return entire argument, IFS doesn't apply to any of it */
1352 		VTRACE(DBG_EXPAND, ("no regions\n", string));
1353 		sp = stalloc(sizeof(*sp));
1354 		sp->text = start;
1355 		*arglist->lastp = sp;
1356 		arglist->lastp = &sp->next;
1357 		return;
1358 	}
1359 
1360 	ifs = ifsval();
1361 
1362 	for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
1363 		p = string + ifsp->begoff;
1364 		VTRACE(DBG_EXPAND, (" !%.*s!(%d)", ifsp->endoff-ifsp->begoff,
1365 		    p, ifsp->endoff-ifsp->begoff));
1366 		while (p < string + ifsp->endoff) {
1367 			had_param_ch = 1;
1368 			q = p;
1369 			if (IS_BORING(*p)) {
1370 				p++;
1371 				continue;
1372 			}
1373 			if (*p == CTLESC)
1374 				p++;
1375 			if (ifsp->inquotes) {
1376 				/* Only NULs (should be from "$@") end args */
1377 				if (*p != 0) {
1378 					p++;
1379 					continue;
1380 				}
1381 				ifsspc = NULL;
1382 				VTRACE(DBG_EXPAND, (" \\0 nxt:\"%s\" ", p));
1383 			} else {
1384 				if (!strchr(ifs, *p)) {
1385 					p++;
1386 					continue;
1387 				}
1388 				had_param_ch = 0;
1389 				ifsspc = strchr(" \t\n", *p);
1390 
1391 				/* Ignore IFS whitespace at start */
1392 				if (q == start && ifsspc != NULL) {
1393 					p++;
1394 					start = p;
1395 					continue;
1396 				}
1397 			}
1398 
1399 			/* Save this argument... */
1400 			*q = '\0';
1401 			VTRACE(DBG_EXPAND, ("<%s>", start));
1402 			sp = stalloc(sizeof(*sp));
1403 			sp->text = start;
1404 			*arglist->lastp = sp;
1405 			arglist->lastp = &sp->next;
1406 			p++;
1407 
1408 			if (ifsspc != NULL) {
1409 				/* Ignore further trailing IFS whitespace */
1410 				for (; p < string + ifsp->endoff; p++) {
1411 					q = p;
1412 					if (*p == CTLNONL)
1413 						continue;
1414 					if (*p == CTLESC)
1415 						p++;
1416 					if (strchr(ifs, *p) == NULL) {
1417 						p = q;
1418 						break;
1419 					}
1420 					if (strchr(" \t\n", *p) == NULL) {
1421 						p++;
1422 						break;
1423 					}
1424 				}
1425 			}
1426 			start = p;
1427 		}
1428 	}
1429 
1430 /*
1431 	while (*start == CTLQUOTEEND)
1432 		start++;
1433 */
1434 
1435 	/*
1436 	 * Save anything left as an argument.
1437 	 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
1438 	 * generating 2 arguments, the second of which is empty.
1439 	 * Some recent clarification of the Posix spec say that it
1440 	 * should only generate one....
1441 	 */
1442 	if (had_param_ch || *start != 0) {
1443 		VTRACE(DBG_EXPAND, (" T<%s>", start));
1444 		sp = stalloc(sizeof(*sp));
1445 		sp->text = start;
1446 		*arglist->lastp = sp;
1447 		arglist->lastp = &sp->next;
1448 	}
1449 	VTRACE(DBG_EXPAND, ("\n"));
1450 }
1451 
1452 STATIC void
1453 ifsfree(void)
1454 {
1455 	while (ifsfirst.next != NULL) {
1456 		struct ifsregion *ifsp;
1457 		INTOFF;
1458 		ifsp = ifsfirst.next->next;
1459 		ckfree(ifsfirst.next);
1460 		ifsfirst.next = ifsp;
1461 		INTON;
1462 	}
1463 	ifslastp = NULL;
1464 	ifsfirst.next = NULL;
1465 }
1466 
1467 
1468 
1469 /*
1470  * Expand shell metacharacters.  At this point, the only control characters
1471  * should be escapes.  The results are stored in the list exparg.
1472  */
1473 
1474 char *expdir;
1475 
1476 
1477 STATIC void
1478 expandmeta(struct strlist *str, int flag)
1479 {
1480 	char *p;
1481 	struct strlist **savelastp;
1482 	struct strlist *sp;
1483 	char c;
1484 	/* TODO - EXP_REDIR */
1485 
1486 	while (str) {
1487 		p = str->text;
1488 		for (;;) {			/* fast check for meta chars */
1489 			if ((c = *p++) == '\0')
1490 				goto nometa;
1491 			if (c == '*' || c == '?' || c == '[' /* || c == '!' */)
1492 				break;
1493 		}
1494 		savelastp = exparg.lastp;
1495 		INTOFF;
1496 		if (expdir == NULL) {
1497 			int i = strlen(str->text);
1498 			expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
1499 		}
1500 
1501 		expmeta(expdir, str->text);
1502 		ckfree(expdir);
1503 		expdir = NULL;
1504 		INTON;
1505 		if (exparg.lastp == savelastp) {
1506 			/*
1507 			 * no matches
1508 			 */
1509  nometa:
1510 			*exparg.lastp = str;
1511 			rmescapes(str->text);
1512 			exparg.lastp = &str->next;
1513 		} else {
1514 			*exparg.lastp = NULL;
1515 			*savelastp = sp = expsort(*savelastp);
1516 			while (sp->next != NULL)
1517 				sp = sp->next;
1518 			exparg.lastp = &sp->next;
1519 		}
1520 		str = str->next;
1521 	}
1522 }
1523 
1524 STATIC void
1525 add_args(struct strlist *str)
1526 {
1527 	while (str) {
1528 		*exparg.lastp = str;
1529 		rmescapes(str->text);
1530 		exparg.lastp = &str->next;
1531 		str = str->next;
1532 	}
1533 }
1534 
1535 
1536 /*
1537  * Do metacharacter (i.e. *, ?, [...]) expansion.
1538  */
1539 
1540 STATIC void
1541 expmeta(char *enddir, char *name)
1542 {
1543 	char *p;
1544 	const char *cp;
1545 	char *q;
1546 	char *start;
1547 	char *endname;
1548 	int metaflag;
1549 	struct stat statb;
1550 	DIR *dirp;
1551 	struct dirent *dp;
1552 	int atend;
1553 	int matchdot;
1554 
1555 	CTRACE(DBG_EXPAND|DBG_MATCH, ("expmeta(\"%s\")\n", name));
1556 	metaflag = 0;
1557 	start = name;
1558 	for (p = name ; ; p++) {
1559 		if (*p == '*' || *p == '?')
1560 			metaflag = 1;
1561 		else if (*p == '[') {
1562 			q = p + 1;
1563 			if (*q == '!' || *q == '^')
1564 				q++;
1565 			for (;;) {
1566 				while (IS_BORING(*q))
1567 					q++;
1568 				if (*q == ']') {
1569 					q++;
1570 					metaflag = 1;
1571 					break;
1572 				}
1573 				if (*q == '[' && q[1] == ':') {
1574 					/*
1575 					 * character class, look for :] ending
1576 					 * also stop on ']' (end bracket expr)
1577 					 * or '\0' or '/' (end pattern)
1578 					 */
1579 					while (*++q != '\0' && *q != ']' &&
1580 					    *q != '/') {
1581 						if (*q == CTLESC) {
1582 							if (*++q == '\0')
1583 								break;
1584 							if (*q == '/')
1585 								break;
1586 						} else if (*q == ':' &&
1587 						    q[1] == ']')
1588 							break;
1589 					}
1590 					if (*q == ':') {
1591 						/*
1592 						 * stopped at ':]'
1593 						 * still in [...]
1594 						 * skip ":]" and continue;
1595 						 */
1596 						q += 2;
1597 						continue;
1598 					}
1599 
1600 					/* done at end of pattern, not [...] */
1601 					if (*q == '\0' || *q == '/')
1602 						break;
1603 
1604 					/* found the ']', we have a [...] */
1605 					metaflag = 1;
1606 					q++;	/* skip ']' */
1607 					break;
1608 				}
1609 				if (*q == CTLESC)
1610 					q++;
1611 				/* end of pattern cannot be escaped */
1612 				if (*q == '/' || *q == '\0')
1613 					break;
1614 				q++;
1615 			}
1616 		} else if (*p == '\0')
1617 			break;
1618 		else if (IS_BORING(*p))
1619 			continue;
1620 		else if (*p == CTLESC)
1621 			p++;
1622 		if (*p == '/') {
1623 			if (metaflag)
1624 				break;
1625 			start = p + 1;
1626 		}
1627 	}
1628 	if (metaflag == 0) {	/* we've reached the end of the file name */
1629 		if (enddir != expdir)
1630 			metaflag++;
1631 		for (p = name ; ; p++) {
1632 			if (IS_BORING(*p))
1633 				continue;
1634 			if (*p == CTLESC)
1635 				p++;
1636 			*enddir++ = *p;
1637 			if (*p == '\0')
1638 				break;
1639 		}
1640 		if (metaflag == 0 || lstat(expdir, &statb) >= 0)
1641 			addfname(expdir);
1642 		return;
1643 	}
1644 	endname = p;
1645 	if (start != name) {
1646 		p = name;
1647 		while (p < start) {
1648 			while (IS_BORING(*p))
1649 				p++;
1650 			if (*p == CTLESC)
1651 				p++;
1652 			*enddir++ = *p++;
1653 		}
1654 	}
1655 	if (enddir == expdir) {
1656 		cp = ".";
1657 	} else if (enddir == expdir + 1 && *expdir == '/') {
1658 		cp = "/";
1659 	} else {
1660 		cp = expdir;
1661 		enddir[-1] = '\0';
1662 	}
1663 	if ((dirp = opendir(cp)) == NULL)
1664 		return;
1665 	if (enddir != expdir)
1666 		enddir[-1] = '/';
1667 	if (*endname == 0) {
1668 		atend = 1;
1669 	} else {
1670 		atend = 0;
1671 		*endname++ = '\0';
1672 	}
1673 	matchdot = 0;
1674 	p = start;
1675 	while (IS_BORING(*p))
1676 		p++;
1677 	if (*p == CTLESC)
1678 		p++;
1679 	if (*p == '.')
1680 		matchdot++;
1681 	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
1682 		if (dp->d_name[0] == '.' && ! matchdot)
1683 			continue;
1684 		if (patmatch(start, dp->d_name, 0)) {
1685 			if (atend) {
1686 				scopy(dp->d_name, enddir);
1687 				addfname(expdir);
1688 			} else {
1689 				for (p = enddir, cp = dp->d_name;
1690 				     (*p++ = *cp++) != '\0';)
1691 					continue;
1692 				p[-1] = '/';
1693 				expmeta(p, endname);
1694 			}
1695 		}
1696 	}
1697 	closedir(dirp);
1698 	if (! atend)
1699 		endname[-1] = '/';
1700 }
1701 
1702 
1703 /*
1704  * Add a file name to the list.
1705  */
1706 
1707 STATIC void
1708 addfname(char *name)
1709 {
1710 	char *p;
1711 	struct strlist *sp;
1712 
1713 	p = stalloc(strlen(name) + 1);
1714 	scopy(name, p);
1715 	sp = stalloc(sizeof(*sp));
1716 	sp->text = p;
1717 	*exparg.lastp = sp;
1718 	exparg.lastp = &sp->next;
1719 }
1720 
1721 
1722 /*
1723  * Sort the results of file name expansion.  It calculates the number of
1724  * strings to sort and then calls msort (short for merge sort) to do the
1725  * work.
1726  */
1727 
1728 STATIC struct strlist *
1729 expsort(struct strlist *str)
1730 {
1731 	int len;
1732 	struct strlist *sp;
1733 
1734 	len = 0;
1735 	for (sp = str ; sp ; sp = sp->next)
1736 		len++;
1737 	return msort(str, len);
1738 }
1739 
1740 
1741 STATIC struct strlist *
1742 msort(struct strlist *list, int len)
1743 {
1744 	struct strlist *p, *q = NULL;
1745 	struct strlist **lpp;
1746 	int half;
1747 	int n;
1748 
1749 	if (len <= 1)
1750 		return list;
1751 	half = len >> 1;
1752 	p = list;
1753 	for (n = half ; --n >= 0 ; ) {
1754 		q = p;
1755 		p = p->next;
1756 	}
1757 	q->next = NULL;			/* terminate first half of list */
1758 	q = msort(list, half);		/* sort first half of list */
1759 	p = msort(p, len - half);		/* sort second half */
1760 	lpp = &list;
1761 	for (;;) {
1762 		if (strcmp(p->text, q->text) < 0) {
1763 			*lpp = p;
1764 			lpp = &p->next;
1765 			if ((p = *lpp) == NULL) {
1766 				*lpp = q;
1767 				break;
1768 			}
1769 		} else {
1770 			*lpp = q;
1771 			lpp = &q->next;
1772 			if ((q = *lpp) == NULL) {
1773 				*lpp = p;
1774 				break;
1775 			}
1776 		}
1777 	}
1778 	return list;
1779 }
1780 
1781 
1782 /*
1783  * See if a character matches a character class, starting at the first colon
1784  * of "[:class:]".
1785  * If a valid character class is recognized, a pointer to the next character
1786  * after the final closing bracket is stored into *end, otherwise a null
1787  * pointer is stored into *end.
1788  */
1789 static int
1790 match_charclass(const char *p, wchar_t chr, const char **end)
1791 {
1792 	char name[20];
1793 	char *nameend;
1794 	wctype_t cclass;
1795 	char *q;
1796 
1797 	*end = NULL;
1798 	p++;
1799 	q = &name[0];
1800 	nameend = strstr(p, ":]");
1801 	if (nameend == NULL || nameend == p)	/* not a valid class */
1802 		return 0;
1803 
1804 	if (*p == CTLESC) {
1805 		if (*++p == CTLESC)
1806 			return 0;
1807 		if (p == nameend)
1808 			return 0;
1809 	}
1810 	if (!is_alpha(*p))
1811 		return 0;
1812 	while (p < nameend) {
1813 		if (*p == CTLESC) {
1814 			p++;
1815 			if (p == nameend)
1816 				return 0;
1817 		}
1818 		if (!is_in_name(*p))	/* '_' is a local extension */
1819 			return 0;
1820 		if (q < &name[sizeof name])
1821 			*q++ = *p++;
1822 		else
1823 			p++;
1824 	}
1825 
1826 	*end = nameend + 2;		/* committed to it being a char class */
1827 
1828 	if (q < &name[sizeof name])	/* a usable name found */
1829 		*q++ = '\0';
1830 	else				/* too long, valid, but no match */
1831 		return 0;
1832 
1833 	cclass = wctype(name);
1834 	/* An unknown class matches nothing but is valid nevertheless. */
1835 	if (cclass == 0)
1836 		return 0;
1837 	return iswctype(chr, cclass);
1838 }
1839 
1840 
1841 /*
1842  * Returns true if the pattern matches the string.
1843  */
1844 
1845 STATIC int
1846 patmatch(const char *pattern, const char *string, int squoted)
1847 {
1848 	const char *p, *q, *end;
1849 	const char *bt_p, *bt_q;
1850 	char c;
1851 	wchar_t wc, wc2;
1852 
1853 	VTRACE(DBG_MATCH, ("patmatch(P=\"%s\", W=\"%s\"%s): ",
1854 	    pattern, string, squoted ? ", SQ" : ""));
1855 	p = pattern;
1856 	q = string;
1857 	bt_p = NULL;
1858 	bt_q = NULL;
1859 	for (;;) {
1860 		switch (c = *p++) {
1861 		case '\0':
1862 			if (squoted && *q == CTLESC) {
1863 				if (q[1] == '\0')
1864 					q++;
1865 			}
1866 			if (*q != '\0')
1867 				goto backtrack;
1868 			VTRACE(DBG_MATCH, ("match\n"));
1869 			return 1;
1870 		case CTLESC:
1871 			if (squoted && *q == CTLESC)
1872 				q++;
1873 			if (*p == '\0' && *q == '\0') {
1874 				VTRACE(DBG_MATCH, ("match-\\\n"));
1875 				return 1;
1876 			}
1877 			if (*q++ != *p++)
1878 				goto backtrack;
1879 			break;
1880 		case '\\':
1881 			if (squoted && *q == CTLESC)
1882 				q++;
1883 			if (*q++ != *p++)
1884 				goto backtrack;
1885 			break;
1886 		case CTLQUOTEMARK:
1887 		case CTLQUOTEEND:
1888 		case CTLNONL:
1889 			continue;
1890 		case '?':
1891 			if (squoted && *q == CTLESC)
1892 				q++;
1893 			if (*q++ == '\0') {
1894 				VTRACE(DBG_MATCH, ("?fail\n"));
1895 				return 0;
1896 			}
1897 			break;
1898 		case '*':
1899 			c = *p;
1900 			while (c == CTLQUOTEMARK || c == '*')
1901 				c = *++p;
1902 			if (c != CTLESC && !IS_BORING(c) &&
1903 			    c != '?' && c != '*' && c != '[') {
1904 				while (*q != c) {
1905 					if (squoted && *q == CTLESC &&
1906 					    q[1] == c)
1907 						break;
1908 					if (*q == '\0') {
1909 						VTRACE(DBG_MATCH, ("*fail\n"));
1910 						return 0;
1911 					}
1912 					if (squoted && *q == CTLESC)
1913 						q++;
1914 					q++;
1915 				}
1916 			}
1917 			if (c == CTLESC && p[1] == '\0') {
1918 				VTRACE(DBG_MATCH, ("match+\\\n"));
1919 				return 1;
1920 			}
1921 			/*
1922 			 * First try the shortest match for the '*' that
1923 			 * could work. We can forget any earlier '*' since
1924 			 * there is no way having it match more characters
1925 			 * can help us, given that we are already here.
1926 			 */
1927 			bt_p = p;
1928 			bt_q = q;
1929 			break;
1930 		case '[': {
1931 			const char *savep, *saveq, *endp;
1932 			int invert, found;
1933 			unsigned char chr;
1934 
1935 			/*
1936 			 * First quick check to see if there is a
1937 			 * possible matching ']' - if not, then this
1938 			 * is not a char class, and the '[' is just
1939 			 * a literal '['.
1940 			 *
1941 			 * This check will not detect all non classes, but
1942 			 * that's OK - It just means that we execute the
1943 			 * harder code sometimes when it it cannot succeed.
1944 			 */
1945 			endp = p;
1946 			if (*endp == '!' || *endp == '^')
1947 				endp++;
1948 			for (;;) {
1949 				while (IS_BORING(*endp))
1950 					endp++;
1951 				if (*endp == '\0')
1952 					goto dft;	/* no matching ] */
1953 				if (*endp++ == ']')
1954 					break;
1955 			}
1956 			/* end shortcut */
1957 
1958 			savep = p, saveq = q;
1959 			invert = 0;
1960 			if (*p == '!' || *p == '^') {
1961 				invert++;
1962 				p++;
1963 			}
1964 			found = 0;
1965 			if (*q == '\0') {
1966 				VTRACE(DBG_MATCH, ("[]fail\n"));
1967 				return 0;
1968 			}
1969 			if (squoted && *q == CTLESC)
1970 				q++;
1971 			chr = (unsigned char)*q++;
1972 			c = *p++;
1973 			do {
1974 				if (IS_BORING(c))
1975 					continue;
1976 				if (c == '\0') {
1977 					p = savep, q = saveq;
1978 					c = '[';
1979 					goto dft;
1980 				}
1981 				if (c == '[' && *p == ':') {
1982 					found |= match_charclass(p, chr, &end);
1983 					if (end != NULL) {
1984 						p = end;
1985 						continue;
1986 					}
1987 				}
1988 				if (c == CTLESC || c == '\\')
1989 					c = *p++;
1990 				wc = (unsigned char)c;
1991 				if (*p == '-' && p[1] != ']') {
1992 					p++;
1993 					if (*p == CTLESC || *p == '\\')
1994 						p++;
1995 					wc2 = (unsigned char)*p++;
1996 					if (   collate_range_cmp(chr, wc) >= 0
1997 					    && collate_range_cmp(chr, wc2) <= 0
1998 					   )
1999 						found = 1;
2000 				} else {
2001 					if (chr == wc)
2002 						found = 1;
2003 				}
2004 			} while ((c = *p++) != ']');
2005 			if (found == invert)
2006 				goto backtrack;
2007 			break;
2008 		}
2009   dft:		default:
2010 			if (squoted && *q == CTLESC)
2011 				q++;
2012 			if (*q++ == c)
2013 				break;
2014   backtrack:
2015 			/*
2016 			 * If we have a mismatch (other than hitting the end
2017 			 * of the string), go back to the last '*' seen and
2018 			 * have it match one additional character.
2019 			 */
2020 			if (bt_p == NULL) {
2021 				VTRACE(DBG_MATCH, ("BTP fail\n"));
2022 				return 0;
2023 			}
2024 			if (*bt_q == '\0') {
2025 				VTRACE(DBG_MATCH, ("BTQ fail\n"));
2026 				return 0;
2027 			}
2028 			bt_q++;
2029 			p = bt_p;
2030 			q = bt_q;
2031 			break;
2032 		}
2033 	}
2034 }
2035 
2036 
2037 
2038 /*
2039  * Remove any CTLESC or CTLNONL characters from a string.
2040  */
2041 
2042 void
2043 rmescapes(char *str)
2044 {
2045 	char *p, *q;
2046 
2047 	p = str;
2048 	while (!ISCTL(*p)) {
2049 		if (*p++ == '\0')
2050 			return;
2051 	}
2052 	q = p;
2053 	while (*p) {
2054 		if (IS_BORING(*p)) {
2055 			p++;
2056 			continue;
2057 		}
2058 		if (*p == CTLCNL) {
2059 			p++;
2060 			*q++ = '\n';
2061 			continue;
2062 		}
2063 		if (*p == CTLESC)
2064 			p++;
2065 #ifdef DEBUG
2066 		else if (ISCTL(*p))
2067 			abort();
2068 #endif
2069 		*q++ = *p++;
2070 	}
2071 	*q = '\0';
2072 }
2073 
2074 /*
2075  * and a special version for dealing with expressions to be parsed
2076  * by the arithmetic evaluator.   That needs to be able to count \n's
2077  * even ones that were \newline elided \n's, so we have to put the
2078  * latter back into the string - just being careful to put them only
2079  * at a place where white space can reasonably occur in the string
2080  * -- then the \n we insert will just be white space, and ignored
2081  * for all purposes except line counting.
2082  */
2083 
2084 void
2085 rmescapes_nl(char *str)
2086 {
2087 	char *p, *q;
2088 	int nls = 0, holdnl = 0, holdlast;
2089 
2090 	p = str;
2091 	while (!ISCTL(*p)) {
2092 		if (*p++ == '\0')
2093 			return;
2094 	}
2095 	if (p > str)	/* must reprocess char before stopper (if any) */
2096 		--p;	/* so we do not place a \n badly */
2097 	q = p;
2098 	while (*p) {
2099 		if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) {
2100 			p++;
2101 			continue;
2102 		}
2103 		if (*p == CTLNONL) {
2104 			p++;
2105 			nls++;
2106 			continue;
2107 		}
2108 		if (*p == CTLCNL) {
2109 			p++;
2110 			*q++ = '\n';
2111 			continue;
2112 		}
2113 		if (*p == CTLESC)
2114 			p++;
2115 #ifdef DEBUG
2116 		else if (ISCTL(*p))
2117 			abort();
2118 #endif
2119 
2120 		holdlast = holdnl;
2121 		holdnl = is_in_name(*p);	/* letters, digits, _ */
2122 		if (q == str || is_space(q[-1]) || (*p != '=' && q[-1] != *p)) {
2123 			if (nls > 0 && holdnl != holdlast) {
2124 				while (nls > 0)
2125 					*q++ = '\n', nls--;
2126 			}
2127 		}
2128 		*q++ = *p++;
2129 	}
2130 	while (--nls >= 0)
2131 		*q++ = '\n';
2132 	*q = '\0';
2133 }
2134 
2135 
2136 
2137 /*
2138  * See if a pattern matches in a case statement.
2139  */
2140 
2141 int
2142 casematch(union node *pattern, char *val)
2143 {
2144 	struct stackmark smark;
2145 	int result;
2146 	char *p;
2147 
2148 	CTRACE(DBG_MATCH, ("casematch(P=\"%s\", W=\"%s\")\n",
2149 	    pattern->narg.text, val));
2150 	setstackmark(&smark);
2151 	argbackq = pattern->narg.backquote;
2152 	STARTSTACKSTR(expdest);
2153 	ifslastp = NULL;
2154 	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
2155 	STPUTC('\0', expdest);
2156 	p = grabstackstr(expdest);
2157 	result = patmatch(p, val, 0);
2158 	popstackmark(&smark);
2159 	return result;
2160 }
2161 
2162 /*
2163  * Our own itoa().   Assumes result buffer is on the stack
2164  */
2165 
2166 STATIC char *
2167 cvtnum(int num, char *buf)
2168 {
2169 	char temp[32];
2170 	int neg = num < 0;
2171 	char *p = temp + sizeof temp - 1;
2172 
2173 	if (neg)
2174 		num = -num;
2175 
2176 	*p = '\0';
2177 	do {
2178 		*--p = num % 10 + '0';
2179 	} while ((num /= 10) != 0 && p > temp + 1);
2180 
2181 	if (neg)
2182 		*--p = '-';
2183 
2184 	while (*p)
2185 		STPUTC(*p++, buf);
2186 	return buf;
2187 }
2188 
2189 /*
2190  * Do most of the work for wordexp(3).
2191  */
2192 
2193 int
2194 wordexpcmd(int argc, char **argv)
2195 {
2196 	size_t len;
2197 	int i;
2198 
2199 	out1fmt("%d", argc - 1);
2200 	out1c('\0');
2201 	for (i = 1, len = 0; i < argc; i++)
2202 		len += strlen(argv[i]);
2203 	out1fmt("%zu", len);
2204 	out1c('\0');
2205 	for (i = 1; i < argc; i++) {
2206 		out1str(argv[i]);
2207 		out1c('\0');
2208 	}
2209 	return (0);
2210 }
2211