expand.c revision 96939
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Kenneth Almquist.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 3. All advertising materials mentioning features or use of this software
171556Srgrimes *    must display the following acknowledgement:
181556Srgrimes *	This product includes software developed by the University of
191556Srgrimes *	California, Berkeley and its contributors.
201556Srgrimes * 4. Neither the name of the University nor the names of its contributors
211556Srgrimes *    may be used to endorse or promote products derived from this software
221556Srgrimes *    without specific prior written permission.
231556Srgrimes *
241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341556Srgrimes * SUCH DAMAGE.
351556Srgrimes */
361556Srgrimes
371556Srgrimes#ifndef lint
3836150Scharnier#if 0
3936150Scharnierstatic char sccsid[] = "@(#)expand.c	8.5 (Berkeley) 5/15/95";
4036150Scharnier#endif
4136150Scharnierstatic const char rcsid[] =
4250471Speter  "$FreeBSD: head/bin/sh/expand.c 96939 2002-05-19 08:30:16Z tjr $";
431556Srgrimes#endif /* not lint */
441556Srgrimes
4517987Speter#include <sys/types.h>
4617987Speter#include <sys/time.h>
4717987Speter#include <sys/stat.h>
4817987Speter#include <errno.h>
4917987Speter#include <dirent.h>
5017987Speter#include <unistd.h>
5117987Speter#include <pwd.h>
5217987Speter#include <stdlib.h>
5319281Sache#include <limits.h>
5438887Stegge#include <stdio.h>
5517987Speter
561556Srgrimes/*
571556Srgrimes * Routines to expand arguments to commands.  We have to deal with
581556Srgrimes * backquotes, shell variables, and file metacharacters.
591556Srgrimes */
601556Srgrimes
611556Srgrimes#include "shell.h"
621556Srgrimes#include "main.h"
631556Srgrimes#include "nodes.h"
641556Srgrimes#include "eval.h"
651556Srgrimes#include "expand.h"
661556Srgrimes#include "syntax.h"
671556Srgrimes#include "parser.h"
681556Srgrimes#include "jobs.h"
691556Srgrimes#include "options.h"
701556Srgrimes#include "var.h"
711556Srgrimes#include "input.h"
721556Srgrimes#include "output.h"
731556Srgrimes#include "memalloc.h"
741556Srgrimes#include "error.h"
751556Srgrimes#include "mystring.h"
7617987Speter#include "arith.h"
7717987Speter#include "show.h"
781556Srgrimes
791556Srgrimes/*
801556Srgrimes * Structure specifying which parts of the string should be searched
811556Srgrimes * for IFS characters.
821556Srgrimes */
831556Srgrimes
841556Srgrimesstruct ifsregion {
851556Srgrimes	struct ifsregion *next;	/* next region in list */
861556Srgrimes	int begoff;		/* offset of start of region */
871556Srgrimes	int endoff;		/* offset of end of region */
881556Srgrimes	int nulonly;		/* search for nul bytes only */
891556Srgrimes};
901556Srgrimes
911556Srgrimes
921556Srgrimeschar *expdest;			/* output of current string */
931556Srgrimesstruct nodelist *argbackq;	/* list of back quote expressions */
941556Srgrimesstruct ifsregion ifsfirst;	/* first struct in list of ifs regions */
951556Srgrimesstruct ifsregion *ifslastp;	/* last struct in list */
961556Srgrimesstruct arglist exparg;		/* holds expanded arg list */
971556Srgrimes
9890111SimpSTATIC void argstr(char *, int);
9990111SimpSTATIC char *exptilde(char *, int);
10090111SimpSTATIC void expbackq(union node *, int, int);
10190111SimpSTATIC int subevalvar(char *, char *, int, int, int, int);
10290111SimpSTATIC char *evalvar(char *, int);
10390111SimpSTATIC int varisset(char *, int);
10490111SimpSTATIC void varvalue(char *, int, int);
10590111SimpSTATIC void recordregion(int, int, int);
10690111SimpSTATIC void removerecordregions(int);
10790111SimpSTATIC void ifsbreakup(char *, struct arglist *);
10890111SimpSTATIC void expandmeta(struct strlist *, int);
10990111SimpSTATIC void expmeta(char *, char *);
11090111SimpSTATIC void addfname(char *);
11190111SimpSTATIC struct strlist *expsort(struct strlist *);
11290111SimpSTATIC struct strlist *msort(struct strlist *, int);
11390111SimpSTATIC int pmatch(char *, char *, int);
11490111SimpSTATIC char *cvtnum(int, char *);
11590111SimpSTATIC int collate_range_cmp(int, int);
1161556Srgrimes
11790111SimpSTATIC int
11890111Simpcollate_range_cmp (int c1, int c2)
11919281Sache{
12019281Sache	static char s1[2], s2[2];
12119281Sache	int ret;
12219281Sache
12319281Sache	c1 &= UCHAR_MAX;
12419281Sache	c2 &= UCHAR_MAX;
12519281Sache	if (c1 == c2)
12619281Sache		return (0);
12719281Sache	s1[0] = c1;
12819281Sache	s2[0] = c2;
12919281Sache	if ((ret = strcoll(s1, s2)) != 0)
13019281Sache		return (ret);
13119281Sache	return (c1 - c2);
13219281Sache}
13319281Sache
1341556Srgrimes/*
1351556Srgrimes * Expand shell variables and backquotes inside a here document.
13690111Simp *	union node *arg		the document
13790111Simp *	int fd;			where to write the expanded version
1381556Srgrimes */
1391556Srgrimes
1401556Srgrimesvoid
14190111Simpexpandhere(union node *arg, int fd)
14290111Simp{
1431556Srgrimes	herefd = fd;
1441556Srgrimes	expandarg(arg, (struct arglist *)NULL, 0);
14539137Stegge	xwrite(fd, stackblock(), expdest - stackblock());
1461556Srgrimes}
1471556Srgrimes
1481556Srgrimes
1491556Srgrimes/*
1501556Srgrimes * Perform variable substitution and command substitution on an argument,
1511556Srgrimes * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
1521556Srgrimes * perform splitting and file name expansion.  When arglist is NULL, perform
1531556Srgrimes * here document expansion.
1541556Srgrimes */
1551556Srgrimes
1561556Srgrimesvoid
15790111Simpexpandarg(union node *arg, struct arglist *arglist, int flag)
15817987Speter{
1591556Srgrimes	struct strlist *sp;
1601556Srgrimes	char *p;
1611556Srgrimes
1621556Srgrimes	argbackq = arg->narg.backquote;
1631556Srgrimes	STARTSTACKSTR(expdest);
1641556Srgrimes	ifsfirst.next = NULL;
1651556Srgrimes	ifslastp = NULL;
1661556Srgrimes	argstr(arg->narg.text, flag);
1671556Srgrimes	if (arglist == NULL) {
1681556Srgrimes		return;			/* here document expanded */
1691556Srgrimes	}
1701556Srgrimes	STPUTC('\0', expdest);
1711556Srgrimes	p = grabstackstr(expdest);
1721556Srgrimes	exparg.lastp = &exparg.list;
1731556Srgrimes	/*
1741556Srgrimes	 * TODO - EXP_REDIR
1751556Srgrimes	 */
1761556Srgrimes	if (flag & EXP_FULL) {
1771556Srgrimes		ifsbreakup(p, &exparg);
1781556Srgrimes		*exparg.lastp = NULL;
1791556Srgrimes		exparg.lastp = &exparg.list;
1801556Srgrimes		expandmeta(exparg.list, flag);
1811556Srgrimes	} else {
1821556Srgrimes		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
1831556Srgrimes			rmescapes(p);
1841556Srgrimes		sp = (struct strlist *)stalloc(sizeof (struct strlist));
1851556Srgrimes		sp->text = p;
1861556Srgrimes		*exparg.lastp = sp;
1871556Srgrimes		exparg.lastp = &sp->next;
1881556Srgrimes	}
1891556Srgrimes	while (ifsfirst.next != NULL) {
1901556Srgrimes		struct ifsregion *ifsp;
1911556Srgrimes		INTOFF;
1921556Srgrimes		ifsp = ifsfirst.next->next;
1931556Srgrimes		ckfree(ifsfirst.next);
1941556Srgrimes		ifsfirst.next = ifsp;
1951556Srgrimes		INTON;
1961556Srgrimes	}
1971556Srgrimes	*exparg.lastp = NULL;
1981556Srgrimes	if (exparg.list) {
1991556Srgrimes		*arglist->lastp = exparg.list;
2001556Srgrimes		arglist->lastp = exparg.lastp;
2011556Srgrimes	}
2021556Srgrimes}
2031556Srgrimes
2041556Srgrimes
2051556Srgrimes
2061556Srgrimes/*
2071556Srgrimes * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
2081556Srgrimes * characters to allow for further processing.  Otherwise treat
2091556Srgrimes * $@ like $* since no splitting will be performed.
2101556Srgrimes */
2111556Srgrimes
2121556SrgrimesSTATIC void
21390111Simpargstr(char *p, int flag)
21417987Speter{
21525233Ssteve	char c;
2161556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);	/* do CTLESC */
2171556Srgrimes	int firsteq = 1;
2181556Srgrimes
2191556Srgrimes	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
2201556Srgrimes		p = exptilde(p, flag);
2211556Srgrimes	for (;;) {
2221556Srgrimes		switch (c = *p++) {
2231556Srgrimes		case '\0':
2241556Srgrimes		case CTLENDVAR: /* ??? */
2251556Srgrimes			goto breakloop;
22638887Stegge		case CTLQUOTEMARK:
22738887Stegge			/* "$@" syntax adherence hack */
22838887Stegge			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
22938887Stegge				break;
23039137Stegge			if ((flag & EXP_FULL) != 0)
23139137Stegge				STPUTC(c, expdest);
23238887Stegge			break;
2331556Srgrimes		case CTLESC:
2341556Srgrimes			if (quotes)
2351556Srgrimes				STPUTC(c, expdest);
2361556Srgrimes			c = *p++;
2371556Srgrimes			STPUTC(c, expdest);
2381556Srgrimes			break;
2391556Srgrimes		case CTLVAR:
2401556Srgrimes			p = evalvar(p, flag);
2411556Srgrimes			break;
2421556Srgrimes		case CTLBACKQ:
2431556Srgrimes		case CTLBACKQ|CTLQUOTE:
2441556Srgrimes			expbackq(argbackq->n, c & CTLQUOTE, flag);
2451556Srgrimes			argbackq = argbackq->next;
2461556Srgrimes			break;
2471556Srgrimes		case CTLENDARI:
2481556Srgrimes			expari(flag);
2491556Srgrimes			break;
2501556Srgrimes		case ':':
2511556Srgrimes		case '=':
2521556Srgrimes			/*
2531556Srgrimes			 * sort of a hack - expand tildes in variable
2541556Srgrimes			 * assignments (after the first '=' and after ':'s).
2551556Srgrimes			 */
2561556Srgrimes			STPUTC(c, expdest);
2571556Srgrimes			if (flag & EXP_VARTILDE && *p == '~') {
2581556Srgrimes				if (c == '=') {
2591556Srgrimes					if (firsteq)
2601556Srgrimes						firsteq = 0;
2611556Srgrimes					else
2621556Srgrimes						break;
2631556Srgrimes				}
2641556Srgrimes				p = exptilde(p, flag);
2651556Srgrimes			}
2661556Srgrimes			break;
2671556Srgrimes		default:
2681556Srgrimes			STPUTC(c, expdest);
2691556Srgrimes		}
2701556Srgrimes	}
2711556Srgrimesbreakloop:;
2721556Srgrimes}
2731556Srgrimes
2741556SrgrimesSTATIC char *
27590111Simpexptilde(char *p, int flag)
27617987Speter{
2771556Srgrimes	char c, *startp = p;
2781556Srgrimes	struct passwd *pw;
2791556Srgrimes	char *home;
2801556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
2811556Srgrimes
28217987Speter	while ((c = *p) != '\0') {
2831556Srgrimes		switch(c) {
2841556Srgrimes		case CTLESC:
2851556Srgrimes			return (startp);
28639137Stegge		case CTLQUOTEMARK:
28739137Stegge			return (startp);
2881556Srgrimes		case ':':
2891556Srgrimes			if (flag & EXP_VARTILDE)
2901556Srgrimes				goto done;
2911556Srgrimes			break;
2921556Srgrimes		case '/':
2931556Srgrimes			goto done;
2941556Srgrimes		}
2951556Srgrimes		p++;
2961556Srgrimes	}
2971556Srgrimesdone:
2981556Srgrimes	*p = '\0';
2991556Srgrimes	if (*(startp+1) == '\0') {
3001556Srgrimes		if ((home = lookupvar("HOME")) == NULL)
3011556Srgrimes			goto lose;
3021556Srgrimes	} else {
3031556Srgrimes		if ((pw = getpwnam(startp+1)) == NULL)
3041556Srgrimes			goto lose;
3051556Srgrimes		home = pw->pw_dir;
3061556Srgrimes	}
3071556Srgrimes	if (*home == '\0')
3081556Srgrimes		goto lose;
3091556Srgrimes	*p = c;
31017987Speter	while ((c = *home++) != '\0') {
31183675Stegge		if (quotes && SQSYNTAX[(int)c] == CCTL)
3121556Srgrimes			STPUTC(CTLESC, expdest);
3131556Srgrimes		STPUTC(c, expdest);
3141556Srgrimes	}
3151556Srgrimes	return (p);
3161556Srgrimeslose:
3171556Srgrimes	*p = c;
3181556Srgrimes	return (startp);
3191556Srgrimes}
3201556Srgrimes
3211556Srgrimes
32238887SteggeSTATIC void
32390111Simpremoverecordregions(int endoff)
32438887Stegge{
32538887Stegge	if (ifslastp == NULL)
32638887Stegge		return;
32738887Stegge
32838887Stegge	if (ifsfirst.endoff > endoff) {
32938887Stegge		while (ifsfirst.next != NULL) {
33038887Stegge			struct ifsregion *ifsp;
33138887Stegge			INTOFF;
33238887Stegge			ifsp = ifsfirst.next->next;
33338887Stegge			ckfree(ifsfirst.next);
33438887Stegge			ifsfirst.next = ifsp;
33538887Stegge			INTON;
33638887Stegge		}
33738887Stegge		if (ifsfirst.begoff > endoff)
33838887Stegge			ifslastp = NULL;
33938887Stegge		else {
34038887Stegge			ifslastp = &ifsfirst;
34138887Stegge			ifsfirst.endoff = endoff;
34238887Stegge		}
34338887Stegge		return;
34438887Stegge	}
34538887Stegge
34638887Stegge	ifslastp = &ifsfirst;
34738887Stegge	while (ifslastp->next && ifslastp->next->begoff < endoff)
34838887Stegge		ifslastp=ifslastp->next;
34938887Stegge	while (ifslastp->next != NULL) {
35038887Stegge		struct ifsregion *ifsp;
35138887Stegge		INTOFF;
35238887Stegge		ifsp = ifslastp->next->next;
35338887Stegge		ckfree(ifslastp->next);
35438887Stegge		ifslastp->next = ifsp;
35538887Stegge		INTON;
35638887Stegge	}
35738887Stegge	if (ifslastp->endoff > endoff)
35838887Stegge		ifslastp->endoff = endoff;
35938887Stegge}
36038887Stegge
3611556Srgrimes/*
3621556Srgrimes * Expand arithmetic expression.  Backup to start of expression,
3631556Srgrimes * evaluate, place result in (backed up) result, adjust string position.
3641556Srgrimes */
3651556Srgrimesvoid
36690111Simpexpari(int flag)
36717987Speter{
3681556Srgrimes	char *p, *start;
3691556Srgrimes	int result;
37038887Stegge	int begoff;
3711556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
37238887Stegge	int quoted;
3731556Srgrimes
37425233Ssteve
3751556Srgrimes	/*
37646684Skris	 * This routine is slightly over-complicated for
3771556Srgrimes	 * efficiency.  First we make sure there is
3781556Srgrimes	 * enough space for the result, which may be bigger
37946684Skris	 * than the expression if we add exponentiation.  Next we
3801556Srgrimes	 * scan backwards looking for the start of arithmetic.  If the
3811556Srgrimes	 * next previous character is a CTLESC character, then we
3821556Srgrimes	 * have to rescan starting from the beginning since CTLESC
3838855Srgrimes	 * characters have to be processed left to right.
3841556Srgrimes	 */
38522777Ssteve#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10
38622777Ssteve#error "integers with more than 10 digits are not supported"
38722777Ssteve#endif
38822777Ssteve	CHECKSTRSPACE(12 - 2, expdest);
3898855Srgrimes	USTPUTC('\0', expdest);
3901556Srgrimes	start = stackblock();
39183676Stegge	p = expdest - 2;
39283676Stegge	while (p >= start && *p != CTLARI)
3931556Srgrimes		--p;
39483676Stegge	if (p < start || *p != CTLARI)
3951556Srgrimes		error("missing CTLARI (shouldn't happen)");
39683676Stegge	if (p > start && *(p - 1) == CTLESC)
3971556Srgrimes		for (p = start; *p != CTLARI; p++)
3981556Srgrimes			if (*p == CTLESC)
3991556Srgrimes				p++;
40038887Stegge
40138887Stegge	if (p[1] == '"')
40238887Stegge		quoted=1;
40338887Stegge	else
40438887Stegge		quoted=0;
40538887Stegge	begoff = p - start;
40638887Stegge	removerecordregions(begoff);
4071556Srgrimes	if (quotes)
40838887Stegge		rmescapes(p+2);
40938887Stegge	result = arith(p+2);
41022777Ssteve	fmtstr(p, 12, "%d", result);
4111556Srgrimes	while (*p++)
4121556Srgrimes		;
41338887Stegge	if (quoted == 0)
41438887Stegge		recordregion(begoff, p - 1 - start, 0);
4151556Srgrimes	result = expdest - p + 1;
4161556Srgrimes	STADJUST(-result, expdest);
4171556Srgrimes}
4181556Srgrimes
4191556Srgrimes
4201556Srgrimes/*
4211556Srgrimes * Expand stuff in backwards quotes.
4221556Srgrimes */
4231556Srgrimes
4241556SrgrimesSTATIC void
42590111Simpexpbackq(union node *cmd, int quoted, int flag)
42617987Speter{
4271556Srgrimes	struct backcmd in;
4281556Srgrimes	int i;
4291556Srgrimes	char buf[128];
4301556Srgrimes	char *p;
4311556Srgrimes	char *dest = expdest;
4321556Srgrimes	struct ifsregion saveifs, *savelastp;
4331556Srgrimes	struct nodelist *saveargbackq;
4341556Srgrimes	char lastc;
4351556Srgrimes	int startloc = dest - stackblock();
4361556Srgrimes	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
4371556Srgrimes	int saveherefd;
4381556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
4391556Srgrimes
4401556Srgrimes	INTOFF;
4411556Srgrimes	saveifs = ifsfirst;
4421556Srgrimes	savelastp = ifslastp;
4431556Srgrimes	saveargbackq = argbackq;
4448855Srgrimes	saveherefd = herefd;
4451556Srgrimes	herefd = -1;
4461556Srgrimes	p = grabstackstr(dest);
4471556Srgrimes	evalbackcmd(cmd, &in);
4481556Srgrimes	ungrabstackstr(p, dest);
4491556Srgrimes	ifsfirst = saveifs;
4501556Srgrimes	ifslastp = savelastp;
4511556Srgrimes	argbackq = saveargbackq;
4521556Srgrimes	herefd = saveherefd;
4531556Srgrimes
4541556Srgrimes	p = in.buf;
4551556Srgrimes	lastc = '\0';
4561556Srgrimes	for (;;) {
4571556Srgrimes		if (--in.nleft < 0) {
4581556Srgrimes			if (in.fd < 0)
4591556Srgrimes				break;
4601556Srgrimes			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
4611556Srgrimes			TRACE(("expbackq: read returns %d\n", i));
4621556Srgrimes			if (i <= 0)
4631556Srgrimes				break;
4641556Srgrimes			p = buf;
4651556Srgrimes			in.nleft = i - 1;
4661556Srgrimes		}
4671556Srgrimes		lastc = *p++;
4681556Srgrimes		if (lastc != '\0') {
46983675Stegge			if (quotes && syntax[(int)lastc] == CCTL)
4701556Srgrimes				STPUTC(CTLESC, dest);
4711556Srgrimes			STPUTC(lastc, dest);
4721556Srgrimes		}
4731556Srgrimes	}
47417987Speter
47517987Speter	/* Eat all trailing newlines */
47617987Speter	for (p--; lastc == '\n'; lastc = *--p)
4771556Srgrimes		STUNPUTC(dest);
47817987Speter
4791556Srgrimes	if (in.fd >= 0)
4801556Srgrimes		close(in.fd);
4811556Srgrimes	if (in.buf)
4821556Srgrimes		ckfree(in.buf);
4831556Srgrimes	if (in.jp)
48445916Scracauer		exitstatus = waitforjob(in.jp, (int *)NULL);
4851556Srgrimes	if (quoted == 0)
4861556Srgrimes		recordregion(startloc, dest - stackblock(), 0);
4871556Srgrimes	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
4881556Srgrimes		(dest - stackblock()) - startloc,
4891556Srgrimes		(dest - stackblock()) - startloc,
4901556Srgrimes		stackblock() + startloc));
4911556Srgrimes	expdest = dest;
4921556Srgrimes	INTON;
4931556Srgrimes}
4941556Srgrimes
4951556Srgrimes
4961556Srgrimes
49717987SpeterSTATIC int
49890111Simpsubevalvar(char *p, char *str, int strloc, int subtype, int startloc,
49990111Simp  int varflags)
50017987Speter{
50117987Speter	char *startp;
50217987Speter	char *loc = NULL;
50345514Stegge	char *q;
50417987Speter	int c = 0;
50517987Speter	int saveherefd = herefd;
50617987Speter	struct nodelist *saveargbackq = argbackq;
50720425Ssteve	int amount;
50820425Ssteve
50917987Speter	herefd = -1;
51017987Speter	argstr(p, 0);
51117987Speter	STACKSTRNUL(expdest);
51217987Speter	herefd = saveherefd;
51317987Speter	argbackq = saveargbackq;
51417987Speter	startp = stackblock() + startloc;
51520425Ssteve	if (str == NULL)
51620425Ssteve	    str = stackblock() + strloc;
51717987Speter
51817987Speter	switch (subtype) {
51917987Speter	case VSASSIGN:
52017987Speter		setvar(str, startp, 0);
52120425Ssteve		amount = startp - expdest;
52220425Ssteve		STADJUST(amount, expdest);
52317987Speter		varflags &= ~VSNUL;
52417987Speter		if (c != 0)
52517987Speter			*loc = c;
52617987Speter		return 1;
52717987Speter
52817987Speter	case VSQUESTION:
52917987Speter		if (*p != CTLENDVAR) {
53017987Speter			outfmt(&errout, "%s\n", startp);
53117987Speter			error((char *)NULL);
53217987Speter		}
53317987Speter		error("%.*s: parameter %snot set", p - str - 1,
53417987Speter		      str, (varflags & VSNUL) ? "null or "
53517987Speter					      : nullstr);
53617987Speter		return 0;
53717987Speter
53817987Speter	case VSTRIMLEFT:
53925233Ssteve		for (loc = startp; loc < str; loc++) {
54017987Speter			c = *loc;
54117987Speter			*loc = '\0';
54245514Stegge			if (patmatch(str, startp, varflags & VSQUOTE)) {
54317987Speter				*loc = c;
54417987Speter				goto recordleft;
54517987Speter			}
54617987Speter			*loc = c;
54745514Stegge			if ((varflags & VSQUOTE) && *loc == CTLESC)
54845514Stegge				loc++;
54917987Speter		}
55017987Speter		return 0;
55117987Speter
55217987Speter	case VSTRIMLEFTMAX:
55345514Stegge		for (loc = str - 1; loc >= startp;) {
55417987Speter			c = *loc;
55517987Speter			*loc = '\0';
55645514Stegge			if (patmatch(str, startp, varflags & VSQUOTE)) {
55717987Speter				*loc = c;
55817987Speter				goto recordleft;
55917987Speter			}
56017987Speter			*loc = c;
56145514Stegge			loc--;
56245514Stegge			if ((varflags & VSQUOTE) && loc > startp &&
56345514Stegge			    *(loc - 1) == CTLESC) {
56445514Stegge				for (q = startp; q < loc; q++)
56545514Stegge					if (*q == CTLESC)
56645514Stegge						q++;
56745514Stegge				if (q > loc)
56845514Stegge					loc--;
56945514Stegge			}
57017987Speter		}
57117987Speter		return 0;
57217987Speter
57317987Speter	case VSTRIMRIGHT:
57445514Stegge		for (loc = str - 1; loc >= startp;) {
57545514Stegge			if (patmatch(str, loc, varflags & VSQUOTE)) {
57620425Ssteve				amount = loc - expdest;
57720425Ssteve				STADJUST(amount, expdest);
57817987Speter				return 1;
57917987Speter			}
58045514Stegge			loc--;
58145514Stegge			if ((varflags & VSQUOTE) && loc > startp &&
58245514Stegge			    *(loc - 1) == CTLESC) {
58345514Stegge				for (q = startp; q < loc; q++)
58445514Stegge					if (*q == CTLESC)
58545514Stegge						q++;
58645514Stegge				if (q > loc)
58745514Stegge					loc--;
58845514Stegge			}
58917987Speter		}
59017987Speter		return 0;
59117987Speter
59217987Speter	case VSTRIMRIGHTMAX:
59317987Speter		for (loc = startp; loc < str - 1; loc++) {
59445514Stegge			if (patmatch(str, loc, varflags & VSQUOTE)) {
59520425Ssteve				amount = loc - expdest;
59620425Ssteve				STADJUST(amount, expdest);
59717987Speter				return 1;
59817987Speter			}
59945514Stegge			if ((varflags & VSQUOTE) && *loc == CTLESC)
60045514Stegge				loc++;
60117987Speter		}
60217987Speter		return 0;
60317987Speter
60417987Speter
60517987Speter	default:
60617987Speter		abort();
60717987Speter	}
60817987Speter
60917987Speterrecordleft:
61020425Ssteve	amount = ((str - 1) - (loc - startp)) - expdest;
61120425Ssteve	STADJUST(amount, expdest);
61217987Speter	while (loc != str - 1)
61317987Speter		*startp++ = *loc++;
61417987Speter	return 1;
61517987Speter}
61617987Speter
61717987Speter
6181556Srgrimes/*
6191556Srgrimes * Expand a variable, and return a pointer to the next character in the
6201556Srgrimes * input string.
6211556Srgrimes */
6221556Srgrimes
6231556SrgrimesSTATIC char *
62490111Simpevalvar(char *p, int flag)
62517987Speter{
6261556Srgrimes	int subtype;
6271556Srgrimes	int varflags;
6281556Srgrimes	char *var;
6291556Srgrimes	char *val;
63045644Stegge	int patloc;
6311556Srgrimes	int c;
6321556Srgrimes	int set;
6331556Srgrimes	int special;
6341556Srgrimes	int startloc;
63517987Speter	int varlen;
63617987Speter	int easy;
6371556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
6381556Srgrimes
6391556Srgrimes	varflags = *p++;
6401556Srgrimes	subtype = varflags & VSTYPE;
6411556Srgrimes	var = p;
6421556Srgrimes	special = 0;
6431556Srgrimes	if (! is_name(*p))
6441556Srgrimes		special = 1;
6451556Srgrimes	p = strchr(p, '=') + 1;
6461556Srgrimesagain: /* jump here after setting a variable with ${var=text} */
6471556Srgrimes	if (special) {
64825233Ssteve		set = varisset(var, varflags & VSNUL);
6491556Srgrimes		val = NULL;
6501556Srgrimes	} else {
65160592Scracauer		val = bltinlookup(var, 1);
65217987Speter		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
6531556Srgrimes			val = NULL;
6541556Srgrimes			set = 0;
6551556Srgrimes		} else
6561556Srgrimes			set = 1;
6571556Srgrimes	}
65817987Speter	varlen = 0;
6591556Srgrimes	startloc = expdest - stackblock();
66096939Stjr	if (!set && uflag) {
66196939Stjr		switch (subtype) {
66296939Stjr		case VSNORMAL:
66396939Stjr		case VSTRIMLEFT:
66496939Stjr		case VSTRIMLEFTMAX:
66596939Stjr		case VSTRIMRIGHT:
66696939Stjr		case VSTRIMRIGHTMAX:
66796939Stjr		case VSLENGTH:
66896939Stjr			error("%.*s: parameter not set", p - var - 1, var);
66996939Stjr		}
67096939Stjr	}
6711556Srgrimes	if (set && subtype != VSPLUS) {
6721556Srgrimes		/* insert the value of the variable */
6731556Srgrimes		if (special) {
67418202Speter			varvalue(var, varflags & VSQUOTE, flag & EXP_FULL);
67517987Speter			if (subtype == VSLENGTH) {
67625233Ssteve				varlen = expdest - stackblock() - startloc;
67725233Ssteve				STADJUST(-varlen, expdest);
67817987Speter			}
6791556Srgrimes		} else {
68020425Ssteve			char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
68117987Speter								  : BASESYNTAX;
6821556Srgrimes
68317987Speter			if (subtype == VSLENGTH) {
68417987Speter				for (;*val; val++)
68517987Speter					varlen++;
6861556Srgrimes			}
68717987Speter			else {
68817987Speter				while (*val) {
68983675Stegge					if (quotes &&
69054132Scracauer					    syntax[(int)*val] == CCTL)
69117987Speter						STPUTC(CTLESC, expdest);
69217987Speter					STPUTC(*val++, expdest);
69317987Speter				}
69417987Speter
69517987Speter			}
6961556Srgrimes		}
6971556Srgrimes	}
69820425Ssteve
6991556Srgrimes	if (subtype == VSPLUS)
7001556Srgrimes		set = ! set;
70117987Speter
70220425Ssteve	easy = ((varflags & VSQUOTE) == 0 ||
70317987Speter		(*var == '@' && shellparam.nparam != 1));
70417987Speter
70517987Speter
70617987Speter	switch (subtype) {
70717987Speter	case VSLENGTH:
70817987Speter		expdest = cvtnum(varlen, expdest);
70917987Speter		goto record;
71017987Speter
71117987Speter	case VSNORMAL:
71217987Speter		if (!easy)
71317987Speter			break;
71417987Speterrecord:
71520425Ssteve		recordregion(startloc, expdest - stackblock(),
71617987Speter			     varflags & VSQUOTE);
71717987Speter		break;
71817987Speter
71917987Speter	case VSPLUS:
72017987Speter	case VSMINUS:
72117987Speter		if (!set) {
7221556Srgrimes			argstr(p, flag);
72317987Speter			break;
72417987Speter		}
72517987Speter		if (easy)
72617987Speter			goto record;
72717987Speter		break;
72817987Speter
72917987Speter	case VSTRIMLEFT:
73017987Speter	case VSTRIMLEFTMAX:
73117987Speter	case VSTRIMRIGHT:
73217987Speter	case VSTRIMRIGHTMAX:
73317987Speter		if (!set)
73417987Speter			break;
73517987Speter		/*
73617987Speter		 * Terminate the string and start recording the pattern
73717987Speter		 * right after it
73817987Speter		 */
73917987Speter		STPUTC('\0', expdest);
74045644Stegge		patloc = expdest - stackblock();
74145644Stegge		if (subevalvar(p, NULL, patloc, subtype,
74238887Stegge			       startloc, varflags) == 0) {
74345644Stegge			int amount = (expdest - stackblock() - patloc) + 1;
74425233Ssteve			STADJUST(-amount, expdest);
74525233Ssteve		}
74638887Stegge		/* Remove any recorded regions beyond start of variable */
74738887Stegge		removerecordregions(startloc);
74838887Stegge		goto record;
74917987Speter
75017987Speter	case VSASSIGN:
75117987Speter	case VSQUESTION:
75217987Speter		if (!set) {
75320425Ssteve			if (subevalvar(p, var, 0, subtype, startloc, varflags)) {
75420425Ssteve				varflags &= ~VSNUL;
75538887Stegge				/*
75638887Stegge				 * Remove any recorded regions beyond
75738887Stegge				 * start of variable
75838887Stegge				 */
75938887Stegge				removerecordregions(startloc);
7601556Srgrimes				goto again;
76120425Ssteve			}
76217987Speter			break;
7631556Srgrimes		}
76417987Speter		if (easy)
76517987Speter			goto record;
76617987Speter		break;
76717987Speter
76817987Speter	default:
76917987Speter		abort();
7701556Srgrimes	}
77117987Speter
7721556Srgrimes	if (subtype != VSNORMAL) {	/* skip to end of alternative */
7731556Srgrimes		int nesting = 1;
7741556Srgrimes		for (;;) {
7751556Srgrimes			if ((c = *p++) == CTLESC)
7761556Srgrimes				p++;
7771556Srgrimes			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
7781556Srgrimes				if (set)
7791556Srgrimes					argbackq = argbackq->next;
7801556Srgrimes			} else if (c == CTLVAR) {
7811556Srgrimes				if ((*p++ & VSTYPE) != VSNORMAL)
7821556Srgrimes					nesting++;
7831556Srgrimes			} else if (c == CTLENDVAR) {
7841556Srgrimes				if (--nesting == 0)
7851556Srgrimes					break;
7861556Srgrimes			}
7871556Srgrimes		}
7881556Srgrimes	}
7891556Srgrimes	return p;
7901556Srgrimes}
7911556Srgrimes
7921556Srgrimes
7931556Srgrimes
7941556Srgrimes/*
7951556Srgrimes * Test whether a specialized variable is set.
7961556Srgrimes */
7971556Srgrimes
7981556SrgrimesSTATIC int
79990111Simpvarisset(char *name, int nulok)
80025233Ssteve{
8011556Srgrimes
80225233Ssteve	if (*name == '!')
80325233Ssteve		return backgndpid != -1;
80425233Ssteve	else if (*name == '@' || *name == '*') {
8051556Srgrimes		if (*shellparam.p == NULL)
8061556Srgrimes			return 0;
80725233Ssteve
80825233Ssteve		if (nulok) {
80925233Ssteve			char **av;
81025233Ssteve
81125233Ssteve			for (av = shellparam.p; *av; av++)
81225233Ssteve				if (**av != '\0')
81325233Ssteve					return 1;
81425233Ssteve			return 0;
81525233Ssteve		}
81618202Speter	} else if (is_digit(*name)) {
81725233Ssteve		char *ap;
81820425Ssteve		int num = atoi(name);
81925233Ssteve
82025233Ssteve		if (num > shellparam.nparam)
82125233Ssteve			return 0;
82225233Ssteve
82325233Ssteve		if (num == 0)
82425233Ssteve			ap = arg0;
82525233Ssteve		else
82625233Ssteve			ap = shellparam.p[num - 1];
82725233Ssteve
82825233Ssteve		if (nulok && (ap == NULL || *ap == '\0'))
82925233Ssteve			return 0;
8301556Srgrimes	}
8311556Srgrimes	return 1;
8321556Srgrimes}
8331556Srgrimes
8341556Srgrimes
8351556Srgrimes
8361556Srgrimes/*
8371556Srgrimes * Add the value of a specialized variable to the stack string.
8381556Srgrimes */
8391556Srgrimes
8401556SrgrimesSTATIC void
84190111Simpvarvalue(char *name, int quoted, int allow_split)
84217987Speter{
8431556Srgrimes	int num;
8441556Srgrimes	char *p;
8451556Srgrimes	int i;
84617987Speter	extern int oexitstatus;
8471556Srgrimes	char sep;
8481556Srgrimes	char **ap;
8491556Srgrimes	char const *syntax;
8501556Srgrimes
8511556Srgrimes#define STRTODEST(p) \
8521556Srgrimes	do {\
8531556Srgrimes	if (allow_split) { \
8541556Srgrimes		syntax = quoted? DQSYNTAX : BASESYNTAX; \
8551556Srgrimes		while (*p) { \
85683675Stegge			if (syntax[(int)*p] == CCTL) \
8571556Srgrimes				STPUTC(CTLESC, expdest); \
8581556Srgrimes			STPUTC(*p++, expdest); \
8591556Srgrimes		} \
8601556Srgrimes	} else \
8611556Srgrimes		while (*p) \
8621556Srgrimes			STPUTC(*p++, expdest); \
8631556Srgrimes	} while (0)
8641556Srgrimes
8651556Srgrimes
86618202Speter	switch (*name) {
8671556Srgrimes	case '$':
8681556Srgrimes		num = rootpid;
8691556Srgrimes		goto numvar;
8701556Srgrimes	case '?':
87117987Speter		num = oexitstatus;
8721556Srgrimes		goto numvar;
8731556Srgrimes	case '#':
8741556Srgrimes		num = shellparam.nparam;
8751556Srgrimes		goto numvar;
8761556Srgrimes	case '!':
8771556Srgrimes		num = backgndpid;
8781556Srgrimesnumvar:
87917987Speter		expdest = cvtnum(num, expdest);
8801556Srgrimes		break;
8811556Srgrimes	case '-':
8821556Srgrimes		for (i = 0 ; i < NOPTS ; i++) {
8831556Srgrimes			if (optlist[i].val)
8841556Srgrimes				STPUTC(optlist[i].letter, expdest);
8851556Srgrimes		}
8861556Srgrimes		break;
8871556Srgrimes	case '@':
88838887Stegge		if (allow_split && quoted) {
88938887Stegge			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
89038887Stegge				STRTODEST(p);
89138887Stegge				if (*ap)
89238887Stegge					STPUTC('\0', expdest);
89338887Stegge			}
89438887Stegge			break;
8951556Srgrimes		}
8968855Srgrimes		/* fall through */
8971556Srgrimes	case '*':
89838887Stegge		if (ifsset() != 0)
89938887Stegge			sep = ifsval()[0];
90038887Stegge		else
90138887Stegge			sep = ' ';
9021556Srgrimes		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
9031556Srgrimes			STRTODEST(p);
90438887Stegge			if (*ap && sep)
9051556Srgrimes				STPUTC(sep, expdest);
9061556Srgrimes		}
9071556Srgrimes		break;
9081556Srgrimes	case '0':
9091556Srgrimes		p = arg0;
9101556Srgrimes		STRTODEST(p);
9111556Srgrimes		break;
9121556Srgrimes	default:
91318202Speter		if (is_digit(*name)) {
91418202Speter			num = atoi(name);
91518202Speter			if (num > 0 && num <= shellparam.nparam) {
91618202Speter				p = shellparam.p[num - 1];
91718202Speter				STRTODEST(p);
91818202Speter			}
9191556Srgrimes		}
9201556Srgrimes		break;
9211556Srgrimes	}
9221556Srgrimes}
9231556Srgrimes
9241556Srgrimes
9251556Srgrimes
9261556Srgrimes/*
9271556Srgrimes * Record the the fact that we have to scan this region of the
9281556Srgrimes * string for IFS characters.
9291556Srgrimes */
9301556Srgrimes
9311556SrgrimesSTATIC void
93290111Simprecordregion(int start, int end, int nulonly)
93317987Speter{
93425233Ssteve	struct ifsregion *ifsp;
9351556Srgrimes
9361556Srgrimes	if (ifslastp == NULL) {
9371556Srgrimes		ifsp = &ifsfirst;
9381556Srgrimes	} else {
9391556Srgrimes		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
9401556Srgrimes		ifslastp->next = ifsp;
9411556Srgrimes	}
9421556Srgrimes	ifslastp = ifsp;
9431556Srgrimes	ifslastp->next = NULL;
9441556Srgrimes	ifslastp->begoff = start;
9451556Srgrimes	ifslastp->endoff = end;
9461556Srgrimes	ifslastp->nulonly = nulonly;
9471556Srgrimes}
9481556Srgrimes
9491556Srgrimes
9501556Srgrimes
9511556Srgrimes/*
9521556Srgrimes * Break the argument string into pieces based upon IFS and add the
9531556Srgrimes * strings to the argument list.  The regions of the string to be
9541556Srgrimes * searched for IFS characters have been stored by recordregion.
9551556Srgrimes */
9561556SrgrimesSTATIC void
95790111Simpifsbreakup(char *string, struct arglist *arglist)
95890111Simp{
9591556Srgrimes	struct ifsregion *ifsp;
9601556Srgrimes	struct strlist *sp;
9611556Srgrimes	char *start;
96225233Ssteve	char *p;
9631556Srgrimes	char *q;
9641556Srgrimes	char *ifs;
96517987Speter	int ifsspc;
96638887Stegge	int nulonly;
9671556Srgrimes
96817987Speter
9691556Srgrimes	start = string;
97038887Stegge	ifsspc = 0;
97138887Stegge	nulonly = 0;
9721556Srgrimes	if (ifslastp != NULL) {
9731556Srgrimes		ifsp = &ifsfirst;
9741556Srgrimes		do {
9751556Srgrimes			p = string + ifsp->begoff;
97638887Stegge			nulonly = ifsp->nulonly;
97738887Stegge			ifs = nulonly ? nullstr :
97838887Stegge				( ifsset() ? ifsval() : " \t\n" );
97938887Stegge			ifsspc = 0;
9801556Srgrimes			while (p < string + ifsp->endoff) {
9811556Srgrimes				q = p;
9821556Srgrimes				if (*p == CTLESC)
9831556Srgrimes					p++;
98438887Stegge				if (strchr(ifs, *p)) {
98538887Stegge					if (!nulonly)
98638887Stegge						ifsspc = (strchr(" \t\n", *p) != NULL);
98738887Stegge					/* Ignore IFS whitespace at start */
98838887Stegge					if (q == start && ifsspc) {
98938887Stegge						p++;
99038887Stegge						start = p;
99138887Stegge						continue;
9921556Srgrimes					}
99338887Stegge					*q = '\0';
99438887Stegge					sp = (struct strlist *)stalloc(sizeof *sp);
99538887Stegge					sp->text = start;
99638887Stegge					*arglist->lastp = sp;
99738887Stegge					arglist->lastp = &sp->next;
99838887Stegge					p++;
99938887Stegge					if (!nulonly) {
10001556Srgrimes						for (;;) {
100138887Stegge							if (p >= string + ifsp->endoff) {
10021556Srgrimes								break;
100338887Stegge							}
10041556Srgrimes							q = p;
10051556Srgrimes							if (*p == CTLESC)
10061556Srgrimes								p++;
100738887Stegge							if (strchr(ifs, *p) == NULL ) {
10081556Srgrimes								p = q;
10091556Srgrimes								break;
101038887Stegge							} else if (strchr(" \t\n",*p) == NULL) {
101138887Stegge								if (ifsspc) {
101238887Stegge									p++;
101338887Stegge									ifsspc = 0;
101438887Stegge								} else {
101538887Stegge									p = q;
101638887Stegge									break;
101738887Stegge								}
101838887Stegge							} else
101938887Stegge								p++;
10201556Srgrimes						}
10211556Srgrimes					}
10221556Srgrimes					start = p;
102338887Stegge				} else
102438887Stegge					p++;
10251556Srgrimes			}
10261556Srgrimes		} while ((ifsp = ifsp->next) != NULL);
102738887Stegge		if (*start || (!ifsspc && start > string &&
102838887Stegge			(nulonly || 1))) {
10291556Srgrimes			sp = (struct strlist *)stalloc(sizeof *sp);
10301556Srgrimes			sp->text = start;
10311556Srgrimes			*arglist->lastp = sp;
10321556Srgrimes			arglist->lastp = &sp->next;
10331556Srgrimes		}
10341556Srgrimes	} else {
10351556Srgrimes		sp = (struct strlist *)stalloc(sizeof *sp);
10361556Srgrimes		sp->text = start;
10371556Srgrimes		*arglist->lastp = sp;
10381556Srgrimes		arglist->lastp = &sp->next;
10391556Srgrimes	}
10401556Srgrimes}
10411556Srgrimes
10421556Srgrimes
10431556Srgrimes
10441556Srgrimes/*
10451556Srgrimes * Expand shell metacharacters.  At this point, the only control characters
10461556Srgrimes * should be escapes.  The results are stored in the list exparg.
10471556Srgrimes */
10481556Srgrimes
10491556Srgrimeschar *expdir;
10501556Srgrimes
10511556Srgrimes
10521556SrgrimesSTATIC void
105390111Simpexpandmeta(struct strlist *str, int flag __unused)
105417987Speter{
10551556Srgrimes	char *p;
10561556Srgrimes	struct strlist **savelastp;
10571556Srgrimes	struct strlist *sp;
10581556Srgrimes	char c;
10591556Srgrimes	/* TODO - EXP_REDIR */
10601556Srgrimes
10611556Srgrimes	while (str) {
10621556Srgrimes		if (fflag)
10631556Srgrimes			goto nometa;
10641556Srgrimes		p = str->text;
10651556Srgrimes		for (;;) {			/* fast check for meta chars */
10661556Srgrimes			if ((c = *p++) == '\0')
10671556Srgrimes				goto nometa;
10681556Srgrimes			if (c == '*' || c == '?' || c == '[' || c == '!')
10691556Srgrimes				break;
10701556Srgrimes		}
10711556Srgrimes		savelastp = exparg.lastp;
10721556Srgrimes		INTOFF;
10731556Srgrimes		if (expdir == NULL) {
10741556Srgrimes			int i = strlen(str->text);
10751556Srgrimes			expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
10761556Srgrimes		}
10771556Srgrimes
10781556Srgrimes		expmeta(expdir, str->text);
10791556Srgrimes		ckfree(expdir);
10801556Srgrimes		expdir = NULL;
10811556Srgrimes		INTON;
10821556Srgrimes		if (exparg.lastp == savelastp) {
10838855Srgrimes			/*
10848855Srgrimes			 * no matches
10851556Srgrimes			 */
10861556Srgrimesnometa:
10871556Srgrimes			*exparg.lastp = str;
10881556Srgrimes			rmescapes(str->text);
10891556Srgrimes			exparg.lastp = &str->next;
10901556Srgrimes		} else {
10911556Srgrimes			*exparg.lastp = NULL;
10921556Srgrimes			*savelastp = sp = expsort(*savelastp);
10931556Srgrimes			while (sp->next != NULL)
10941556Srgrimes				sp = sp->next;
10951556Srgrimes			exparg.lastp = &sp->next;
10961556Srgrimes		}
10971556Srgrimes		str = str->next;
10981556Srgrimes	}
10991556Srgrimes}
11001556Srgrimes
11011556Srgrimes
11021556Srgrimes/*
11031556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion.
11041556Srgrimes */
11051556Srgrimes
11061556SrgrimesSTATIC void
110790111Simpexpmeta(char *enddir, char *name)
110890111Simp{
110925233Ssteve	char *p;
11101556Srgrimes	char *q;
11111556Srgrimes	char *start;
11121556Srgrimes	char *endname;
11131556Srgrimes	int metaflag;
11141556Srgrimes	struct stat statb;
11151556Srgrimes	DIR *dirp;
11161556Srgrimes	struct dirent *dp;
11171556Srgrimes	int atend;
11181556Srgrimes	int matchdot;
11191556Srgrimes
11201556Srgrimes	metaflag = 0;
11211556Srgrimes	start = name;
11221556Srgrimes	for (p = name ; ; p++) {
11231556Srgrimes		if (*p == '*' || *p == '?')
11241556Srgrimes			metaflag = 1;
11251556Srgrimes		else if (*p == '[') {
11261556Srgrimes			q = p + 1;
112726488Sache			if (*q == '!' || *q == '^')
11281556Srgrimes				q++;
11291556Srgrimes			for (;;) {
113038887Stegge				while (*q == CTLQUOTEMARK)
113138887Stegge					q++;
11321556Srgrimes				if (*q == CTLESC)
11331556Srgrimes					q++;
11341556Srgrimes				if (*q == '/' || *q == '\0')
11351556Srgrimes					break;
11361556Srgrimes				if (*++q == ']') {
11371556Srgrimes					metaflag = 1;
11381556Srgrimes					break;
11391556Srgrimes				}
11401556Srgrimes			}
11411556Srgrimes		} else if (*p == '!' && p[1] == '!'	&& (p == name || p[-1] == '/')) {
11421556Srgrimes			metaflag = 1;
11431556Srgrimes		} else if (*p == '\0')
11441556Srgrimes			break;
114538887Stegge		else if (*p == CTLQUOTEMARK)
114638887Stegge			continue;
11471556Srgrimes		else if (*p == CTLESC)
11481556Srgrimes			p++;
11491556Srgrimes		if (*p == '/') {
11501556Srgrimes			if (metaflag)
11511556Srgrimes				break;
11521556Srgrimes			start = p + 1;
11531556Srgrimes		}
11541556Srgrimes	}
11551556Srgrimes	if (metaflag == 0) {	/* we've reached the end of the file name */
11561556Srgrimes		if (enddir != expdir)
11571556Srgrimes			metaflag++;
11581556Srgrimes		for (p = name ; ; p++) {
115938887Stegge			if (*p == CTLQUOTEMARK)
116038887Stegge				continue;
11611556Srgrimes			if (*p == CTLESC)
11621556Srgrimes				p++;
11631556Srgrimes			*enddir++ = *p;
11641556Srgrimes			if (*p == '\0')
11651556Srgrimes				break;
11661556Srgrimes		}
11671556Srgrimes		if (metaflag == 0 || stat(expdir, &statb) >= 0)
11681556Srgrimes			addfname(expdir);
11691556Srgrimes		return;
11701556Srgrimes	}
11711556Srgrimes	endname = p;
11721556Srgrimes	if (start != name) {
11731556Srgrimes		p = name;
11741556Srgrimes		while (p < start) {
117538887Stegge			while (*p == CTLQUOTEMARK)
117638887Stegge				p++;
11771556Srgrimes			if (*p == CTLESC)
11781556Srgrimes				p++;
11791556Srgrimes			*enddir++ = *p++;
11801556Srgrimes		}
11811556Srgrimes	}
11821556Srgrimes	if (enddir == expdir) {
11831556Srgrimes		p = ".";
11841556Srgrimes	} else if (enddir == expdir + 1 && *expdir == '/') {
11851556Srgrimes		p = "/";
11861556Srgrimes	} else {
11871556Srgrimes		p = expdir;
11881556Srgrimes		enddir[-1] = '\0';
11891556Srgrimes	}
11901556Srgrimes	if ((dirp = opendir(p)) == NULL)
11911556Srgrimes		return;
11921556Srgrimes	if (enddir != expdir)
11931556Srgrimes		enddir[-1] = '/';
11941556Srgrimes	if (*endname == 0) {
11951556Srgrimes		atend = 1;
11961556Srgrimes	} else {
11971556Srgrimes		atend = 0;
11981556Srgrimes		*endname++ = '\0';
11991556Srgrimes	}
12001556Srgrimes	matchdot = 0;
120138887Stegge	p = start;
120238887Stegge	while (*p == CTLQUOTEMARK)
120338887Stegge		p++;
120438887Stegge	if (*p == CTLESC)
120538887Stegge		p++;
120638887Stegge	if (*p == '.')
12071556Srgrimes		matchdot++;
12081556Srgrimes	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
12091556Srgrimes		if (dp->d_name[0] == '.' && ! matchdot)
12101556Srgrimes			continue;
121145514Stegge		if (patmatch(start, dp->d_name, 0)) {
12121556Srgrimes			if (atend) {
12131556Srgrimes				scopy(dp->d_name, enddir);
12141556Srgrimes				addfname(expdir);
12151556Srgrimes			} else {
12161556Srgrimes				char *q;
121717987Speter				for (p = enddir, q = dp->d_name;
121817987Speter				     (*p++ = *q++) != '\0';)
121917987Speter					continue;
12201556Srgrimes				p[-1] = '/';
12211556Srgrimes				expmeta(p, endname);
12221556Srgrimes			}
12231556Srgrimes		}
12241556Srgrimes	}
12251556Srgrimes	closedir(dirp);
12261556Srgrimes	if (! atend)
12271556Srgrimes		endname[-1] = '/';
12281556Srgrimes}
12291556Srgrimes
12301556Srgrimes
12311556Srgrimes/*
12321556Srgrimes * Add a file name to the list.
12331556Srgrimes */
12341556Srgrimes
12351556SrgrimesSTATIC void
123690111Simpaddfname(char *name)
123790111Simp{
12381556Srgrimes	char *p;
12391556Srgrimes	struct strlist *sp;
12401556Srgrimes
12411556Srgrimes	p = stalloc(strlen(name) + 1);
12421556Srgrimes	scopy(name, p);
12431556Srgrimes	sp = (struct strlist *)stalloc(sizeof *sp);
12441556Srgrimes	sp->text = p;
12451556Srgrimes	*exparg.lastp = sp;
12461556Srgrimes	exparg.lastp = &sp->next;
12471556Srgrimes}
12481556Srgrimes
12491556Srgrimes
12501556Srgrimes/*
12511556Srgrimes * Sort the results of file name expansion.  It calculates the number of
12521556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the
12531556Srgrimes * work.
12541556Srgrimes */
12551556Srgrimes
12561556SrgrimesSTATIC struct strlist *
125790111Simpexpsort(struct strlist *str)
125890111Simp{
12591556Srgrimes	int len;
12601556Srgrimes	struct strlist *sp;
12611556Srgrimes
12621556Srgrimes	len = 0;
12631556Srgrimes	for (sp = str ; sp ; sp = sp->next)
12641556Srgrimes		len++;
12651556Srgrimes	return msort(str, len);
12661556Srgrimes}
12671556Srgrimes
12681556Srgrimes
12691556SrgrimesSTATIC struct strlist *
127090111Simpmsort(struct strlist *list, int len)
127117987Speter{
127217987Speter	struct strlist *p, *q = NULL;
12731556Srgrimes	struct strlist **lpp;
12741556Srgrimes	int half;
12751556Srgrimes	int n;
12761556Srgrimes
12771556Srgrimes	if (len <= 1)
12781556Srgrimes		return list;
12798855Srgrimes	half = len >> 1;
12801556Srgrimes	p = list;
12811556Srgrimes	for (n = half ; --n >= 0 ; ) {
12821556Srgrimes		q = p;
12831556Srgrimes		p = p->next;
12841556Srgrimes	}
12851556Srgrimes	q->next = NULL;			/* terminate first half of list */
12861556Srgrimes	q = msort(list, half);		/* sort first half of list */
12871556Srgrimes	p = msort(p, len - half);		/* sort second half */
12881556Srgrimes	lpp = &list;
12891556Srgrimes	for (;;) {
12901556Srgrimes		if (strcmp(p->text, q->text) < 0) {
12911556Srgrimes			*lpp = p;
12921556Srgrimes			lpp = &p->next;
12931556Srgrimes			if ((p = *lpp) == NULL) {
12941556Srgrimes				*lpp = q;
12951556Srgrimes				break;
12961556Srgrimes			}
12971556Srgrimes		} else {
12981556Srgrimes			*lpp = q;
12991556Srgrimes			lpp = &q->next;
13001556Srgrimes			if ((q = *lpp) == NULL) {
13011556Srgrimes				*lpp = p;
13021556Srgrimes				break;
13031556Srgrimes			}
13041556Srgrimes		}
13051556Srgrimes	}
13061556Srgrimes	return list;
13071556Srgrimes}
13081556Srgrimes
13091556Srgrimes
13101556Srgrimes
13111556Srgrimes/*
13121556Srgrimes * Returns true if the pattern matches the string.
13131556Srgrimes */
13141556Srgrimes
13151556Srgrimesint
131690111Simppatmatch(char *pattern, char *string, int squoted)
131790111Simp{
13181556Srgrimes#ifdef notdef
13191556Srgrimes	if (pattern[0] == '!' && pattern[1] == '!')
13201556Srgrimes		return 1 - pmatch(pattern + 2, string);
13211556Srgrimes	else
13221556Srgrimes#endif
132345514Stegge		return pmatch(pattern, string, squoted);
13241556Srgrimes}
13251556Srgrimes
132617987Speter
132717525SacheSTATIC int
132890111Simppmatch(char *pattern, char *string, int squoted)
132990111Simp{
133025233Ssteve	char *p, *q;
133125233Ssteve	char c;
13321556Srgrimes
13331556Srgrimes	p = pattern;
13341556Srgrimes	q = string;
13351556Srgrimes	for (;;) {
13361556Srgrimes		switch (c = *p++) {
13371556Srgrimes		case '\0':
13381556Srgrimes			goto breakloop;
13391556Srgrimes		case CTLESC:
134045514Stegge			if (squoted && *q == CTLESC)
134145514Stegge				q++;
13421556Srgrimes			if (*q++ != *p++)
13431556Srgrimes				return 0;
13441556Srgrimes			break;
134538887Stegge		case CTLQUOTEMARK:
134638887Stegge			continue;
13471556Srgrimes		case '?':
134845514Stegge			if (squoted && *q == CTLESC)
134945514Stegge				q++;
13501556Srgrimes			if (*q++ == '\0')
13511556Srgrimes				return 0;
13521556Srgrimes			break;
13531556Srgrimes		case '*':
13541556Srgrimes			c = *p;
135538887Stegge			while (c == CTLQUOTEMARK || c == '*')
135638887Stegge				c = *++p;
135738887Stegge			if (c != CTLESC &&  c != CTLQUOTEMARK &&
135838887Stegge			    c != '?' && c != '*' && c != '[') {
13591556Srgrimes				while (*q != c) {
136045514Stegge					if (squoted && *q == CTLESC &&
136145514Stegge					    q[1] == c)
136245514Stegge						break;
13631556Srgrimes					if (*q == '\0')
13641556Srgrimes						return 0;
136545514Stegge					if (squoted && *q == CTLESC)
136645514Stegge						q++;
13671556Srgrimes					q++;
13681556Srgrimes				}
13691556Srgrimes			}
13701556Srgrimes			do {
137145514Stegge				if (pmatch(p, q, squoted))
13721556Srgrimes					return 1;
137345514Stegge				if (squoted && *q == CTLESC)
137445514Stegge					q++;
13751556Srgrimes			} while (*q++ != '\0');
13761556Srgrimes			return 0;
13771556Srgrimes		case '[': {
13781556Srgrimes			char *endp;
13791556Srgrimes			int invert, found;
13801556Srgrimes			char chr;
13811556Srgrimes
13821556Srgrimes			endp = p;
138326488Sache			if (*endp == '!' || *endp == '^')
13841556Srgrimes				endp++;
13851556Srgrimes			for (;;) {
138638887Stegge				while (*endp == CTLQUOTEMARK)
138738887Stegge					endp++;
13881556Srgrimes				if (*endp == '\0')
13891556Srgrimes					goto dft;		/* no matching ] */
13901556Srgrimes				if (*endp == CTLESC)
13911556Srgrimes					endp++;
13921556Srgrimes				if (*++endp == ']')
13931556Srgrimes					break;
13941556Srgrimes			}
13951556Srgrimes			invert = 0;
139626488Sache			if (*p == '!' || *p == '^') {
13971556Srgrimes				invert++;
13981556Srgrimes				p++;
13991556Srgrimes			}
14001556Srgrimes			found = 0;
14011556Srgrimes			chr = *q++;
140245514Stegge			if (squoted && chr == CTLESC)
140345514Stegge				chr = *q++;
140417987Speter			if (chr == '\0')
140517987Speter				return 0;
14061556Srgrimes			c = *p++;
14071556Srgrimes			do {
140838887Stegge				if (c == CTLQUOTEMARK)
140938887Stegge					continue;
14101556Srgrimes				if (c == CTLESC)
14111556Srgrimes					c = *p++;
14121556Srgrimes				if (*p == '-' && p[1] != ']') {
14131556Srgrimes					p++;
141438887Stegge					while (*p == CTLQUOTEMARK)
141538887Stegge						p++;
14161556Srgrimes					if (*p == CTLESC)
14171556Srgrimes						p++;
141817557Sache					if (   collate_range_cmp(chr, c) >= 0
141917557Sache					    && collate_range_cmp(chr, *p) <= 0
142017525Sache					   )
14211556Srgrimes						found = 1;
14221556Srgrimes					p++;
14231556Srgrimes				} else {
14241556Srgrimes					if (chr == c)
14251556Srgrimes						found = 1;
14261556Srgrimes				}
14271556Srgrimes			} while ((c = *p++) != ']');
14281556Srgrimes			if (found == invert)
14291556Srgrimes				return 0;
14301556Srgrimes			break;
14311556Srgrimes		}
14321556Srgrimesdft:	        default:
143345514Stegge			if (squoted && *q == CTLESC)
143445514Stegge				q++;
14351556Srgrimes			if (*q++ != c)
14361556Srgrimes				return 0;
14371556Srgrimes			break;
14381556Srgrimes		}
14391556Srgrimes	}
14401556Srgrimesbreakloop:
14411556Srgrimes	if (*q != '\0')
14421556Srgrimes		return 0;
14431556Srgrimes	return 1;
14441556Srgrimes}
14451556Srgrimes
14461556Srgrimes
14471556Srgrimes
14481556Srgrimes/*
14491556Srgrimes * Remove any CTLESC characters from a string.
14501556Srgrimes */
14511556Srgrimes
14521556Srgrimesvoid
145390111Simprmescapes(char *str)
145438887Stegge{
145525233Ssteve	char *p, *q;
14561556Srgrimes
14571556Srgrimes	p = str;
145838887Stegge	while (*p != CTLESC && *p != CTLQUOTEMARK) {
14591556Srgrimes		if (*p++ == '\0')
14601556Srgrimes			return;
14611556Srgrimes	}
14621556Srgrimes	q = p;
14631556Srgrimes	while (*p) {
146438887Stegge		if (*p == CTLQUOTEMARK) {
146538887Stegge			p++;
146638887Stegge			continue;
146738887Stegge		}
14681556Srgrimes		if (*p == CTLESC)
14691556Srgrimes			p++;
14701556Srgrimes		*q++ = *p++;
14711556Srgrimes	}
14721556Srgrimes	*q = '\0';
14731556Srgrimes}
14741556Srgrimes
14751556Srgrimes
14761556Srgrimes
14771556Srgrimes/*
14781556Srgrimes * See if a pattern matches in a case statement.
14791556Srgrimes */
14801556Srgrimes
14811556Srgrimesint
148290111Simpcasematch(union node *pattern, char *val)
148390111Simp{
14841556Srgrimes	struct stackmark smark;
14851556Srgrimes	int result;
14861556Srgrimes	char *p;
14871556Srgrimes
14881556Srgrimes	setstackmark(&smark);
14891556Srgrimes	argbackq = pattern->narg.backquote;
14901556Srgrimes	STARTSTACKSTR(expdest);
14911556Srgrimes	ifslastp = NULL;
14921556Srgrimes	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
14931556Srgrimes	STPUTC('\0', expdest);
14941556Srgrimes	p = grabstackstr(expdest);
149545514Stegge	result = patmatch(p, val, 0);
14961556Srgrimes	popstackmark(&smark);
14971556Srgrimes	return result;
14981556Srgrimes}
149917987Speter
150017987Speter/*
150117987Speter * Our own itoa().
150217987Speter */
150317987Speter
150417987SpeterSTATIC char *
150590111Simpcvtnum(int num, char *buf)
150690111Simp{
150717987Speter	char temp[32];
150817987Speter	int neg = num < 0;
150917987Speter	char *p = temp + 31;
151017987Speter
151117987Speter	temp[31] = '\0';
151217987Speter
151317987Speter	do {
151417987Speter		*--p = num % 10 + '0';
151517987Speter	} while ((num /= 10) != 0);
151617987Speter
151717987Speter	if (neg)
151817987Speter		*--p = '-';
151917987Speter
152017987Speter	while (*p)
152117987Speter		STPUTC(*p++, buf);
152217987Speter	return buf;
152317987Speter}
1524