xref: /netbsd-src/bin/sh/expand.c (revision 1fbf0781c68e4b33cb60e78a8755ebda79e736a7)
1 /*	$NetBSD: expand.c,v 1.42 1999/01/25 14:20:56 mycroft 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. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)expand.c	8.5 (Berkeley) 5/15/95";
43 #else
44 __RCSID("$NetBSD: expand.c,v 1.42 1999/01/25 14:20:56 mycroft Exp $");
45 #endif
46 #endif /* not lint */
47 
48 #include <sys/types.h>
49 #include <sys/time.h>
50 #include <sys/stat.h>
51 #include <errno.h>
52 #include <dirent.h>
53 #include <unistd.h>
54 #include <pwd.h>
55 #include <stdlib.h>
56 #include <stdio.h>
57 
58 /*
59  * Routines to expand arguments to commands.  We have to deal with
60  * backquotes, shell variables, and file metacharacters.
61  */
62 
63 #include "shell.h"
64 #include "main.h"
65 #include "nodes.h"
66 #include "eval.h"
67 #include "expand.h"
68 #include "syntax.h"
69 #include "parser.h"
70 #include "jobs.h"
71 #include "options.h"
72 #include "var.h"
73 #include "input.h"
74 #include "output.h"
75 #include "memalloc.h"
76 #include "error.h"
77 #include "mystring.h"
78 #include "arith.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 nulonly;		/* 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 void argstr __P((char *, int));
101 STATIC char *exptilde __P((char *, int));
102 STATIC void expbackq __P((union node *, int, int));
103 STATIC int subevalvar __P((char *, char *, int, int, int, int));
104 STATIC char *evalvar __P((char *, int));
105 STATIC int varisset __P((char *, int));
106 STATIC void varvalue __P((char *, int, int));
107 STATIC void recordregion __P((int, int, int));
108 STATIC void removerecordregions __P((int));
109 STATIC void ifsbreakup __P((char *, struct arglist *));
110 STATIC void ifsfree __P((void));
111 STATIC void expandmeta __P((struct strlist *, int));
112 STATIC void expmeta __P((char *, char *));
113 STATIC void addfname __P((char *));
114 STATIC struct strlist *expsort __P((struct strlist *));
115 STATIC struct strlist *msort __P((struct strlist *, int));
116 STATIC int pmatch __P((char *, char *));
117 STATIC char *cvtnum __P((int, char *));
118 
119 /*
120  * Expand shell variables and backquotes inside a here document.
121  */
122 
123 void
124 expandhere(arg, fd)
125 	union node *arg;	/* the document */
126 	int fd;			/* where to write the expanded version */
127 	{
128 	herefd = fd;
129 	expandarg(arg, (struct arglist *)NULL, 0);
130 	xwrite(fd, stackblock(), expdest - stackblock());
131 }
132 
133 
134 /*
135  * Perform variable substitution and command substitution on an argument,
136  * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
137  * perform splitting and file name expansion.  When arglist is NULL, perform
138  * here document expansion.
139  */
140 
141 void
142 expandarg(arg, arglist, flag)
143 	union node *arg;
144 	struct arglist *arglist;
145 	int flag;
146 {
147 	struct strlist *sp;
148 	char *p;
149 
150 	argbackq = arg->narg.backquote;
151 	STARTSTACKSTR(expdest);
152 	ifsfirst.next = NULL;
153 	ifslastp = NULL;
154 	argstr(arg->narg.text, flag);
155 	if (arglist == NULL) {
156 		return;			/* here document expanded */
157 	}
158 	STPUTC('\0', expdest);
159 	p = grabstackstr(expdest);
160 	exparg.lastp = &exparg.list;
161 	/*
162 	 * TODO - EXP_REDIR
163 	 */
164 	if (flag & EXP_FULL) {
165 		ifsbreakup(p, &exparg);
166 		*exparg.lastp = NULL;
167 		exparg.lastp = &exparg.list;
168 		expandmeta(exparg.list, flag);
169 	} else {
170 		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
171 			rmescapes(p);
172 		sp = (struct strlist *)stalloc(sizeof (struct strlist));
173 		sp->text = p;
174 		*exparg.lastp = sp;
175 		exparg.lastp = &sp->next;
176 	}
177 	ifsfree();
178 	*exparg.lastp = NULL;
179 	if (exparg.list) {
180 		*arglist->lastp = exparg.list;
181 		arglist->lastp = exparg.lastp;
182 	}
183 }
184 
185 
186 
187 /*
188  * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
189  * characters to allow for further processing.  Otherwise treat
190  * $@ like $* since no splitting will be performed.
191  */
192 
193 STATIC void
194 argstr(p, flag)
195 	char *p;
196 	int flag;
197 {
198 	char c;
199 	int quotes = flag & (EXP_FULL | EXP_CASE);	/* do CTLESC */
200 	int firsteq = 1;
201 
202 	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
203 		p = exptilde(p, flag);
204 	for (;;) {
205 		switch (c = *p++) {
206 		case '\0':
207 		case CTLENDVAR: /* ??? */
208 			goto breakloop;
209 		case CTLQUOTEMARK:
210 			/* "$@" syntax adherence hack */
211 			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
212 				break;
213 			if ((flag & EXP_FULL) != 0)
214 				STPUTC(c, expdest);
215 			break;
216 		case CTLESC:
217 			if (quotes)
218 				STPUTC(c, expdest);
219 			c = *p++;
220 			STPUTC(c, expdest);
221 			break;
222 		case CTLVAR:
223 			p = evalvar(p, flag);
224 			break;
225 		case CTLBACKQ:
226 		case CTLBACKQ|CTLQUOTE:
227 			expbackq(argbackq->n, c & CTLQUOTE, flag);
228 			argbackq = argbackq->next;
229 			break;
230 		case CTLENDARI:
231 			expari(flag);
232 			break;
233 		case ':':
234 		case '=':
235 			/*
236 			 * sort of a hack - expand tildes in variable
237 			 * assignments (after the first '=' and after ':'s).
238 			 */
239 			STPUTC(c, expdest);
240 			if (flag & EXP_VARTILDE && *p == '~') {
241 				if (c == '=') {
242 					if (firsteq)
243 						firsteq = 0;
244 					else
245 						break;
246 				}
247 				p = exptilde(p, flag);
248 			}
249 			break;
250 		default:
251 			STPUTC(c, expdest);
252 		}
253 	}
254 breakloop:;
255 	return;
256 }
257 
258 STATIC char *
259 exptilde(p, flag)
260 	char *p;
261 	int flag;
262 {
263 	char c, *startp = p;
264 	struct passwd *pw;
265 	const char *home;
266 	int quotes = flag & (EXP_FULL | EXP_CASE);
267 
268 	while ((c = *p) != '\0') {
269 		switch(c) {
270 		case CTLESC:
271 			return (startp);
272 		case CTLQUOTEMARK:
273 			return (startp);
274 		case ':':
275 			if (flag & EXP_VARTILDE)
276 				goto done;
277 			break;
278 		case '/':
279 			goto done;
280 		}
281 		p++;
282 	}
283 done:
284 	*p = '\0';
285 	if (*(startp+1) == '\0') {
286 		if ((home = lookupvar("HOME")) == NULL)
287 			goto lose;
288 	} else {
289 		if ((pw = getpwnam(startp+1)) == NULL)
290 			goto lose;
291 		home = pw->pw_dir;
292 	}
293 	if (*home == '\0')
294 		goto lose;
295 	*p = c;
296 	while ((c = *home++) != '\0') {
297 		if (quotes && SQSYNTAX[(int)c] == CCTL)
298 			STPUTC(CTLESC, expdest);
299 		STPUTC(c, expdest);
300 	}
301 	return (p);
302 lose:
303 	*p = c;
304 	return (startp);
305 }
306 
307 
308 STATIC void
309 removerecordregions(endoff)
310 	int endoff;
311 {
312 	if (ifslastp == NULL)
313 		return;
314 
315 	if (ifsfirst.endoff > endoff) {
316 		while (ifsfirst.next != NULL) {
317 			struct ifsregion *ifsp;
318 			INTOFF;
319 			ifsp = ifsfirst.next->next;
320 			ckfree(ifsfirst.next);
321 			ifsfirst.next = ifsp;
322 			INTON;
323 		}
324 		if (ifsfirst.begoff > endoff)
325 			ifslastp = NULL;
326 		else {
327 			ifslastp = &ifsfirst;
328 			ifsfirst.endoff = endoff;
329 		}
330 		return;
331 	}
332 
333 	ifslastp = &ifsfirst;
334 	while (ifslastp->next && ifslastp->next->begoff < endoff)
335 		ifslastp=ifslastp->next;
336 	while (ifslastp->next != NULL) {
337 		struct ifsregion *ifsp;
338 		INTOFF;
339 		ifsp = ifslastp->next->next;
340 		ckfree(ifslastp->next);
341 		ifslastp->next = ifsp;
342 		INTON;
343 	}
344 	if (ifslastp->endoff > endoff)
345 		ifslastp->endoff = endoff;
346 }
347 
348 
349 /*
350  * Expand arithmetic expression.  Backup to start of expression,
351  * evaluate, place result in (backed up) result, adjust string position.
352  */
353 void
354 expari(flag)
355 	int flag;
356 {
357 	char *p, *start;
358 	int result;
359 	int begoff;
360 	int quotes = flag & (EXP_FULL | EXP_CASE);
361 	int quoted;
362 
363 	/*	ifsfree(); */
364 
365 	/*
366 	 * This routine is slightly over-complicated for
367 	 * efficiency.  First we make sure there is
368 	 * enough space for the result, which may be bigger
369 	 * than the expression if we add exponentation.  Next we
370 	 * scan backwards looking for the start of arithmetic.  If the
371 	 * next previous character is a CTLESC character, then we
372 	 * have to rescan starting from the beginning since CTLESC
373 	 * characters have to be processed left to right.
374 	 */
375 	CHECKSTRSPACE(8, expdest);
376 	USTPUTC('\0', expdest);
377 	start = stackblock();
378 	p = expdest;
379 	while (*p != CTLARI && p >= start)
380 		--p;
381 	if (*p != CTLARI)
382 		error("missing CTLARI (shouldn't happen)");
383 	if (p > start && *(p-1) == CTLESC)
384 		for (p = start; *p != CTLARI; p++)
385 			if (*p == CTLESC)
386 				p++;
387 
388 	if (p[1] == '"')
389 		quoted=1;
390 	else
391 		quoted=0;
392 	begoff = p - start;
393 	removerecordregions(begoff);
394 	if (quotes)
395 		rmescapes(p+2);
396 	result = arith(p+2);
397 	fmtstr(p, 10, "%d", result);
398 
399 	while (*p++)
400 		;
401 
402 	if (quoted == 0)
403 		recordregion(begoff, p - 1 - start, 0);
404 	result = expdest - p + 1;
405 	STADJUST(-result, expdest);
406 }
407 
408 
409 /*
410  * Expand stuff in backwards quotes.
411  */
412 
413 STATIC void
414 expbackq(cmd, quoted, flag)
415 	union node *cmd;
416 	int quoted;
417 	int flag;
418 {
419 	struct backcmd in;
420 	int i;
421 	char buf[128];
422 	char *p;
423 	char *dest = expdest;
424 	struct ifsregion saveifs, *savelastp;
425 	struct nodelist *saveargbackq;
426 	char lastc;
427 	int startloc = dest - stackblock();
428 	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
429 	int saveherefd;
430 	int quotes = flag & (EXP_FULL | EXP_CASE);
431 
432 	INTOFF;
433 	saveifs = ifsfirst;
434 	savelastp = ifslastp;
435 	saveargbackq = argbackq;
436 	saveherefd = herefd;
437 	herefd = -1;
438 	p = grabstackstr(dest);
439 	evalbackcmd(cmd, &in);
440 	ungrabstackstr(p, dest);
441 	ifsfirst = saveifs;
442 	ifslastp = savelastp;
443 	argbackq = saveargbackq;
444 	herefd = saveherefd;
445 
446 	p = in.buf;
447 	lastc = '\0';
448 	for (;;) {
449 		if (--in.nleft < 0) {
450 			if (in.fd < 0)
451 				break;
452 			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
453 			TRACE(("expbackq: read returns %d\n", i));
454 			if (i <= 0)
455 				break;
456 			p = buf;
457 			in.nleft = i - 1;
458 		}
459 		lastc = *p++;
460 		if (lastc != '\0') {
461 			if (quotes && syntax[(int)lastc] == CCTL)
462 				STPUTC(CTLESC, dest);
463 			STPUTC(lastc, dest);
464 		}
465 	}
466 
467 	/* Eat all trailing newlines */
468 	for (p--; lastc == '\n'; lastc = *--p)
469 		STUNPUTC(dest);
470 
471 	if (in.fd >= 0)
472 		close(in.fd);
473 	if (in.buf)
474 		ckfree(in.buf);
475 	if (in.jp)
476 		exitstatus = waitforjob(in.jp);
477 	if (quoted == 0)
478 		recordregion(startloc, dest - stackblock(), 0);
479 	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
480 		(dest - stackblock()) - startloc,
481 		(dest - stackblock()) - startloc,
482 		stackblock() + startloc));
483 	expdest = dest;
484 	INTON;
485 }
486 
487 
488 
489 STATIC int
490 subevalvar(p, str, strloc, subtype, startloc, varflags)
491 	char *p;
492 	char *str;
493 	int strloc;
494 	int subtype;
495 	int startloc;
496 	int varflags;
497 {
498 	char *startp;
499 	char *loc = NULL;
500 	int c = 0;
501 	int saveherefd = herefd;
502 	struct nodelist *saveargbackq = argbackq;
503 	int amount;
504 
505 	herefd = -1;
506 	argstr(p, 0);
507 	STACKSTRNUL(expdest);
508 	herefd = saveherefd;
509 	argbackq = saveargbackq;
510 	startp = stackblock() + startloc;
511 	if (str == NULL)
512 	    str = stackblock() + strloc;
513 
514 	switch (subtype) {
515 	case VSASSIGN:
516 		setvar(str, startp, 0);
517 		amount = startp - expdest;
518 		STADJUST(amount, expdest);
519 		varflags &= ~VSNUL;
520 		if (c != 0)
521 			*loc = c;
522 		return 1;
523 
524 	case VSQUESTION:
525 		if (*p != CTLENDVAR) {
526 			outfmt(&errout, "%s\n", startp);
527 			error((char *)NULL);
528 		}
529 		error("%.*s: parameter %snot set", p - str - 1,
530 		      str, (varflags & VSNUL) ? "null or "
531 					      : nullstr);
532 		/* NOTREACHED */
533 
534 	case VSTRIMLEFT:
535 		for (loc = startp; loc < str; loc++) {
536 			c = *loc;
537 			*loc = '\0';
538 			if (patmatch(str, startp))
539 				goto recordleft;
540 			*loc = c;
541 		}
542 		return 0;
543 
544 	case VSTRIMLEFTMAX:
545 		for (loc = str - 1; loc >= startp; loc--) {
546 			c = *loc;
547 			*loc = '\0';
548 			if (patmatch(str, startp))
549 				goto recordleft;
550 			*loc = c;
551 		}
552 		return 0;
553 
554 	case VSTRIMRIGHT:
555 		for (loc = str - 1; loc >= startp; loc--)
556 			if (patmatch(str, loc))
557 				goto recordright;
558 		return 0;
559 
560 	case VSTRIMRIGHTMAX:
561 		for (loc = startp; loc < str - 1; loc++)
562 			if (patmatch(str, loc))
563 				goto recordright;
564 		return 0;
565 
566 	default:
567 		abort();
568 	}
569 
570 recordleft:
571 	*loc = c;
572 	amount = ((str - 1) - (loc - startp)) - expdest;
573 	STADJUST(amount, expdest);
574 	while (loc != str - 1)
575 		*startp++ = *loc++;
576 	return 1;
577 
578 recordright:
579 	amount = loc - expdest;
580 	STADJUST(amount, expdest);
581 	STPUTC('\0', expdest);
582 	STADJUST(-1, expdest);
583 	return 1;
584 }
585 
586 
587 /*
588  * Expand a variable, and return a pointer to the next character in the
589  * input string.
590  */
591 
592 STATIC char *
593 evalvar(p, flag)
594 	char *p;
595 	int flag;
596 {
597 	int subtype;
598 	int varflags;
599 	char *var;
600 	char *val;
601 	char *pat;
602 	int c;
603 	int set;
604 	int special;
605 	int startloc;
606 	int varlen;
607 	int easy;
608 	int quotes = flag & (EXP_FULL | EXP_CASE);
609 
610 	varflags = *p++;
611 	subtype = varflags & VSTYPE;
612 	var = p;
613 	special = 0;
614 	if (! is_name(*p))
615 		special = 1;
616 	p = strchr(p, '=') + 1;
617 again: /* jump here after setting a variable with ${var=text} */
618 	if (special) {
619 		set = varisset(var, varflags & VSNUL);
620 		val = NULL;
621 	} else {
622 		val = lookupvar(var);
623 		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
624 			val = NULL;
625 			set = 0;
626 		} else
627 			set = 1;
628 	}
629 	varlen = 0;
630 	startloc = expdest - stackblock();
631 	if (set && subtype != VSPLUS) {
632 		/* insert the value of the variable */
633 		if (special) {
634 			varvalue(var, varflags & VSQUOTE, flag & EXP_FULL);
635 			if (subtype == VSLENGTH) {
636 				varlen = expdest - stackblock() - startloc;
637 				STADJUST(-varlen, expdest);
638 			}
639 		} else {
640 			char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
641 								  : BASESYNTAX;
642 
643 			if (subtype == VSLENGTH) {
644 				for (;*val; val++)
645 					varlen++;
646 			}
647 			else {
648 				while (*val) {
649 					if (quotes && syntax[(int)*val] == CCTL)
650 						STPUTC(CTLESC, expdest);
651 					STPUTC(*val++, expdest);
652 				}
653 
654 			}
655 		}
656 	}
657 
658 	if (subtype == VSPLUS)
659 		set = ! set;
660 
661 	easy = ((varflags & VSQUOTE) == 0 ||
662 		(*var == '@' && shellparam.nparam != 1));
663 
664 
665 	switch (subtype) {
666 	case VSLENGTH:
667 		expdest = cvtnum(varlen, expdest);
668 		goto record;
669 
670 	case VSNORMAL:
671 		if (!easy)
672 			break;
673 record:
674 		recordregion(startloc, expdest - stackblock(),
675 			     varflags & VSQUOTE);
676 		break;
677 
678 	case VSPLUS:
679 	case VSMINUS:
680 		if (!set) {
681 		        argstr(p, flag);
682 			break;
683 		}
684 		if (easy)
685 			goto record;
686 		break;
687 
688 	case VSTRIMLEFT:
689 	case VSTRIMLEFTMAX:
690 	case VSTRIMRIGHT:
691 	case VSTRIMRIGHTMAX:
692 		if (!set)
693 			break;
694 		/*
695 		 * Terminate the string and start recording the pattern
696 		 * right after it
697 		 */
698 		STPUTC('\0', expdest);
699 		pat = expdest;
700 		if (subevalvar(p, NULL, expdest - stackblock(), subtype,
701 			       startloc, varflags) == 0) {
702 			int amount = (expdest - pat) + 1;
703 			STADJUST(-amount, expdest);
704 		}
705 		/* Remove any recorded regions beyond start of variable */
706 		removerecordregions(startloc);
707 		goto record;
708 
709 	case VSASSIGN:
710 	case VSQUESTION:
711 		if (!set) {
712 			if (subevalvar(p, var, 0, subtype, startloc,
713 				       varflags)) {
714 				varflags &= ~VSNUL;
715 				/*
716 				 * Remove any recorded regions beyond
717 				 * start of variable
718 				 */
719 				removerecordregions(startloc);
720 				goto again;
721 			}
722 			break;
723 		}
724 		if (easy)
725 			goto record;
726 		break;
727 
728 	default:
729 		abort();
730 	}
731 
732 	if (subtype != VSNORMAL) {	/* skip to end of alternative */
733 		int nesting = 1;
734 		for (;;) {
735 			if ((c = *p++) == CTLESC)
736 				p++;
737 			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
738 				if (set)
739 					argbackq = argbackq->next;
740 			} else if (c == CTLVAR) {
741 				if ((*p++ & VSTYPE) != VSNORMAL)
742 					nesting++;
743 			} else if (c == CTLENDVAR) {
744 				if (--nesting == 0)
745 					break;
746 			}
747 		}
748 	}
749 	return p;
750 }
751 
752 
753 
754 /*
755  * Test whether a specialized variable is set.
756  */
757 
758 STATIC int
759 varisset(name, nulok)
760 	char *name;
761 	int nulok;
762 {
763 	if (*name == '!')
764 		return backgndpid != -1;
765 	else if (*name == '@' || *name == '*') {
766 		if (*shellparam.p == NULL)
767 			return 0;
768 
769 		if (nulok) {
770 			char **av;
771 
772 			for (av = shellparam.p; *av; av++)
773 				if (**av != '\0')
774 					return 1;
775 			return 0;
776 		}
777 	} else if (is_digit(*name)) {
778 		char *ap;
779 		int num = atoi(name);
780 
781 		if (num > shellparam.nparam)
782 			return 0;
783 
784 		if (num == 0)
785 			ap = arg0;
786 		else
787 			ap = shellparam.p[num - 1];
788 
789 		if (nulok && (ap == NULL || *ap == '\0'))
790 			return 0;
791 	}
792 	return 1;
793 }
794 
795 
796 
797 /*
798  * Add the value of a specialized variable to the stack string.
799  */
800 
801 STATIC void
802 varvalue(name, quoted, allow_split)
803 	char *name;
804 	int quoted;
805 	int allow_split;
806 {
807 	int num;
808 	char *p;
809 	int i;
810 	extern int oexitstatus;
811 	char sep;
812 	char **ap;
813 	char const *syntax;
814 
815 #define STRTODEST(p) \
816 	do {\
817 	if (allow_split) { \
818 		syntax = quoted? DQSYNTAX : BASESYNTAX; \
819 		while (*p) { \
820 			if (syntax[(int)*p] == CCTL) \
821 				STPUTC(CTLESC, expdest); \
822 			STPUTC(*p++, expdest); \
823 		} \
824 	} else \
825 		while (*p) \
826 			STPUTC(*p++, expdest); \
827 	} while (0)
828 
829 
830 	switch (*name) {
831 	case '$':
832 		num = rootpid;
833 		goto numvar;
834 	case '?':
835 		num = oexitstatus;
836 		goto numvar;
837 	case '#':
838 		num = shellparam.nparam;
839 		goto numvar;
840 	case '!':
841 		num = backgndpid;
842 numvar:
843 		expdest = cvtnum(num, expdest);
844 		break;
845 	case '-':
846 		for (i = 0 ; i < NOPTS ; i++) {
847 			if (optlist[i].val)
848 				STPUTC(optlist[i].letter, expdest);
849 		}
850 		break;
851 	case '@':
852 		if (allow_split && quoted) {
853 			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
854 				STRTODEST(p);
855 				if (*ap)
856 					STPUTC('\0', expdest);
857 			}
858 			break;
859 		}
860 		/* fall through */
861 	case '*':
862 		if (ifsset() != 0)
863 			sep = ifsval()[0];
864 		else
865 			sep = ' ';
866 		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
867 			STRTODEST(p);
868 			if (*ap && sep)
869 				STPUTC(sep, expdest);
870 		}
871 		break;
872 	case '0':
873 		p = arg0;
874 		STRTODEST(p);
875 		break;
876 	default:
877 		if (is_digit(*name)) {
878 			num = atoi(name);
879 			if (num > 0 && num <= shellparam.nparam) {
880 				p = shellparam.p[num - 1];
881 				STRTODEST(p);
882 			}
883 		}
884 		break;
885 	}
886 }
887 
888 
889 
890 /*
891  * Record the the fact that we have to scan this region of the
892  * string for IFS characters.
893  */
894 
895 STATIC void
896 recordregion(start, end, nulonly)
897 	int start;
898 	int end;
899 	int nulonly;
900 {
901 	struct ifsregion *ifsp;
902 
903 	if (ifslastp == NULL) {
904 		ifsp = &ifsfirst;
905 	} else {
906 		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
907 		ifslastp->next = ifsp;
908 	}
909 	ifslastp = ifsp;
910 	ifslastp->next = NULL;
911 	ifslastp->begoff = start;
912 	ifslastp->endoff = end;
913 	ifslastp->nulonly = nulonly;
914 }
915 
916 
917 
918 /*
919  * Break the argument string into pieces based upon IFS and add the
920  * strings to the argument list.  The regions of the string to be
921  * searched for IFS characters have been stored by recordregion.
922  */
923 STATIC void
924 ifsbreakup(string, arglist)
925 	char *string;
926 	struct arglist *arglist;
927 	{
928 	struct ifsregion *ifsp;
929 	struct strlist *sp;
930 	char *start;
931 	char *p;
932 	char *q;
933 	char *ifs;
934 	int ifsspc;
935 	int nulonly;
936 
937 
938 	start = string;
939 	ifsspc = 0;
940 	nulonly = 0;
941 	if (ifslastp != NULL) {
942 		ifsp = &ifsfirst;
943 		do {
944 			p = string + ifsp->begoff;
945 			nulonly = ifsp->nulonly;
946 			ifs = nulonly ? nullstr :
947 				( ifsset() ? ifsval() : " \t\n" );
948 			ifsspc = 0;
949 			while (p < string + ifsp->endoff) {
950 				q = p;
951 				if (*p == CTLESC)
952 					p++;
953 				if (strchr(ifs, *p)) {
954 					if (!nulonly)
955 						ifsspc = (strchr(" \t\n", *p) != NULL);
956 					/* Ignore IFS whitespace at start */
957 					if (q == start && ifsspc) {
958 						p++;
959 						start = p;
960 						continue;
961 					}
962 					*q = '\0';
963 					sp = (struct strlist *)stalloc(sizeof *sp);
964 					sp->text = start;
965 					*arglist->lastp = sp;
966 					arglist->lastp = &sp->next;
967 					p++;
968 					if (!nulonly) {
969 						for (;;) {
970 							if (p >= string + ifsp->endoff) {
971 								break;
972 							}
973 							q = p;
974 							if (*p == CTLESC)
975 								p++;
976 							if (strchr(ifs, *p) == NULL ) {
977 								p = q;
978 								break;
979 							} else if (strchr(" \t\n",*p) == NULL) {
980 								if (ifsspc) {
981 									p++;
982 									ifsspc = 0;
983 								} else {
984 									p = q;
985 									break;
986 								}
987 							} else
988 								p++;
989 						}
990 					}
991 					start = p;
992 				} else
993 					p++;
994 			}
995 		} while ((ifsp = ifsp->next) != NULL);
996 		if (*start || (!ifsspc && start > string &&
997 			(nulonly || 1))) {
998 			sp = (struct strlist *)stalloc(sizeof *sp);
999 			sp->text = start;
1000 			*arglist->lastp = sp;
1001 			arglist->lastp = &sp->next;
1002 		}
1003 	} else {
1004 		sp = (struct strlist *)stalloc(sizeof *sp);
1005 		sp->text = start;
1006 		*arglist->lastp = sp;
1007 		arglist->lastp = &sp->next;
1008 	}
1009 }
1010 
1011 STATIC void
1012 ifsfree()
1013 {
1014 	while (ifsfirst.next != NULL) {
1015 		struct ifsregion *ifsp;
1016 		INTOFF;
1017 		ifsp = ifsfirst.next->next;
1018 		ckfree(ifsfirst.next);
1019 		ifsfirst.next = ifsp;
1020 		INTON;
1021 	}
1022 	ifslastp = NULL;
1023 	ifsfirst.next = NULL;
1024 }
1025 
1026 
1027 
1028 /*
1029  * Expand shell metacharacters.  At this point, the only control characters
1030  * should be escapes.  The results are stored in the list exparg.
1031  */
1032 
1033 char *expdir;
1034 
1035 
1036 STATIC void
1037 expandmeta(str, flag)
1038 	struct strlist *str;
1039 	int flag;
1040 {
1041 	char *p;
1042 	struct strlist **savelastp;
1043 	struct strlist *sp;
1044 	char c;
1045 	/* TODO - EXP_REDIR */
1046 
1047 	while (str) {
1048 		if (fflag)
1049 			goto nometa;
1050 		p = str->text;
1051 		for (;;) {			/* fast check for meta chars */
1052 			if ((c = *p++) == '\0')
1053 				goto nometa;
1054 			if (c == '*' || c == '?' || c == '[' || c == '!')
1055 				break;
1056 		}
1057 		savelastp = exparg.lastp;
1058 		INTOFF;
1059 		if (expdir == NULL) {
1060 			int i = strlen(str->text);
1061 			expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
1062 		}
1063 
1064 		expmeta(expdir, str->text);
1065 		ckfree(expdir);
1066 		expdir = NULL;
1067 		INTON;
1068 		if (exparg.lastp == savelastp) {
1069 			/*
1070 			 * no matches
1071 			 */
1072 nometa:
1073 			*exparg.lastp = str;
1074 			rmescapes(str->text);
1075 			exparg.lastp = &str->next;
1076 		} else {
1077 			*exparg.lastp = NULL;
1078 			*savelastp = sp = expsort(*savelastp);
1079 			while (sp->next != NULL)
1080 				sp = sp->next;
1081 			exparg.lastp = &sp->next;
1082 		}
1083 		str = str->next;
1084 	}
1085 }
1086 
1087 
1088 /*
1089  * Do metacharacter (i.e. *, ?, [...]) expansion.
1090  */
1091 
1092 STATIC void
1093 expmeta(enddir, name)
1094 	char *enddir;
1095 	char *name;
1096 	{
1097 	char *p;
1098 	char *q;
1099 	char *start;
1100 	char *endname;
1101 	int metaflag;
1102 	struct stat statb;
1103 	DIR *dirp;
1104 	struct dirent *dp;
1105 	int atend;
1106 	int matchdot;
1107 
1108 	metaflag = 0;
1109 	start = name;
1110 	for (p = name ; ; p++) {
1111 		if (*p == '*' || *p == '?')
1112 			metaflag = 1;
1113 		else if (*p == '[') {
1114 			q = p + 1;
1115 			if (*q == '!')
1116 				q++;
1117 			for (;;) {
1118 				while (*q == CTLQUOTEMARK)
1119 					q++;
1120 				if (*q == CTLESC)
1121 					q++;
1122 				if (*q == '/' || *q == '\0')
1123 					break;
1124 				if (*++q == ']') {
1125 					metaflag = 1;
1126 					break;
1127 				}
1128 			}
1129 		} else if (*p == '!' && p[1] == '!'	&& (p == name || p[-1] == '/')) {
1130 			metaflag = 1;
1131 		} else if (*p == '\0')
1132 			break;
1133 		else if (*p == CTLQUOTEMARK)
1134 			continue;
1135 		else if (*p == CTLESC)
1136 			p++;
1137 		if (*p == '/') {
1138 			if (metaflag)
1139 				break;
1140 			start = p + 1;
1141 		}
1142 	}
1143 	if (metaflag == 0) {	/* we've reached the end of the file name */
1144 		if (enddir != expdir)
1145 			metaflag++;
1146 		for (p = name ; ; p++) {
1147 			if (*p == CTLQUOTEMARK)
1148 				continue;
1149 			if (*p == CTLESC)
1150 				p++;
1151 			*enddir++ = *p;
1152 			if (*p == '\0')
1153 				break;
1154 		}
1155 		if (metaflag == 0 || stat(expdir, &statb) >= 0)
1156 			addfname(expdir);
1157 		return;
1158 	}
1159 	endname = p;
1160 	if (start != name) {
1161 		p = name;
1162 		while (p < start) {
1163 			while (*p == CTLQUOTEMARK)
1164 				p++;
1165 			if (*p == CTLESC)
1166 				p++;
1167 			*enddir++ = *p++;
1168 		}
1169 	}
1170 	if (enddir == expdir) {
1171 		p = ".";
1172 	} else if (enddir == expdir + 1 && *expdir == '/') {
1173 		p = "/";
1174 	} else {
1175 		p = expdir;
1176 		enddir[-1] = '\0';
1177 	}
1178 	if ((dirp = opendir(p)) == NULL)
1179 		return;
1180 	if (enddir != expdir)
1181 		enddir[-1] = '/';
1182 	if (*endname == 0) {
1183 		atend = 1;
1184 	} else {
1185 		atend = 0;
1186 		*endname++ = '\0';
1187 	}
1188 	matchdot = 0;
1189 	p = start;
1190 	while (*p == CTLQUOTEMARK)
1191 		p++;
1192 	if (*p == CTLESC)
1193 		p++;
1194 	if (*p == '.')
1195 		matchdot++;
1196 	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
1197 		if (dp->d_name[0] == '.' && ! matchdot)
1198 			continue;
1199 		if (patmatch(start, dp->d_name)) {
1200 			if (atend) {
1201 				scopy(dp->d_name, enddir);
1202 				addfname(expdir);
1203 			} else {
1204 				char *q;
1205 				for (p = enddir, q = dp->d_name;
1206 				     (*p++ = *q++) != '\0';)
1207 					continue;
1208 				p[-1] = '/';
1209 				expmeta(p, endname);
1210 			}
1211 		}
1212 	}
1213 	closedir(dirp);
1214 	if (! atend)
1215 		endname[-1] = '/';
1216 }
1217 
1218 
1219 /*
1220  * Add a file name to the list.
1221  */
1222 
1223 STATIC void
1224 addfname(name)
1225 	char *name;
1226 	{
1227 	char *p;
1228 	struct strlist *sp;
1229 
1230 	p = stalloc(strlen(name) + 1);
1231 	scopy(name, p);
1232 	sp = (struct strlist *)stalloc(sizeof *sp);
1233 	sp->text = p;
1234 	*exparg.lastp = sp;
1235 	exparg.lastp = &sp->next;
1236 }
1237 
1238 
1239 /*
1240  * Sort the results of file name expansion.  It calculates the number of
1241  * strings to sort and then calls msort (short for merge sort) to do the
1242  * work.
1243  */
1244 
1245 STATIC struct strlist *
1246 expsort(str)
1247 	struct strlist *str;
1248 	{
1249 	int len;
1250 	struct strlist *sp;
1251 
1252 	len = 0;
1253 	for (sp = str ; sp ; sp = sp->next)
1254 		len++;
1255 	return msort(str, len);
1256 }
1257 
1258 
1259 STATIC struct strlist *
1260 msort(list, len)
1261 	struct strlist *list;
1262 	int len;
1263 {
1264 	struct strlist *p, *q = NULL;
1265 	struct strlist **lpp;
1266 	int half;
1267 	int n;
1268 
1269 	if (len <= 1)
1270 		return list;
1271 	half = len >> 1;
1272 	p = list;
1273 	for (n = half ; --n >= 0 ; ) {
1274 		q = p;
1275 		p = p->next;
1276 	}
1277 	q->next = NULL;			/* terminate first half of list */
1278 	q = msort(list, half);		/* sort first half of list */
1279 	p = msort(p, len - half);		/* sort second half */
1280 	lpp = &list;
1281 	for (;;) {
1282 		if (strcmp(p->text, q->text) < 0) {
1283 			*lpp = p;
1284 			lpp = &p->next;
1285 			if ((p = *lpp) == NULL) {
1286 				*lpp = q;
1287 				break;
1288 			}
1289 		} else {
1290 			*lpp = q;
1291 			lpp = &q->next;
1292 			if ((q = *lpp) == NULL) {
1293 				*lpp = p;
1294 				break;
1295 			}
1296 		}
1297 	}
1298 	return list;
1299 }
1300 
1301 
1302 
1303 /*
1304  * Returns true if the pattern matches the string.
1305  */
1306 
1307 int
1308 patmatch(pattern, string)
1309 	char *pattern;
1310 	char *string;
1311 	{
1312 #ifdef notdef
1313 	if (pattern[0] == '!' && pattern[1] == '!')
1314 		return 1 - pmatch(pattern + 2, string);
1315 	else
1316 #endif
1317 		return pmatch(pattern, string);
1318 }
1319 
1320 
1321 STATIC int
1322 pmatch(pattern, string)
1323 	char *pattern;
1324 	char *string;
1325 	{
1326 	char *p, *q;
1327 	char c;
1328 
1329 	p = pattern;
1330 	q = string;
1331 	for (;;) {
1332 		switch (c = *p++) {
1333 		case '\0':
1334 			goto breakloop;
1335 		case CTLESC:
1336 			if (*q++ != *p++)
1337 				return 0;
1338 			break;
1339 		case CTLQUOTEMARK:
1340 			continue;
1341 		case '?':
1342 			if (*q++ == '\0')
1343 				return 0;
1344 			break;
1345 		case '*':
1346 			c = *p;
1347 			while (c == CTLQUOTEMARK || c == '*')
1348 				c = *++p;
1349 			if (c != CTLESC &&  c != CTLQUOTEMARK &&
1350 			    c != '?' && c != '*' && c != '[') {
1351 				while (*q != c) {
1352 					if (*q == '\0')
1353 						return 0;
1354 					q++;
1355 				}
1356 			}
1357 			do {
1358 				if (pmatch(p, q))
1359 					return 1;
1360 			} while (*q++ != '\0');
1361 			return 0;
1362 		case '[': {
1363 			char *endp;
1364 			int invert, found;
1365 			char chr;
1366 
1367 			endp = p;
1368 			if (*endp == '!')
1369 				endp++;
1370 			for (;;) {
1371 				while (*endp == CTLQUOTEMARK)
1372 					endp++;
1373 				if (*endp == '\0')
1374 					goto dft;		/* no matching ] */
1375 				if (*endp == CTLESC)
1376 					endp++;
1377 				if (*++endp == ']')
1378 					break;
1379 			}
1380 			invert = 0;
1381 			if (*p == '!') {
1382 				invert++;
1383 				p++;
1384 			}
1385 			found = 0;
1386 			chr = *q++;
1387 			if (chr == '\0')
1388 				return 0;
1389 			c = *p++;
1390 			do {
1391 				if (c == CTLQUOTEMARK)
1392 					continue;
1393 				if (c == CTLESC)
1394 					c = *p++;
1395 				if (*p == '-' && p[1] != ']') {
1396 					p++;
1397 					while (*p == CTLQUOTEMARK)
1398 						p++;
1399 					if (*p == CTLESC)
1400 						p++;
1401 					if (chr >= c && chr <= *p)
1402 						found = 1;
1403 					p++;
1404 				} else {
1405 					if (chr == c)
1406 						found = 1;
1407 				}
1408 			} while ((c = *p++) != ']');
1409 			if (found == invert)
1410 				return 0;
1411 			break;
1412 		}
1413 dft:	        default:
1414 			if (*q++ != c)
1415 				return 0;
1416 			break;
1417 		}
1418 	}
1419 breakloop:
1420 	if (*q != '\0')
1421 		return 0;
1422 	return 1;
1423 }
1424 
1425 
1426 
1427 /*
1428  * Remove any CTLESC characters from a string.
1429  */
1430 
1431 void
1432 rmescapes(str)
1433 	char *str;
1434 {
1435 	char *p, *q;
1436 
1437 	p = str;
1438 	while (*p != CTLESC && *p != CTLQUOTEMARK) {
1439 		if (*p++ == '\0')
1440 			return;
1441 	}
1442 	q = p;
1443 	while (*p) {
1444 		if (*p == CTLQUOTEMARK) {
1445 			p++;
1446 			continue;
1447 		}
1448 		if (*p == CTLESC)
1449 			p++;
1450 		*q++ = *p++;
1451 	}
1452 	*q = '\0';
1453 }
1454 
1455 
1456 
1457 /*
1458  * See if a pattern matches in a case statement.
1459  */
1460 
1461 int
1462 casematch(pattern, val)
1463 	union node *pattern;
1464 	char *val;
1465 	{
1466 	struct stackmark smark;
1467 	int result;
1468 	char *p;
1469 
1470 	setstackmark(&smark);
1471 	argbackq = pattern->narg.backquote;
1472 	STARTSTACKSTR(expdest);
1473 	ifslastp = NULL;
1474 	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
1475 	STPUTC('\0', expdest);
1476 	p = grabstackstr(expdest);
1477 	result = patmatch(p, val);
1478 	popstackmark(&smark);
1479 	return result;
1480 }
1481 
1482 /*
1483  * Our own itoa().
1484  */
1485 
1486 STATIC char *
1487 cvtnum(num, buf)
1488 	int num;
1489 	char *buf;
1490 	{
1491 	char temp[32];
1492 	int neg = num < 0;
1493 	char *p = temp + 31;
1494 
1495 	temp[31] = '\0';
1496 
1497 	do {
1498 		*--p = num % 10 + '0';
1499 	} while ((num /= 10) != 0);
1500 
1501 	if (neg)
1502 		*--p = '-';
1503 
1504 	while (*p)
1505 		STPUTC(*p++, buf);
1506 	return buf;
1507 }
1508