expand.c revision 206150
1195609Smp/*-
2195609Smp * Copyright (c) 1991, 1993
3195609Smp *	The Regents of the University of California.  All rights reserved.
4195609Smp *
5195609Smp * This code is derived from software contributed to Berkeley by
6195609Smp * Kenneth Almquist.
7195609Smp *
8195609Smp * Redistribution and use in source and binary forms, with or without
9195609Smp * modification, are permitted provided that the following conditions
10195609Smp * are met:
11195609Smp * 1. Redistributions of source code must retain the above copyright
12195609Smp *    notice, this list of conditions and the following disclaimer.
13195609Smp * 2. Redistributions in binary form must reproduce the above copyright
14195609Smp *    notice, this list of conditions and the following disclaimer in the
15195609Smp *    documentation and/or other materials provided with the distribution.
16195609Smp * 4. Neither the name of the University nor the names of its contributors
17195609Smp *    may be used to endorse or promote products derived from this software
18195609Smp *    without specific prior written permission.
19195609Smp *
20195609Smp * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21195609Smp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22195609Smp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23195609Smp * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24195609Smp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25195609Smp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26195609Smp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27195609Smp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28195609Smp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29195609Smp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30195609Smp * SUCH DAMAGE.
31195609Smp */
32195609Smp
33195609Smp#ifndef lint
34195609Smp#if 0
35195609Smpstatic char sccsid[] = "@(#)expand.c	8.5 (Berkeley) 5/15/95";
36195609Smp#endif
37195609Smp#endif /* not lint */
38195609Smp#include <sys/cdefs.h>
39195609Smp__FBSDID("$FreeBSD: head/bin/sh/expand.c 206150 2010-04-03 22:04:44Z jilles $");
40195609Smp
41195609Smp#include <sys/types.h>
42195609Smp#include <sys/time.h>
43195609Smp#include <sys/stat.h>
44195609Smp#include <errno.h>
45195609Smp#include <dirent.h>
46195609Smp#include <unistd.h>
47195609Smp#include <pwd.h>
48195609Smp#include <stdlib.h>
49195609Smp#include <limits.h>
50195609Smp#include <stdio.h>
51195609Smp#include <string.h>
52195609Smp
53195609Smp/*
54195609Smp * Routines to expand arguments to commands.  We have to deal with
55195609Smp * backquotes, shell variables, and file metacharacters.
56195609Smp */
57195609Smp
58195609Smp#include "shell.h"
59195609Smp#include "main.h"
60195609Smp#include "nodes.h"
61195609Smp#include "eval.h"
62195609Smp#include "expand.h"
63195609Smp#include "syntax.h"
64195609Smp#include "parser.h"
65195609Smp#include "jobs.h"
66195609Smp#include "options.h"
67195609Smp#include "var.h"
68195609Smp#include "input.h"
69195609Smp#include "output.h"
70195609Smp#include "memalloc.h"
71195609Smp#include "error.h"
72167465Smp#include "mystring.h"
73167465Smp#include "arith.h"
74167465Smp#include "show.h"
75167465Smp
76167465Smp/*
77167465Smp * Structure specifying which parts of the string should be searched
78167465Smp * for IFS characters.
79167465Smp */
80167465Smp
81167465Smpstruct ifsregion {
82167465Smp	struct ifsregion *next;	/* next region in list */
83167465Smp	int begoff;		/* offset of start of region */
84167465Smp	int endoff;		/* offset of end of region */
85167465Smp	int inquotes;		/* search for nul bytes only */
86167465Smp};
87167465Smp
88167465Smp
89167465SmpSTATIC char *expdest;			/* output of current string */
90167465SmpSTATIC struct nodelist *argbackq;	/* list of back quote expressions */
91167465SmpSTATIC struct ifsregion ifsfirst;	/* first struct in list of ifs regions */
92167465SmpSTATIC struct ifsregion *ifslastp;	/* last struct in list */
93167465SmpSTATIC struct arglist exparg;		/* holds expanded arg list */
94167465Smp
95167465SmpSTATIC void argstr(char *, int);
96167465SmpSTATIC char *exptilde(char *, int);
97167465SmpSTATIC void expbackq(union node *, int, int);
98167465SmpSTATIC int subevalvar(char *, char *, int, int, int, int);
99167465SmpSTATIC char *evalvar(char *, int);
100167465SmpSTATIC int varisset(char *, int);
101167465SmpSTATIC void varvalue(char *, int, int, int);
102167465SmpSTATIC void recordregion(int, int, int);
103167465SmpSTATIC void removerecordregions(int);
104167465SmpSTATIC void ifsbreakup(char *, struct arglist *);
105167465SmpSTATIC void expandmeta(struct strlist *, int);
106167465SmpSTATIC void expmeta(char *, char *);
107167465SmpSTATIC void addfname(char *);
108167465SmpSTATIC struct strlist *expsort(struct strlist *);
109167465SmpSTATIC struct strlist *msort(struct strlist *, int);
110167465SmpSTATIC int pmatch(const char *, const char *, int);
111167465SmpSTATIC char *cvtnum(int, char *);
112167465SmpSTATIC int collate_range_cmp(int, int);
113167465Smp
114167465SmpSTATIC int
115167465Smpcollate_range_cmp(int c1, int c2)
116167465Smp{
117167465Smp	static char s1[2], s2[2];
118167465Smp
119167465Smp	s1[0] = c1;
120167465Smp	s2[0] = c2;
121167465Smp	return (strcoll(s1, s2));
122167465Smp}
123167465Smp
124167465Smp/*
125167465Smp * Expand shell variables and backquotes inside a here document.
126167465Smp *	union node *arg		the document
127167465Smp *	int fd;			where to write the expanded version
128167465Smp */
129167465Smp
130167465Smpvoid
131167465Smpexpandhere(union node *arg, int fd)
132167465Smp{
133167465Smp	herefd = fd;
134167465Smp	expandarg(arg, (struct arglist *)NULL, 0);
135167465Smp	xwrite(fd, stackblock(), expdest - stackblock());
136167465Smp}
137167465Smp
138167465Smp
139167465Smp/*
140167465Smp * Perform variable substitution and command substitution on an argument,
141167465Smp * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
142167465Smp * perform splitting and file name expansion.  When arglist is NULL, perform
143167465Smp * here document expansion.
144167465Smp */
145167465Smp
146167465Smpvoid
147167465Smpexpandarg(union node *arg, struct arglist *arglist, int flag)
148167465Smp{
149167465Smp	struct strlist *sp;
150167465Smp	char *p;
151167465Smp
152167465Smp	argbackq = arg->narg.backquote;
153167465Smp	STARTSTACKSTR(expdest);
154167465Smp	ifsfirst.next = NULL;
155167465Smp	ifslastp = NULL;
156167465Smp	argstr(arg->narg.text, flag);
157167465Smp	if (arglist == NULL) {
158167465Smp		return;			/* here document expanded */
159167465Smp	}
160167465Smp	STPUTC('\0', expdest);
161167465Smp	p = grabstackstr(expdest);
162167465Smp	exparg.lastp = &exparg.list;
163167465Smp	/*
164167465Smp	 * TODO - EXP_REDIR
165167465Smp	 */
166167465Smp	if (flag & EXP_FULL) {
167167465Smp		ifsbreakup(p, &exparg);
168167465Smp		*exparg.lastp = NULL;
169167465Smp		exparg.lastp = &exparg.list;
170167465Smp		expandmeta(exparg.list, flag);
171145479Smp	} else {
172145479Smp		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
173145479Smp			rmescapes(p);
174145479Smp		sp = (struct strlist *)stalloc(sizeof (struct strlist));
175145479Smp		sp->text = p;
176145479Smp		*exparg.lastp = sp;
177145479Smp		exparg.lastp = &sp->next;
178145479Smp	}
179145479Smp	while (ifsfirst.next != NULL) {
180145479Smp		struct ifsregion *ifsp;
181145479Smp		INTOFF;
182145479Smp		ifsp = ifsfirst.next->next;
183145479Smp		ckfree(ifsfirst.next);
184145479Smp		ifsfirst.next = ifsp;
185145479Smp		INTON;
186145479Smp	}
187145479Smp	*exparg.lastp = NULL;
188145479Smp	if (exparg.list) {
189145479Smp		*arglist->lastp = exparg.list;
190145479Smp		arglist->lastp = exparg.lastp;
191145479Smp	}
192145479Smp}
193145479Smp
194145479Smp
195145479Smp
196145479Smp/*
197145479Smp * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
198145479Smp * characters to allow for further processing.  Otherwise treat
199145479Smp * $@ like $* since no splitting will be performed.
200145479Smp */
201145479Smp
202145479SmpSTATIC void
203145479Smpargstr(char *p, int flag)
204145479Smp{
205145479Smp	char c;
206145479Smp	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);	/* do CTLESC */
207145479Smp	int firsteq = 1;
208145479Smp
209145479Smp	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
210145479Smp		p = exptilde(p, flag);
211145479Smp	for (;;) {
212145479Smp		switch (c = *p++) {
213145479Smp		case '\0':
214145479Smp		case CTLENDVAR: /* ??? */
215145479Smp			goto breakloop;
216145479Smp		case CTLQUOTEMARK:
217145479Smp			/* "$@" syntax adherence hack */
218145479Smp			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
219145479Smp				break;
220145479Smp			if ((flag & EXP_FULL) != 0)
221145479Smp				STPUTC(c, expdest);
222145479Smp			break;
223145479Smp		case CTLESC:
224145479Smp			if (quotes)
225145479Smp				STPUTC(c, expdest);
226145479Smp			c = *p++;
227145479Smp			STPUTC(c, expdest);
228145479Smp			break;
229145479Smp		case CTLVAR:
230145479Smp			p = evalvar(p, flag);
231145479Smp			break;
232145479Smp		case CTLBACKQ:
233145479Smp		case CTLBACKQ|CTLQUOTE:
234145479Smp			expbackq(argbackq->n, c & CTLQUOTE, flag);
235145479Smp			argbackq = argbackq->next;
236145479Smp			break;
237145479Smp		case CTLENDARI:
238145479Smp			expari(flag);
239145479Smp			break;
240145479Smp		case ':':
241145479Smp		case '=':
242145479Smp			/*
243145479Smp			 * sort of a hack - expand tildes in variable
244145479Smp			 * assignments (after the first '=' and after ':'s).
245145479Smp			 */
246145479Smp			STPUTC(c, expdest);
247145479Smp			if (flag & EXP_VARTILDE && *p == '~') {
248145479Smp				if (c == '=') {
249145479Smp					if (firsteq)
250145479Smp						firsteq = 0;
251145479Smp					else
252145479Smp						break;
253145479Smp				}
254145479Smp				p = exptilde(p, flag);
255145479Smp			}
256145479Smp			break;
257145479Smp		default:
258145479Smp			STPUTC(c, expdest);
259145479Smp		}
260145479Smp	}
261131962Smpbreakloop:;
262131962Smp}
263131962Smp
264131962SmpSTATIC char *
265131962Smpexptilde(char *p, int flag)
266131962Smp{
267131962Smp	char c, *startp = p;
268131962Smp	struct passwd *pw;
269131962Smp	char *home;
270131962Smp	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
271131962Smp
272131962Smp	while ((c = *p) != '\0') {
273131962Smp		switch(c) {
274131962Smp		case CTLESC: /* This means CTL* are always considered quoted. */
275131962Smp		case CTLVAR:
276131962Smp		case CTLBACKQ:
277131962Smp		case CTLBACKQ | CTLQUOTE:
278131962Smp		case CTLARI:
279131962Smp		case CTLENDARI:
280131962Smp		case CTLQUOTEMARK:
281131962Smp			return (startp);
282131962Smp		case ':':
283131962Smp			if (flag & EXP_VARTILDE)
284131962Smp				goto done;
285131962Smp			break;
286131962Smp		case '/':
287131962Smp		case CTLENDVAR:
288131962Smp			goto done;
289131962Smp		}
290131962Smp		p++;
291131962Smp	}
292131962Smpdone:
293131962Smp	*p = '\0';
294131962Smp	if (*(startp+1) == '\0') {
295131962Smp		if ((home = lookupvar("HOME")) == NULL)
296131962Smp			goto lose;
297131962Smp	} else {
298131962Smp		if ((pw = getpwnam(startp+1)) == NULL)
299131962Smp			goto lose;
300131962Smp		home = pw->pw_dir;
301131962Smp	}
302131962Smp	if (*home == '\0')
303131962Smp		goto lose;
304100616Smp	*p = c;
305100616Smp	while ((c = *home++) != '\0') {
306100616Smp		if (quotes && SQSYNTAX[(int)c] == CCTL)
307100616Smp			STPUTC(CTLESC, expdest);
308100616Smp		STPUTC(c, expdest);
309100616Smp	}
310100616Smp	return (p);
311100616Smplose:
312100616Smp	*p = c;
313100616Smp	return (startp);
314100616Smp}
315100616Smp
316100616Smp
317100616SmpSTATIC void
318100616Smpremoverecordregions(int endoff)
319100616Smp{
320100616Smp	if (ifslastp == NULL)
321100616Smp		return;
322100616Smp
323100616Smp	if (ifsfirst.endoff > endoff) {
324100616Smp		while (ifsfirst.next != NULL) {
325100616Smp			struct ifsregion *ifsp;
326100616Smp			INTOFF;
327100616Smp			ifsp = ifsfirst.next->next;
328100616Smp			ckfree(ifsfirst.next);
329100616Smp			ifsfirst.next = ifsp;
330100616Smp			INTON;
331100616Smp		}
332100616Smp		if (ifsfirst.begoff > endoff)
333100616Smp			ifslastp = NULL;
334100616Smp		else {
335100616Smp			ifslastp = &ifsfirst;
336100616Smp			ifsfirst.endoff = endoff;
337100616Smp		}
338100616Smp		return;
339100616Smp	}
340100616Smp
341100616Smp	ifslastp = &ifsfirst;
342100616Smp	while (ifslastp->next && ifslastp->next->begoff < endoff)
343100616Smp		ifslastp=ifslastp->next;
344100616Smp	while (ifslastp->next != NULL) {
345100616Smp		struct ifsregion *ifsp;
346100616Smp		INTOFF;
347100616Smp		ifsp = ifslastp->next->next;
348100616Smp		ckfree(ifslastp->next);
349100616Smp		ifslastp->next = ifsp;
350100616Smp		INTON;
351100616Smp	}
352100616Smp	if (ifslastp->endoff > endoff)
353100616Smp		ifslastp->endoff = endoff;
354100616Smp}
355100616Smp
356100616Smp/*
357100616Smp * Expand arithmetic expression.  Backup to start of expression,
358100616Smp * evaluate, place result in (backed up) result, adjust string position.
359100616Smp */
360100616Smpvoid
361100616Smpexpari(int flag)
362100616Smp{
363100616Smp	char *p, *start;
364100616Smp	arith_t result;
365100616Smp	int begoff;
366100616Smp	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
367100616Smp	int quoted;
368100616Smp
369100616Smp
370100616Smp	/*
37183098Smp	 * This routine is slightly over-complicated for
37283098Smp	 * efficiency.  First we make sure there is
37383098Smp	 * enough space for the result, which may be bigger
37483098Smp	 * than the expression if we add exponentiation.  Next we
37583098Smp	 * scan backwards looking for the start of arithmetic.  If the
376100616Smp	 * next previous character is a CTLESC character, then we
37783098Smp	 * have to rescan starting from the beginning since CTLESC
37883098Smp	 * characters have to be processed left to right.
37983098Smp	 */
38083098Smp	CHECKSTRSPACE(DIGITS(result) - 2, expdest);
38183098Smp	USTPUTC('\0', expdest);
38283098Smp	start = stackblock();
38383098Smp	p = expdest - 2;
38483098Smp	while (p >= start && *p != CTLARI)
38583098Smp		--p;
38683098Smp	if (p < start || *p != CTLARI)
38783098Smp		error("missing CTLARI (shouldn't happen)");
38883098Smp	if (p > start && *(p - 1) == CTLESC)
38983098Smp		for (p = start; *p != CTLARI; p++)
39083098Smp			if (*p == CTLESC)
39183098Smp				p++;
39283098Smp
39383098Smp	if (p[1] == '"')
39483098Smp		quoted=1;
39583098Smp	else
39683098Smp		quoted=0;
39783098Smp	begoff = p - start;
39883098Smp	removerecordregions(begoff);
39983098Smp	if (quotes)
40083098Smp		rmescapes(p+2);
40183098Smp	result = arith(p+2);
40283098Smp	fmtstr(p, DIGITS(result), ARITH_FORMAT_STR, result);
40383098Smp	while (*p++)
40483098Smp		;
40583098Smp	if (quoted == 0)
40683098Smp		recordregion(begoff, p - 1 - start, 0);
40783098Smp	result = expdest - p + 1;
40883098Smp	STADJUST(-result, expdest);
40983098Smp}
41083098Smp
41183098Smp
41283098Smp/*
41383098Smp * Expand stuff in backwards quotes.
41483098Smp */
41569408Sache
41669408SacheSTATIC void
41769408Sacheexpbackq(union node *cmd, int quoted, int flag)
41869408Sache{
41969408Sache	struct backcmd in;
42069408Sache	int i;
42169408Sache	char buf[128];
42269408Sache	char *p;
42369408Sache	char *dest = expdest;
42469408Sache	struct ifsregion saveifs, *savelastp;
42569408Sache	struct nodelist *saveargbackq;
42669408Sache	char lastc;
42769408Sache	int startloc = dest - stackblock();
42869408Sache	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
42969408Sache	int saveherefd;
43069408Sache	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
43169408Sache	int nnl;
43269408Sache
43369408Sache	INTOFF;
43469408Sache	saveifs = ifsfirst;
43569408Sache	savelastp = ifslastp;
43669408Sache	saveargbackq = argbackq;
43769408Sache	saveherefd = herefd;
43869408Sache	herefd = -1;
43969408Sache	p = grabstackstr(dest);
44069408Sache	evalbackcmd(cmd, &in);
44169408Sache	ungrabstackstr(p, dest);
44269408Sache	ifsfirst = saveifs;
44369408Sache	ifslastp = savelastp;
44469408Sache	argbackq = saveargbackq;
44569408Sache	herefd = saveherefd;
44669408Sache
44769408Sache	p = in.buf;
44869408Sache	lastc = '\0';
44969408Sache	nnl = 0;
45069408Sache	/* Don't copy trailing newlines */
45169408Sache	for (;;) {
45269408Sache		if (--in.nleft < 0) {
45369408Sache			if (in.fd < 0)
45469408Sache				break;
45569408Sache			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
45669408Sache			TRACE(("expbackq: read returns %d\n", i));
45769408Sache			if (i <= 0)
45869408Sache				break;
45969408Sache			p = buf;
46069408Sache			in.nleft = i - 1;
46169408Sache		}
46269408Sache		lastc = *p++;
46369408Sache		if (lastc != '\0') {
46469408Sache			if (quotes && syntax[(int)lastc] == CCTL)
46569408Sache				STPUTC(CTLESC, dest);
46669408Sache			if (lastc == '\n') {
46769408Sache				nnl++;
46869408Sache			} else {
46969408Sache				while (nnl > 0) {
47069408Sache					nnl--;
47169408Sache					STPUTC('\n', dest);
47269408Sache				}
47359415Sobrien				STPUTC(lastc, dest);
47459415Sobrien			}
47569408Sache		}
47669408Sache	}
47769408Sache
47869408Sache	if (in.fd >= 0)
47969408Sache		close(in.fd);
48069408Sache	if (in.buf)
48159243Sobrien		ckfree(in.buf);
48259243Sobrien	if (in.jp)
48359243Sobrien		exitstatus = waitforjob(in.jp, (int *)NULL);
48459243Sobrien	if (quoted == 0)
48559243Sobrien		recordregion(startloc, dest - stackblock(), 0);
48659243Sobrien	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
48759243Sobrien		(dest - stackblock()) - startloc,
48859243Sobrien		(dest - stackblock()) - startloc,
48959243Sobrien		stackblock() + startloc));
49059243Sobrien	expdest = dest;
49159243Sobrien	INTON;
49259243Sobrien}
49359243Sobrien
49459243Sobrien
49559243Sobrien
49659243SobrienSTATIC int
49759243Sobriensubevalvar(char *p, char *str, int strloc, int subtype, int startloc,
49859243Sobrien  int varflags)
49959243Sobrien{
50059243Sobrien	char *startp;
50159243Sobrien	char *loc = NULL;
50259243Sobrien	char *q;
50359243Sobrien	int c = 0;
50459243Sobrien	int saveherefd = herefd;
50559243Sobrien	struct nodelist *saveargbackq = argbackq;
50659243Sobrien	int amount;
50759243Sobrien
50859243Sobrien	herefd = -1;
50959243Sobrien	argstr(p, (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX ||
51059243Sobrien	    subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX ?
51159243Sobrien	    EXP_CASE : 0) | EXP_TILDE);
51259243Sobrien	STACKSTRNUL(expdest);
51359243Sobrien	herefd = saveherefd;
51459243Sobrien	argbackq = saveargbackq;
51559243Sobrien	startp = stackblock() + startloc;
51659243Sobrien	if (str == NULL)
51759243Sobrien	    str = stackblock() + strloc;
51859243Sobrien
51959243Sobrien	switch (subtype) {
52059243Sobrien	case VSASSIGN:
52159243Sobrien		setvar(str, startp, 0);
52259243Sobrien		amount = startp - expdest;
52359243Sobrien		STADJUST(amount, expdest);
52459243Sobrien		varflags &= ~VSNUL;
52559243Sobrien		if (c != 0)
52659243Sobrien			*loc = c;
52759243Sobrien		return 1;
52859243Sobrien
52959243Sobrien	case VSQUESTION:
53059243Sobrien		if (*p != CTLENDVAR) {
53159243Sobrien			outfmt(out2, "%s\n", startp);
53259243Sobrien			error((char *)NULL);
53359243Sobrien		}
53459243Sobrien		error("%.*s: parameter %snot set", (int)(p - str - 1),
53559243Sobrien		      str, (varflags & VSNUL) ? "null or "
53659243Sobrien					      : nullstr);
53759243Sobrien		return 0;
53859243Sobrien
53959243Sobrien	case VSTRIMLEFT:
54059243Sobrien		for (loc = startp; loc < str; loc++) {
54159243Sobrien			c = *loc;
54259243Sobrien			*loc = '\0';
54359243Sobrien			if (patmatch(str, startp, varflags & VSQUOTE)) {
54459243Sobrien				*loc = c;
54559243Sobrien				goto recordleft;
54659243Sobrien			}
54759243Sobrien			*loc = c;
54859243Sobrien			if ((varflags & VSQUOTE) && *loc == CTLESC)
54959243Sobrien				loc++;
55059243Sobrien		}
55159243Sobrien		return 0;
55259243Sobrien
55359243Sobrien	case VSTRIMLEFTMAX:
55459243Sobrien		for (loc = str - 1; loc >= startp;) {
55559243Sobrien			c = *loc;
55659243Sobrien			*loc = '\0';
55759243Sobrien			if (patmatch(str, startp, varflags & VSQUOTE)) {
55859243Sobrien				*loc = c;
55959243Sobrien				goto recordleft;
56059243Sobrien			}
56159243Sobrien			*loc = c;
56259243Sobrien			loc--;
56359243Sobrien			if ((varflags & VSQUOTE) && loc > startp &&
56459243Sobrien			    *(loc - 1) == CTLESC) {
56559243Sobrien				for (q = startp; q < loc; q++)
56659243Sobrien					if (*q == CTLESC)
56759243Sobrien						q++;
56859243Sobrien				if (q > loc)
56959243Sobrien					loc--;
57059243Sobrien			}
57159243Sobrien		}
57259243Sobrien		return 0;
57359243Sobrien
57459243Sobrien	case VSTRIMRIGHT:
57559243Sobrien		for (loc = str - 1; loc >= startp;) {
57659243Sobrien			if (patmatch(str, loc, varflags & VSQUOTE)) {
57759243Sobrien				amount = loc - expdest;
57859243Sobrien				STADJUST(amount, expdest);
57959243Sobrien				return 1;
58059243Sobrien			}
58159243Sobrien			loc--;
58259243Sobrien			if ((varflags & VSQUOTE) && loc > startp &&
58359243Sobrien			    *(loc - 1) == CTLESC) {
58459243Sobrien				for (q = startp; q < loc; q++)
58559243Sobrien					if (*q == CTLESC)
58659243Sobrien						q++;
58759243Sobrien				if (q > loc)
58859243Sobrien					loc--;
58959243Sobrien			}
59059243Sobrien		}
59159243Sobrien		return 0;
59259243Sobrien
59359243Sobrien	case VSTRIMRIGHTMAX:
59459243Sobrien		for (loc = startp; loc < str - 1; loc++) {
59559243Sobrien			if (patmatch(str, loc, varflags & VSQUOTE)) {
59659243Sobrien				amount = loc - expdest;
59759243Sobrien				STADJUST(amount, expdest);
59859243Sobrien				return 1;
59959243Sobrien			}
60059243Sobrien			if ((varflags & VSQUOTE) && *loc == CTLESC)
60159243Sobrien				loc++;
60259243Sobrien		}
60359243Sobrien		return 0;
60459243Sobrien
60559243Sobrien
60659243Sobrien	default:
60759243Sobrien		abort();
60859243Sobrien	}
60959243Sobrien
61059243Sobrienrecordleft:
61159243Sobrien	amount = ((str - 1) - (loc - startp)) - expdest;
61259243Sobrien	STADJUST(amount, expdest);
61359243Sobrien	while (loc != str - 1)
61459243Sobrien		*startp++ = *loc++;
61559243Sobrien	return 1;
61659243Sobrien}
61759243Sobrien
61859243Sobrien
61959243Sobrien/*
62059243Sobrien * Expand a variable, and return a pointer to the next character in the
62159243Sobrien * input string.
62259243Sobrien */
62359243Sobrien
62459243SobrienSTATIC char *
62559243Sobrienevalvar(char *p, int flag)
62659243Sobrien{
62759243Sobrien	int subtype;
62859243Sobrien	int varflags;
62959243Sobrien	char *var;
63059243Sobrien	char *val;
63159243Sobrien	int patloc;
63259243Sobrien	int c;
63359243Sobrien	int set;
63459243Sobrien	int special;
63559243Sobrien	int startloc;
63659243Sobrien	int varlen;
63759243Sobrien	int easy;
63859243Sobrien	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
63959243Sobrien
64059243Sobrien	varflags = (unsigned char)*p++;
64159243Sobrien	subtype = varflags & VSTYPE;
64259243Sobrien	var = p;
64359243Sobrien	special = 0;
64459243Sobrien	if (! is_name(*p))
64559243Sobrien		special = 1;
64659243Sobrien	p = strchr(p, '=') + 1;
64759243Sobrienagain: /* jump here after setting a variable with ${var=text} */
64859243Sobrien	if (varflags & VSLINENO) {
64959243Sobrien		set = 1;
65059243Sobrien		special = 0;
65159243Sobrien		val = var;
65259243Sobrien		p[-1] = '\0';	/* temporarily overwrite '=' to have \0
65359243Sobrien				   terminated string */
65459243Sobrien	} else if (special) {
65559243Sobrien		set = varisset(var, varflags & VSNUL);
65659243Sobrien		val = NULL;
65759243Sobrien	} else {
65859243Sobrien		val = bltinlookup(var, 1);
65959243Sobrien		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
66059243Sobrien			val = NULL;
66159243Sobrien			set = 0;
66259243Sobrien		} else
66359243Sobrien			set = 1;
66459243Sobrien	}
66559243Sobrien	varlen = 0;
66659243Sobrien	startloc = expdest - stackblock();
66759243Sobrien	if (!set && uflag && *var != '@' && *var != '*') {
66859243Sobrien		switch (subtype) {
66959243Sobrien		case VSNORMAL:
67059243Sobrien		case VSTRIMLEFT:
67159243Sobrien		case VSTRIMLEFTMAX:
67259243Sobrien		case VSTRIMRIGHT:
67359243Sobrien		case VSTRIMRIGHTMAX:
67459243Sobrien		case VSLENGTH:
67559243Sobrien			error("%.*s: parameter not set", (int)(p - var - 1),
67659243Sobrien			    var);
67759243Sobrien		}
67859243Sobrien	}
67959243Sobrien	if (set && subtype != VSPLUS) {
68059243Sobrien		/* insert the value of the variable */
68159243Sobrien		if (special) {
68259243Sobrien			varvalue(var, varflags & VSQUOTE, subtype, flag);
68359243Sobrien			if (subtype == VSLENGTH) {
68459243Sobrien				varlen = expdest - stackblock() - startloc;
68559243Sobrien				STADJUST(-varlen, expdest);
68659243Sobrien			}
68759243Sobrien		} else {
68859243Sobrien			char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
68959243Sobrien								  : BASESYNTAX;
69059243Sobrien
69159243Sobrien			if (subtype == VSLENGTH) {
69259243Sobrien				for (;*val; val++)
69359243Sobrien					varlen++;
69459243Sobrien			}
69559243Sobrien			else {
69659243Sobrien				while (*val) {
69759243Sobrien					if (quotes &&
69859243Sobrien					    syntax[(int)*val] == CCTL)
69959243Sobrien						STPUTC(CTLESC, expdest);
70059243Sobrien					STPUTC(*val++, expdest);
70159243Sobrien				}
70259243Sobrien
70359243Sobrien			}
70459243Sobrien		}
70559243Sobrien	}
70659243Sobrien
70759243Sobrien	if (subtype == VSPLUS)
70859243Sobrien		set = ! set;
70959243Sobrien
71059243Sobrien	easy = ((varflags & VSQUOTE) == 0 ||
71159243Sobrien		(*var == '@' && shellparam.nparam != 1));
71259243Sobrien
71359243Sobrien
71459243Sobrien	switch (subtype) {
71559243Sobrien	case VSLENGTH:
71659243Sobrien		expdest = cvtnum(varlen, expdest);
71759243Sobrien		goto record;
71859243Sobrien
71959243Sobrien	case VSNORMAL:
72059243Sobrien		if (!easy)
72159243Sobrien			break;
72259243Sobrienrecord:
72359243Sobrien		recordregion(startloc, expdest - stackblock(),
72459243Sobrien			     varflags & VSQUOTE);
72559243Sobrien		break;
72659243Sobrien
72759243Sobrien	case VSPLUS:
72859243Sobrien	case VSMINUS:
72959243Sobrien		if (!set) {
73059243Sobrien			argstr(p, flag);
73159243Sobrien			break;
73259243Sobrien		}
73359243Sobrien		if (easy)
73459243Sobrien			goto record;
73559243Sobrien		break;
73659243Sobrien
73759243Sobrien	case VSTRIMLEFT:
73859243Sobrien	case VSTRIMLEFTMAX:
73959243Sobrien	case VSTRIMRIGHT:
74059243Sobrien	case VSTRIMRIGHTMAX:
74159243Sobrien		if (!set)
74259243Sobrien			break;
74359243Sobrien		/*
74459243Sobrien		 * Terminate the string and start recording the pattern
74559243Sobrien		 * right after it
74659243Sobrien		 */
74759243Sobrien		STPUTC('\0', expdest);
74859243Sobrien		patloc = expdest - stackblock();
74959243Sobrien		if (subevalvar(p, NULL, patloc, subtype,
75059243Sobrien			       startloc, varflags) == 0) {
75159243Sobrien			int amount = (expdest - stackblock() - patloc) + 1;
75259243Sobrien			STADJUST(-amount, expdest);
75359243Sobrien		}
75459243Sobrien		/* Remove any recorded regions beyond start of variable */
75559243Sobrien		removerecordregions(startloc);
75659243Sobrien		goto record;
75759243Sobrien
75859243Sobrien	case VSASSIGN:
75959243Sobrien	case VSQUESTION:
76059243Sobrien		if (!set) {
76159243Sobrien			if (subevalvar(p, var, 0, subtype, startloc, varflags)) {
76259243Sobrien				varflags &= ~VSNUL;
76359243Sobrien				/*
76459243Sobrien				 * Remove any recorded regions beyond
76559243Sobrien				 * start of variable
76659243Sobrien				 */
76759243Sobrien				removerecordregions(startloc);
76859243Sobrien				goto again;
76959243Sobrien			}
77059243Sobrien			break;
77159243Sobrien		}
77259243Sobrien		if (easy)
77359243Sobrien			goto record;
77459243Sobrien		break;
77559243Sobrien
77659243Sobrien	case VSERROR:
77759243Sobrien		c = p - var - 1;
77859243Sobrien		error("${%.*s%s}: Bad substitution", c, var,
77959243Sobrien		    (c > 0 && *p != CTLENDVAR) ? "..." : "");
78059243Sobrien
78159243Sobrien	default:
78259243Sobrien		abort();
78359243Sobrien	}
78459243Sobrien	p[-1] = '=';	/* recover overwritten '=' */
78559243Sobrien
78659243Sobrien	if (subtype != VSNORMAL) {	/* skip to end of alternative */
78759243Sobrien		int nesting = 1;
78859243Sobrien		for (;;) {
78959243Sobrien			if ((c = *p++) == CTLESC)
79059243Sobrien				p++;
79159243Sobrien			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
79259243Sobrien				if (set)
79359243Sobrien					argbackq = argbackq->next;
79459243Sobrien			} else if (c == CTLVAR) {
79559243Sobrien				if ((*p++ & VSTYPE) != VSNORMAL)
79659243Sobrien					nesting++;
79759243Sobrien			} else if (c == CTLENDVAR) {
79859243Sobrien				if (--nesting == 0)
79959243Sobrien					break;
80059243Sobrien			}
80159243Sobrien		}
80259243Sobrien	}
80359243Sobrien	return p;
80459243Sobrien}
80559243Sobrien
80659243Sobrien
80759243Sobrien
80859243Sobrien/*
80959243Sobrien * Test whether a specialized variable is set.
81059243Sobrien */
81159243Sobrien
81259243SobrienSTATIC int
81359243Sobrienvarisset(char *name, int nulok)
81459243Sobrien{
81559243Sobrien
81659243Sobrien	if (*name == '!')
81759243Sobrien		return backgndpid != -1;
81859243Sobrien	else if (*name == '@' || *name == '*') {
81959243Sobrien		if (*shellparam.p == NULL)
82059243Sobrien			return 0;
82159243Sobrien
82259243Sobrien		if (nulok) {
82359243Sobrien			char **av;
82459243Sobrien
82559243Sobrien			for (av = shellparam.p; *av; av++)
82659243Sobrien				if (**av != '\0')
82759243Sobrien					return 1;
82859243Sobrien			return 0;
82959243Sobrien		}
83059243Sobrien	} else if (is_digit(*name)) {
83159243Sobrien		char *ap;
83259243Sobrien		int num = atoi(name);
83359243Sobrien
83459243Sobrien		if (num > shellparam.nparam)
83559243Sobrien			return 0;
83659243Sobrien
83759243Sobrien		if (num == 0)
83859243Sobrien			ap = arg0;
83959243Sobrien		else
84059243Sobrien			ap = shellparam.p[num - 1];
84159243Sobrien
84259243Sobrien		if (nulok && (ap == NULL || *ap == '\0'))
84359243Sobrien			return 0;
84459243Sobrien	}
84559243Sobrien	return 1;
84659243Sobrien}
84759243Sobrien
84859243Sobrien
84959243Sobrien
85059243Sobrien/*
85159243Sobrien * Add the value of a specialized variable to the stack string.
85259243Sobrien */
85359243Sobrien
85459243SobrienSTATIC void
85559243Sobrienvarvalue(char *name, int quoted, int subtype, int flag)
85659243Sobrien{
85759243Sobrien	int num;
85859243Sobrien	char *p;
85959243Sobrien	int i;
86059243Sobrien	char sep;
86159243Sobrien	char **ap;
86259243Sobrien	char const *syntax;
86359243Sobrien
86459243Sobrien#define STRTODEST(p) \
86559243Sobrien	do {\
86659243Sobrien	if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) { \
86759243Sobrien		syntax = quoted? DQSYNTAX : BASESYNTAX; \
86859243Sobrien		while (*p) { \
86959243Sobrien			if (syntax[(int)*p] == CCTL) \
87059243Sobrien				STPUTC(CTLESC, expdest); \
87159243Sobrien			STPUTC(*p++, expdest); \
87259243Sobrien		} \
87359243Sobrien	} else \
87459243Sobrien		while (*p) \
87559243Sobrien			STPUTC(*p++, expdest); \
87659243Sobrien	} while (0)
87759243Sobrien
87859243Sobrien
87959243Sobrien	switch (*name) {
88059243Sobrien	case '$':
88159243Sobrien		num = rootpid;
88259243Sobrien		goto numvar;
88359243Sobrien	case '?':
88459243Sobrien		num = oexitstatus;
88559243Sobrien		goto numvar;
88659243Sobrien	case '#':
88759243Sobrien		num = shellparam.nparam;
88859243Sobrien		goto numvar;
88959243Sobrien	case '!':
89059243Sobrien		num = backgndpid;
89159243Sobriennumvar:
89259243Sobrien		expdest = cvtnum(num, expdest);
89359243Sobrien		break;
89459243Sobrien	case '-':
89559243Sobrien		for (i = 0 ; i < NOPTS ; i++) {
89659243Sobrien			if (optlist[i].val)
89759243Sobrien				STPUTC(optlist[i].letter, expdest);
89859243Sobrien		}
89959243Sobrien		break;
90059243Sobrien	case '@':
90159243Sobrien		if (flag & EXP_FULL && quoted) {
90259243Sobrien			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
90359243Sobrien				STRTODEST(p);
90459243Sobrien				if (*ap)
90559243Sobrien					STPUTC('\0', expdest);
90659243Sobrien			}
90759243Sobrien			break;
90859243Sobrien		}
90959243Sobrien		/* FALLTHROUGH */
91059243Sobrien	case '*':
91159243Sobrien		if (ifsset())
91259243Sobrien			sep = ifsval()[0];
91359243Sobrien		else
91459243Sobrien			sep = ' ';
91559243Sobrien		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
91659243Sobrien			STRTODEST(p);
91759243Sobrien			if (*ap && sep)
91859243Sobrien				STPUTC(sep, expdest);
91959243Sobrien		}
92059243Sobrien		break;
92159243Sobrien	case '0':
92259243Sobrien		p = arg0;
92359243Sobrien		STRTODEST(p);
92459243Sobrien		break;
92559243Sobrien	default:
92659243Sobrien		if (is_digit(*name)) {
92759243Sobrien			num = atoi(name);
92859243Sobrien			if (num > 0 && num <= shellparam.nparam) {
92959243Sobrien				p = shellparam.p[num - 1];
93059243Sobrien				STRTODEST(p);
93159243Sobrien			}
93259243Sobrien		}
93359243Sobrien		break;
93459243Sobrien	}
93559243Sobrien}
93659243Sobrien
93759243Sobrien
93859243Sobrien
93959243Sobrien/*
94059243Sobrien * Record the the fact that we have to scan this region of the
94159243Sobrien * string for IFS characters.
94259243Sobrien */
94359243Sobrien
94459243SobrienSTATIC void
94559243Sobrienrecordregion(int start, int end, int inquotes)
94659243Sobrien{
94759243Sobrien	struct ifsregion *ifsp;
94859243Sobrien
94959243Sobrien	if (ifslastp == NULL) {
95059243Sobrien		ifsp = &ifsfirst;
95159243Sobrien	} else {
95259243Sobrien		if (ifslastp->endoff == start
95359243Sobrien		    && ifslastp->inquotes == inquotes) {
95459243Sobrien			/* extend previous area */
95559243Sobrien			ifslastp->endoff = end;
95659243Sobrien			return;
95759243Sobrien		}
95859243Sobrien		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
95959243Sobrien		ifslastp->next = ifsp;
96059243Sobrien	}
96159243Sobrien	ifslastp = ifsp;
96259243Sobrien	ifslastp->next = NULL;
96359243Sobrien	ifslastp->begoff = start;
96459243Sobrien	ifslastp->endoff = end;
96559243Sobrien	ifslastp->inquotes = inquotes;
96659243Sobrien}
96759243Sobrien
96859243Sobrien
96959243Sobrien
97059243Sobrien/*
97159243Sobrien * Break the argument string into pieces based upon IFS and add the
97259243Sobrien * strings to the argument list.  The regions of the string to be
97359243Sobrien * searched for IFS characters have been stored by recordregion.
97459243Sobrien */
97559243SobrienSTATIC void
97659243Sobrienifsbreakup(char *string, struct arglist *arglist)
97759243Sobrien{
97859243Sobrien	struct ifsregion *ifsp;
97959243Sobrien	struct strlist *sp;
98059243Sobrien	char *start;
98159243Sobrien	char *p;
98259243Sobrien	char *q;
98359243Sobrien	const char *ifs;
98459243Sobrien	const char *ifsspc;
98559243Sobrien	int had_param_ch = 0;
98659243Sobrien
98759243Sobrien	start = string;
98859243Sobrien
98959243Sobrien	if (ifslastp == NULL) {
99059243Sobrien		/* Return entire argument, IFS doesn't apply to any of it */
99159243Sobrien		sp = (struct strlist *)stalloc(sizeof *sp);
99259243Sobrien		sp->text = start;
99359243Sobrien		*arglist->lastp = sp;
99459243Sobrien		arglist->lastp = &sp->next;
99559243Sobrien		return;
99659243Sobrien	}
99759243Sobrien
99859243Sobrien	ifs = ifsset() ? ifsval() : " \t\n";
99959243Sobrien
100059243Sobrien	for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
100159243Sobrien		p = string + ifsp->begoff;
100259243Sobrien		while (p < string + ifsp->endoff) {
100359243Sobrien			q = p;
100459243Sobrien			if (*p == CTLESC)
100559243Sobrien				p++;
100659243Sobrien			if (ifsp->inquotes) {
100759243Sobrien				/* Only NULs (should be from "$@") end args */
100859243Sobrien				had_param_ch = 1;
100959243Sobrien				if (*p != 0) {
101059243Sobrien					p++;
101159243Sobrien					continue;
101259243Sobrien				}
101359243Sobrien				ifsspc = NULL;
101459243Sobrien			} else {
101559243Sobrien				if (!strchr(ifs, *p)) {
101659243Sobrien					had_param_ch = 1;
101759243Sobrien					p++;
101859243Sobrien					continue;
101959243Sobrien				}
102059243Sobrien				ifsspc = strchr(" \t\n", *p);
102159243Sobrien
102259243Sobrien				/* Ignore IFS whitespace at start */
102359243Sobrien				if (q == start && ifsspc != NULL) {
102459243Sobrien					p++;
102559243Sobrien					start = p;
102659243Sobrien					continue;
102759243Sobrien				}
102859243Sobrien				had_param_ch = 0;
102959243Sobrien			}
103059243Sobrien
103159243Sobrien			/* Save this argument... */
103259243Sobrien			*q = '\0';
103359243Sobrien			sp = (struct strlist *)stalloc(sizeof *sp);
103459243Sobrien			sp->text = start;
103559243Sobrien			*arglist->lastp = sp;
103659243Sobrien			arglist->lastp = &sp->next;
103759243Sobrien			p++;
103859243Sobrien
103959243Sobrien			if (ifsspc != NULL) {
104059243Sobrien				/* Ignore further trailing IFS whitespace */
104159243Sobrien				for (; p < string + ifsp->endoff; p++) {
104259243Sobrien					q = p;
104359243Sobrien					if (*p == CTLESC)
104459243Sobrien						p++;
104559243Sobrien					if (strchr(ifs, *p) == NULL) {
104659243Sobrien						p = q;
104759243Sobrien						break;
104859243Sobrien					}
104959243Sobrien					if (strchr(" \t\n", *p) == NULL) {
105059243Sobrien						p++;
105159243Sobrien						break;
105259243Sobrien					}
105359243Sobrien				}
105459243Sobrien			}
105559243Sobrien			start = p;
105659243Sobrien		}
105759243Sobrien	}
105859243Sobrien
105959243Sobrien	/*
106059243Sobrien	 * Save anything left as an argument.
106159243Sobrien	 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
106259243Sobrien	 * generating 2 arguments, the second of which is empty.
106359243Sobrien	 * Some recent clarification of the Posix spec say that it
106459243Sobrien	 * should only generate one....
106559243Sobrien	 */
106659243Sobrien	if (had_param_ch || *start != 0) {
106759243Sobrien		sp = (struct strlist *)stalloc(sizeof *sp);
106859243Sobrien		sp->text = start;
106959243Sobrien		*arglist->lastp = sp;
107059243Sobrien		arglist->lastp = &sp->next;
107159243Sobrien	}
107259243Sobrien}
107359243Sobrien
107459243Sobrien
107559243Sobrien
107659243Sobrien/*
107759243Sobrien * Expand shell metacharacters.  At this point, the only control characters
107859243Sobrien * should be escapes.  The results are stored in the list exparg.
107959243Sobrien */
108059243Sobrien
108159243SobrienSTATIC char *expdir;
108259243Sobrien
108359243Sobrien
108459243SobrienSTATIC void
108559243Sobrienexpandmeta(struct strlist *str, int flag __unused)
108659243Sobrien{
108759243Sobrien	char *p;
108859243Sobrien	struct strlist **savelastp;
108959243Sobrien	struct strlist *sp;
109059243Sobrien	char c;
109159243Sobrien	/* TODO - EXP_REDIR */
109259243Sobrien
109359243Sobrien	while (str) {
109459243Sobrien		if (fflag)
109559243Sobrien			goto nometa;
109659243Sobrien		p = str->text;
109759243Sobrien		for (;;) {			/* fast check for meta chars */
109859243Sobrien			if ((c = *p++) == '\0')
109959243Sobrien				goto nometa;
110059243Sobrien			if (c == '*' || c == '?' || c == '[' || c == '!')
110159243Sobrien				break;
110259243Sobrien		}
110359243Sobrien		savelastp = exparg.lastp;
110459243Sobrien		INTOFF;
110559243Sobrien		if (expdir == NULL) {
110659243Sobrien			int i = strlen(str->text);
110759243Sobrien			expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
110859243Sobrien		}
110959243Sobrien
111059243Sobrien		expmeta(expdir, str->text);
111159243Sobrien		ckfree(expdir);
111259243Sobrien		expdir = NULL;
111359243Sobrien		INTON;
111459243Sobrien		if (exparg.lastp == savelastp) {
111559243Sobrien			/*
111659243Sobrien			 * no matches
111759243Sobrien			 */
111859243Sobriennometa:
111959243Sobrien			*exparg.lastp = str;
112059243Sobrien			rmescapes(str->text);
112159243Sobrien			exparg.lastp = &str->next;
112259243Sobrien		} else {
112359243Sobrien			*exparg.lastp = NULL;
112459243Sobrien			*savelastp = sp = expsort(*savelastp);
112559243Sobrien			while (sp->next != NULL)
112659243Sobrien				sp = sp->next;
112759243Sobrien			exparg.lastp = &sp->next;
112859243Sobrien		}
112959243Sobrien		str = str->next;
113059243Sobrien	}
113159243Sobrien}
113259243Sobrien
113359243Sobrien
113459243Sobrien/*
113559243Sobrien * Do metacharacter (i.e. *, ?, [...]) expansion.
113659243Sobrien */
113759243Sobrien
113859243SobrienSTATIC void
113959243Sobrienexpmeta(char *enddir, char *name)
114059243Sobrien{
114159243Sobrien	char *p;
114259243Sobrien	char *q;
114359243Sobrien	char *start;
114459243Sobrien	char *endname;
114559243Sobrien	int metaflag;
114659243Sobrien	struct stat statb;
114759243Sobrien	DIR *dirp;
114859243Sobrien	struct dirent *dp;
114959243Sobrien	int atend;
115059243Sobrien	int matchdot;
115159243Sobrien
115259243Sobrien	metaflag = 0;
115359243Sobrien	start = name;
115459243Sobrien	for (p = name ; ; p++) {
115559243Sobrien		if (*p == '*' || *p == '?')
115659243Sobrien			metaflag = 1;
115759243Sobrien		else if (*p == '[') {
115859243Sobrien			q = p + 1;
115959243Sobrien			if (*q == '!' || *q == '^')
116059243Sobrien				q++;
116159243Sobrien			for (;;) {
116259243Sobrien				while (*q == CTLQUOTEMARK)
116359243Sobrien					q++;
116459243Sobrien				if (*q == CTLESC)
116559243Sobrien					q++;
116659243Sobrien				if (*q == '/' || *q == '\0')
116759243Sobrien					break;
116859243Sobrien				if (*++q == ']') {
116959243Sobrien					metaflag = 1;
117059243Sobrien					break;
117159243Sobrien				}
117259243Sobrien			}
117359243Sobrien		} else if (*p == '!' && p[1] == '!'	&& (p == name || p[-1] == '/')) {
117459243Sobrien			metaflag = 1;
117559243Sobrien		} else if (*p == '\0')
117659243Sobrien			break;
117759243Sobrien		else if (*p == CTLQUOTEMARK)
117859243Sobrien			continue;
117959243Sobrien		else if (*p == CTLESC)
118059243Sobrien			p++;
118159243Sobrien		if (*p == '/') {
118259243Sobrien			if (metaflag)
118359243Sobrien				break;
118459243Sobrien			start = p + 1;
118559243Sobrien		}
118659243Sobrien	}
118759243Sobrien	if (metaflag == 0) {	/* we've reached the end of the file name */
118859243Sobrien		if (enddir != expdir)
118959243Sobrien			metaflag++;
119059243Sobrien		for (p = name ; ; p++) {
119159243Sobrien			if (*p == CTLQUOTEMARK)
119259243Sobrien				continue;
119359243Sobrien			if (*p == CTLESC)
119459243Sobrien				p++;
119559243Sobrien			*enddir++ = *p;
119659243Sobrien			if (*p == '\0')
119759243Sobrien				break;
119859243Sobrien		}
119959243Sobrien		if (metaflag == 0 || lstat(expdir, &statb) >= 0)
120059243Sobrien			addfname(expdir);
120159243Sobrien		return;
120259243Sobrien	}
120359243Sobrien	endname = p;
120459243Sobrien	if (start != name) {
120559243Sobrien		p = name;
120659243Sobrien		while (p < start) {
120759243Sobrien			while (*p == CTLQUOTEMARK)
120859243Sobrien				p++;
120959243Sobrien			if (*p == CTLESC)
121059243Sobrien				p++;
121159243Sobrien			*enddir++ = *p++;
121259243Sobrien		}
121359243Sobrien	}
121459243Sobrien	if (enddir == expdir) {
121559243Sobrien		p = ".";
121659243Sobrien	} else if (enddir == expdir + 1 && *expdir == '/') {
121759243Sobrien		p = "/";
121859243Sobrien	} else {
121959243Sobrien		p = expdir;
122059243Sobrien		enddir[-1] = '\0';
122159243Sobrien	}
122259243Sobrien	if ((dirp = opendir(p)) == NULL)
122359243Sobrien		return;
122459243Sobrien	if (enddir != expdir)
122559243Sobrien		enddir[-1] = '/';
122659243Sobrien	if (*endname == 0) {
122759243Sobrien		atend = 1;
122859243Sobrien	} else {
122959243Sobrien		atend = 0;
123059243Sobrien		*endname++ = '\0';
123159243Sobrien	}
123259243Sobrien	matchdot = 0;
123359243Sobrien	p = start;
123459243Sobrien	while (*p == CTLQUOTEMARK)
123559243Sobrien		p++;
123659243Sobrien	if (*p == CTLESC)
123759243Sobrien		p++;
123859243Sobrien	if (*p == '.')
123959243Sobrien		matchdot++;
124059243Sobrien	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
124159243Sobrien		if (dp->d_name[0] == '.' && ! matchdot)
124259243Sobrien			continue;
124359243Sobrien		if (patmatch(start, dp->d_name, 0)) {
124459243Sobrien			if (atend) {
124559243Sobrien				scopy(dp->d_name, enddir);
124659243Sobrien				addfname(expdir);
124759243Sobrien			} else {
124859243Sobrien				for (p = enddir, q = dp->d_name;
124959243Sobrien				     (*p++ = *q++) != '\0';)
125059243Sobrien					continue;
125159243Sobrien				p[-1] = '/';
125259243Sobrien				expmeta(p, endname);
125359243Sobrien			}
125459243Sobrien		}
125559243Sobrien	}
125659243Sobrien	closedir(dirp);
125759243Sobrien	if (! atend)
125859243Sobrien		endname[-1] = '/';
125959243Sobrien}
126059243Sobrien
126159243Sobrien
126259243Sobrien/*
126359243Sobrien * Add a file name to the list.
126459243Sobrien */
126559243Sobrien
126659243SobrienSTATIC void
126759243Sobrienaddfname(char *name)
126859243Sobrien{
126959243Sobrien	char *p;
127059243Sobrien	struct strlist *sp;
127159243Sobrien
127259243Sobrien	p = stalloc(strlen(name) + 1);
127359243Sobrien	scopy(name, p);
127459243Sobrien	sp = (struct strlist *)stalloc(sizeof *sp);
127559243Sobrien	sp->text = p;
127659243Sobrien	*exparg.lastp = sp;
127759243Sobrien	exparg.lastp = &sp->next;
127859243Sobrien}
127959243Sobrien
128059243Sobrien
128159243Sobrien/*
128259243Sobrien * Sort the results of file name expansion.  It calculates the number of
128359243Sobrien * strings to sort and then calls msort (short for merge sort) to do the
128459243Sobrien * work.
128559243Sobrien */
128659243Sobrien
128759243SobrienSTATIC struct strlist *
128859243Sobrienexpsort(struct strlist *str)
128959243Sobrien{
129059243Sobrien	int len;
129159243Sobrien	struct strlist *sp;
129259243Sobrien
129359243Sobrien	len = 0;
129459243Sobrien	for (sp = str ; sp ; sp = sp->next)
129559243Sobrien		len++;
129659243Sobrien	return msort(str, len);
129759243Sobrien}
129859243Sobrien
129959243Sobrien
130059243SobrienSTATIC struct strlist *
130159243Sobrienmsort(struct strlist *list, int len)
130259243Sobrien{
130359243Sobrien	struct strlist *p, *q = NULL;
130459243Sobrien	struct strlist **lpp;
130559243Sobrien	int half;
130659243Sobrien	int n;
130759243Sobrien
130859243Sobrien	if (len <= 1)
130959243Sobrien		return list;
131059243Sobrien	half = len >> 1;
131159243Sobrien	p = list;
131259243Sobrien	for (n = half ; --n >= 0 ; ) {
131359243Sobrien		q = p;
131459243Sobrien		p = p->next;
131559243Sobrien	}
131659243Sobrien	q->next = NULL;			/* terminate first half of list */
131759243Sobrien	q = msort(list, half);		/* sort first half of list */
131859243Sobrien	p = msort(p, len - half);		/* sort second half */
131959243Sobrien	lpp = &list;
132059243Sobrien	for (;;) {
132159243Sobrien		if (strcmp(p->text, q->text) < 0) {
132259243Sobrien			*lpp = p;
132359243Sobrien			lpp = &p->next;
132459243Sobrien			if ((p = *lpp) == NULL) {
132559243Sobrien				*lpp = q;
132659243Sobrien				break;
132759243Sobrien			}
132859243Sobrien		} else {
132959243Sobrien			*lpp = q;
133059243Sobrien			lpp = &q->next;
133159243Sobrien			if ((q = *lpp) == NULL) {
133259243Sobrien				*lpp = p;
133359243Sobrien				break;
133459243Sobrien			}
133559243Sobrien		}
133659243Sobrien	}
133759243Sobrien	return list;
133859243Sobrien}
133959243Sobrien
134059243Sobrien
134159243Sobrien
134259243Sobrien/*
134359243Sobrien * Returns true if the pattern matches the string.
134459243Sobrien */
134559243Sobrien
134659243Sobrienint
134759243Sobrienpatmatch(const char *pattern, const char *string, int squoted)
134859243Sobrien{
134959243Sobrien#ifdef notdef
135059243Sobrien	if (pattern[0] == '!' && pattern[1] == '!')
135159243Sobrien		return 1 - pmatch(pattern + 2, string);
135259243Sobrien	else
135359243Sobrien#endif
135459243Sobrien		return pmatch(pattern, string, squoted);
135559243Sobrien}
135659243Sobrien
135759243Sobrien
135859243SobrienSTATIC int
135959243Sobrienpmatch(const char *pattern, const char *string, int squoted)
136059243Sobrien{
136159243Sobrien	const char *p, *q;
136259243Sobrien	char c;
136359243Sobrien
136459243Sobrien	p = pattern;
136559243Sobrien	q = string;
136659243Sobrien	for (;;) {
136759243Sobrien		switch (c = *p++) {
136859243Sobrien		case '\0':
136959243Sobrien			goto breakloop;
137059243Sobrien		case CTLESC:
137159243Sobrien			if (squoted && *q == CTLESC)
137259243Sobrien				q++;
137359243Sobrien			if (*q++ != *p++)
137459243Sobrien				return 0;
137559243Sobrien			break;
137659243Sobrien		case CTLQUOTEMARK:
137759243Sobrien			continue;
137859243Sobrien		case '?':
137959243Sobrien			if (squoted && *q == CTLESC)
138059243Sobrien				q++;
138159243Sobrien			if (*q++ == '\0')
138259243Sobrien				return 0;
138359243Sobrien			break;
138459243Sobrien		case '*':
138559243Sobrien			c = *p;
138659243Sobrien			while (c == CTLQUOTEMARK || c == '*')
138759243Sobrien				c = *++p;
138859243Sobrien			if (c != CTLESC &&  c != CTLQUOTEMARK &&
138959243Sobrien			    c != '?' && c != '*' && c != '[') {
139059243Sobrien				while (*q != c) {
139159243Sobrien					if (squoted && *q == CTLESC &&
139259243Sobrien					    q[1] == c)
139359243Sobrien						break;
139459243Sobrien					if (*q == '\0')
139559243Sobrien						return 0;
139659243Sobrien					if (squoted && *q == CTLESC)
139759243Sobrien						q++;
139859243Sobrien					q++;
139959243Sobrien				}
140059243Sobrien			}
140159243Sobrien			do {
140259243Sobrien				if (pmatch(p, q, squoted))
140359243Sobrien					return 1;
140459243Sobrien				if (squoted && *q == CTLESC)
140559243Sobrien					q++;
140659243Sobrien			} while (*q++ != '\0');
140759243Sobrien			return 0;
140859243Sobrien		case '[': {
140959243Sobrien			const char *endp;
141059243Sobrien			int invert, found;
141159243Sobrien			char chr;
141259243Sobrien
141359243Sobrien			endp = p;
141459243Sobrien			if (*endp == '!' || *endp == '^')
141559243Sobrien				endp++;
141659243Sobrien			for (;;) {
141759243Sobrien				while (*endp == CTLQUOTEMARK)
141859243Sobrien					endp++;
141959243Sobrien				if (*endp == '\0')
142059243Sobrien					goto dft;		/* no matching ] */
142159243Sobrien				if (*endp == CTLESC)
142259243Sobrien					endp++;
142359243Sobrien				if (*++endp == ']')
142459243Sobrien					break;
142559243Sobrien			}
142659243Sobrien			invert = 0;
142759243Sobrien			if (*p == '!' || *p == '^') {
142859243Sobrien				invert++;
142959243Sobrien				p++;
143059243Sobrien			}
143159243Sobrien			found = 0;
143259243Sobrien			chr = *q++;
143359243Sobrien			if (squoted && chr == CTLESC)
143459243Sobrien				chr = *q++;
143559243Sobrien			if (chr == '\0')
143659243Sobrien				return 0;
143759243Sobrien			c = *p++;
143859243Sobrien			do {
143959243Sobrien				if (c == CTLQUOTEMARK)
144059243Sobrien					continue;
144159243Sobrien				if (c == CTLESC)
144259243Sobrien					c = *p++;
144359243Sobrien				if (*p == '-' && p[1] != ']') {
144459243Sobrien					p++;
144559243Sobrien					while (*p == CTLQUOTEMARK)
144659243Sobrien						p++;
144759243Sobrien					if (*p == CTLESC)
144859243Sobrien						p++;
144959243Sobrien					if (   collate_range_cmp(chr, c) >= 0
145059243Sobrien					    && collate_range_cmp(chr, *p) <= 0
145159243Sobrien					   )
145259243Sobrien						found = 1;
145359243Sobrien					p++;
145459243Sobrien				} else {
145559243Sobrien					if (chr == c)
145659243Sobrien						found = 1;
145759243Sobrien				}
145859243Sobrien			} while ((c = *p++) != ']');
145959243Sobrien			if (found == invert)
146059243Sobrien				return 0;
146159243Sobrien			break;
146259243Sobrien		}
146359243Sobriendft:	        default:
146459243Sobrien			if (squoted && *q == CTLESC)
146559243Sobrien				q++;
146659243Sobrien			if (*q++ != c)
146759243Sobrien				return 0;
146859243Sobrien			break;
146959243Sobrien		}
147059243Sobrien	}
147159243Sobrienbreakloop:
147259243Sobrien	if (*q != '\0')
147359243Sobrien		return 0;
147459243Sobrien	return 1;
147559243Sobrien}
147659243Sobrien
147759243Sobrien
147859243Sobrien
147959243Sobrien/*
148059243Sobrien * Remove any CTLESC characters from a string.
148159243Sobrien */
148259243Sobrien
148359243Sobrienvoid
148459243Sobrienrmescapes(char *str)
148559243Sobrien{
148659243Sobrien	char *p, *q;
148759243Sobrien
148859243Sobrien	p = str;
148959243Sobrien	while (*p != CTLESC && *p != CTLQUOTEMARK) {
149059243Sobrien		if (*p++ == '\0')
149159243Sobrien			return;
149259243Sobrien	}
149359243Sobrien	q = p;
149459243Sobrien	while (*p) {
149559243Sobrien		if (*p == CTLQUOTEMARK) {
149659243Sobrien			p++;
149759243Sobrien			continue;
149859243Sobrien		}
149959243Sobrien		if (*p == CTLESC)
150059243Sobrien			p++;
150159243Sobrien		*q++ = *p++;
150259243Sobrien	}
150359243Sobrien	*q = '\0';
150459243Sobrien}
150559243Sobrien
150659243Sobrien
150759243Sobrien
150859243Sobrien/*
150959243Sobrien * See if a pattern matches in a case statement.
151059243Sobrien */
151159243Sobrien
151259243Sobrienint
151359243Sobriencasematch(union node *pattern, const char *val)
151459243Sobrien{
151559243Sobrien	struct stackmark smark;
151659243Sobrien	int result;
151759243Sobrien	char *p;
151859243Sobrien
151959243Sobrien	setstackmark(&smark);
152059243Sobrien	argbackq = pattern->narg.backquote;
152159243Sobrien	STARTSTACKSTR(expdest);
152259243Sobrien	ifslastp = NULL;
152359243Sobrien	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
152459243Sobrien	STPUTC('\0', expdest);
152559243Sobrien	p = grabstackstr(expdest);
152659243Sobrien	result = patmatch(p, val, 0);
152759243Sobrien	popstackmark(&smark);
152859243Sobrien	return result;
152959243Sobrien}
153059243Sobrien
153159243Sobrien/*
153259243Sobrien * Our own itoa().
153359243Sobrien */
153459243Sobrien
153559243SobrienSTATIC char *
153659243Sobriencvtnum(int num, char *buf)
153759243Sobrien{
153859243Sobrien	char temp[32];
153959243Sobrien	int neg = num < 0;
154059243Sobrien	char *p = temp + 31;
154159243Sobrien
154259243Sobrien	temp[31] = '\0';
154359243Sobrien
154459243Sobrien	do {
154559243Sobrien		*--p = num % 10 + '0';
154659243Sobrien	} while ((num /= 10) != 0);
154759243Sobrien
154859243Sobrien	if (neg)
154959243Sobrien		*--p = '-';
155059243Sobrien
155159243Sobrien	while (*p)
155259243Sobrien		STPUTC(*p++, buf);
155359243Sobrien	return buf;
155459243Sobrien}
155559243Sobrien
155659243Sobrien/*
155759243Sobrien * Do most of the work for wordexp(3).
155859243Sobrien */
155959243Sobrien
156059243Sobrienint
156159243Sobrienwordexpcmd(int argc, char **argv)
156259243Sobrien{
156359243Sobrien	size_t len;
156459243Sobrien	int i;
156559243Sobrien
156659243Sobrien	out1fmt("%08x", argc - 1);
156759243Sobrien	for (i = 1, len = 0; i < argc; i++)
156859243Sobrien		len += strlen(argv[i]);
156959243Sobrien	out1fmt("%08x", (int)len);
157059243Sobrien	for (i = 1; i < argc; i++) {
157159243Sobrien		out1str(argv[i]);
157259243Sobrien		out1c('\0');
157359243Sobrien	}
157459243Sobrien        return (0);
157559243Sobrien}
157659243Sobrien