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