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