11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
4207944Sjilles * Copyright (c) 1997-2005
5207944Sjilles *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
61556Srgrimes *
71556Srgrimes * This code is derived from software contributed to Berkeley by
81556Srgrimes * Kenneth Almquist.
91556Srgrimes *
101556Srgrimes * Redistribution and use in source and binary forms, with or without
111556Srgrimes * modification, are permitted provided that the following conditions
121556Srgrimes * are met:
131556Srgrimes * 1. Redistributions of source code must retain the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer.
151556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
161556Srgrimes *    notice, this list of conditions and the following disclaimer in the
171556Srgrimes *    documentation and/or other materials provided with the distribution.
181556Srgrimes * 4. Neither the name of the University nor the names of its contributors
191556Srgrimes *    may be used to endorse or promote products derived from this software
201556Srgrimes *    without specific prior written permission.
211556Srgrimes *
221556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
231556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
241556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
251556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
261556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
271556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
281556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
291556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
301556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
311556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
321556Srgrimes * SUCH DAMAGE.
331556Srgrimes */
341556Srgrimes
351556Srgrimes#ifndef lint
3636150Scharnier#if 0
3736150Scharnierstatic char sccsid[] = "@(#)expand.c	8.5 (Berkeley) 5/15/95";
3836150Scharnier#endif
391556Srgrimes#endif /* not lint */
4099110Sobrien#include <sys/cdefs.h>
4199110Sobrien__FBSDID("$FreeBSD$");
421556Srgrimes
4317987Speter#include <sys/types.h>
4417987Speter#include <sys/time.h>
4517987Speter#include <sys/stat.h>
46213775Sjhb#include <dirent.h>
4717987Speter#include <errno.h>
48213775Sjhb#include <inttypes.h>
49213775Sjhb#include <limits.h>
5017987Speter#include <pwd.h>
51213775Sjhb#include <stdio.h>
5217987Speter#include <stdlib.h>
53108286Stjr#include <string.h>
54213775Sjhb#include <unistd.h>
55221646Sjilles#include <wchar.h>
56223120Sjilles#include <wctype.h>
5717987Speter
581556Srgrimes/*
591556Srgrimes * Routines to expand arguments to commands.  We have to deal with
601556Srgrimes * backquotes, shell variables, and file metacharacters.
611556Srgrimes */
621556Srgrimes
631556Srgrimes#include "shell.h"
641556Srgrimes#include "main.h"
651556Srgrimes#include "nodes.h"
661556Srgrimes#include "eval.h"
671556Srgrimes#include "expand.h"
681556Srgrimes#include "syntax.h"
691556Srgrimes#include "parser.h"
701556Srgrimes#include "jobs.h"
711556Srgrimes#include "options.h"
721556Srgrimes#include "var.h"
731556Srgrimes#include "input.h"
741556Srgrimes#include "output.h"
751556Srgrimes#include "memalloc.h"
761556Srgrimes#include "error.h"
771556Srgrimes#include "mystring.h"
7817987Speter#include "arith.h"
7917987Speter#include "show.h"
80223060Sjilles#include "builtins.h"
811556Srgrimes
821556Srgrimes/*
831556Srgrimes * Structure specifying which parts of the string should be searched
841556Srgrimes * for IFS characters.
851556Srgrimes */
861556Srgrimes
871556Srgrimesstruct ifsregion {
881556Srgrimes	struct ifsregion *next;	/* next region in list */
891556Srgrimes	int begoff;		/* offset of start of region */
901556Srgrimes	int endoff;		/* offset of end of region */
91194975Sjilles	int inquotes;		/* search for nul bytes only */
921556Srgrimes};
931556Srgrimes
941556Srgrimes
95213760Sobrienstatic char *expdest;			/* output of current string */
96213760Sobrienstatic struct nodelist *argbackq;	/* list of back quote expressions */
97213760Sobrienstatic struct ifsregion ifsfirst;	/* first struct in list of ifs regions */
98213760Sobrienstatic struct ifsregion *ifslastp;	/* last struct in list */
99213760Sobrienstatic struct arglist exparg;		/* holds expanded arg list */
1001556Srgrimes
101213811Sobrienstatic void argstr(char *, int);
102213811Sobrienstatic char *exptilde(char *, int);
103213811Sobrienstatic void expbackq(union node *, int, int);
104214524Sjillesstatic int subevalvar(char *, char *, int, int, int, int, int);
105213811Sobrienstatic char *evalvar(char *, int);
106213811Sobrienstatic int varisset(char *, int);
107213811Sobrienstatic void varvalue(char *, int, int, int);
108213811Sobrienstatic void recordregion(int, int, int);
109213811Sobrienstatic void removerecordregions(int);
110213811Sobrienstatic void ifsbreakup(char *, struct arglist *);
111213811Sobrienstatic void expandmeta(struct strlist *, int);
112213811Sobrienstatic void expmeta(char *, char *);
113213811Sobrienstatic void addfname(char *);
114213811Sobrienstatic struct strlist *expsort(struct strlist *);
115213811Sobrienstatic struct strlist *msort(struct strlist *, int);
116229220Sjillesstatic int patmatch(const char *, const char *, int);
117213811Sobrienstatic char *cvtnum(int, char *);
118221646Sjillesstatic int collate_range_cmp(wchar_t, wchar_t);
1191556Srgrimes
120213811Sobrienstatic int
121221646Sjillescollate_range_cmp(wchar_t c1, wchar_t c2)
12219281Sache{
123221646Sjilles	static wchar_t s1[2], s2[2];
12419281Sache
12519281Sache	s1[0] = c1;
12619281Sache	s2[0] = c2;
127221646Sjilles	return (wcscoll(s1, s2));
12819281Sache}
12919281Sache
130216384Sjillesstatic char *
131216384Sjillesstputs_quotes(const char *data, const char *syntax, char *p)
132216384Sjilles{
133216384Sjilles	while (*data) {
134216384Sjilles		CHECKSTRSPACE(2, p);
135216384Sjilles		if (syntax[(int)*data] == CCTL)
136216384Sjilles			USTPUTC(CTLESC, p);
137216384Sjilles		USTPUTC(*data++, p);
138216384Sjilles	}
139216384Sjilles	return (p);
140216384Sjilles}
141216384Sjilles#define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p)
1421556Srgrimes
1431556Srgrimes/*
144212243Sjilles * Perform expansions on an argument, placing the resulting list of arguments
145212243Sjilles * in arglist.  Parameter expansion, command substitution and arithmetic
146212243Sjilles * expansion are always performed; additional expansions can be requested
147212243Sjilles * via flag (EXP_*).
148212243Sjilles * The result is left in the stack string.
149218203Sjilles * When arglist is NULL, perform here document expansion.
150212243Sjilles *
151212243Sjilles * Caution: this function uses global state and is not reentrant.
152212243Sjilles * However, a new invocation after an interrupted invocation is safe
153212243Sjilles * and will reset the global state for the new call.
1541556Srgrimes */
1551556Srgrimesvoid
15690111Simpexpandarg(union node *arg, struct arglist *arglist, int flag)
15717987Speter{
1581556Srgrimes	struct strlist *sp;
1591556Srgrimes	char *p;
1601556Srgrimes
1611556Srgrimes	argbackq = arg->narg.backquote;
1621556Srgrimes	STARTSTACKSTR(expdest);
1631556Srgrimes	ifsfirst.next = NULL;
1641556Srgrimes	ifslastp = NULL;
1651556Srgrimes	argstr(arg->narg.text, flag);
1661556Srgrimes	if (arglist == NULL) {
167222907Sjilles		STACKSTRNUL(expdest);
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/*
207212243Sjilles * Perform parameter expansion, command substitution and arithmetic
208212243Sjilles * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE.
209212243Sjilles * Processing ends at a CTLENDVAR character as well as '\0'.
210212243Sjilles * This is used to expand word in ${var+word} etc.
211212243Sjilles * If EXP_FULL, EXP_CASE or EXP_REDIR are set, keep and/or generate CTLESC
212212243Sjilles * characters to allow for further processing.
213212243Sjilles * If EXP_FULL is set, also preserve CTLQUOTEMARK characters.
2141556Srgrimes */
215213811Sobrienstatic void
21690111Simpargstr(char *p, int flag)
21717987Speter{
21825233Ssteve	char c;
219104672Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);	/* do CTLESC */
2201556Srgrimes	int firsteq = 1;
221214512Sjilles	int split_lit;
222214512Sjilles	int lit_quoted;
2231556Srgrimes
224214512Sjilles	split_lit = flag & EXP_SPLIT_LIT;
225214512Sjilles	lit_quoted = flag & EXP_LIT_QUOTED;
226214512Sjilles	flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED);
2271556Srgrimes	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
2281556Srgrimes		p = exptilde(p, flag);
2291556Srgrimes	for (;;) {
230215783Sjilles		CHECKSTRSPACE(2, expdest);
2311556Srgrimes		switch (c = *p++) {
2321556Srgrimes		case '\0':
233212243Sjilles		case CTLENDVAR:
2341556Srgrimes			goto breakloop;
23538887Stegge		case CTLQUOTEMARK:
236214512Sjilles			lit_quoted = 1;
23738887Stegge			/* "$@" syntax adherence hack */
23838887Stegge			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
23938887Stegge				break;
24039137Stegge			if ((flag & EXP_FULL) != 0)
241215783Sjilles				USTPUTC(c, expdest);
24238887Stegge			break;
243214512Sjilles		case CTLQUOTEEND:
244214512Sjilles			lit_quoted = 0;
245214512Sjilles			break;
2461556Srgrimes		case CTLESC:
2471556Srgrimes			if (quotes)
248215783Sjilles				USTPUTC(c, expdest);
2491556Srgrimes			c = *p++;
250215783Sjilles			USTPUTC(c, expdest);
251214512Sjilles			if (split_lit && !lit_quoted)
252214512Sjilles				recordregion(expdest - stackblock() -
253214512Sjilles				    (quotes ? 2 : 1),
254214512Sjilles				    expdest - stackblock(), 0);
2551556Srgrimes			break;
2561556Srgrimes		case CTLVAR:
2571556Srgrimes			p = evalvar(p, flag);
2581556Srgrimes			break;
2591556Srgrimes		case CTLBACKQ:
2601556Srgrimes		case CTLBACKQ|CTLQUOTE:
2611556Srgrimes			expbackq(argbackq->n, c & CTLQUOTE, flag);
2621556Srgrimes			argbackq = argbackq->next;
2631556Srgrimes			break;
2641556Srgrimes		case CTLENDARI:
2651556Srgrimes			expari(flag);
2661556Srgrimes			break;
2671556Srgrimes		case ':':
2681556Srgrimes		case '=':
2691556Srgrimes			/*
2701556Srgrimes			 * sort of a hack - expand tildes in variable
2711556Srgrimes			 * assignments (after the first '=' and after ':'s).
2721556Srgrimes			 */
273215783Sjilles			USTPUTC(c, expdest);
274214512Sjilles			if (split_lit && !lit_quoted)
275214512Sjilles				recordregion(expdest - stackblock() - 1,
276214512Sjilles				    expdest - stackblock(), 0);
277214512Sjilles			if (flag & EXP_VARTILDE && *p == '~' &&
278214512Sjilles			    (c != '=' || firsteq)) {
279214512Sjilles				if (c == '=')
280214512Sjilles					firsteq = 0;
2811556Srgrimes				p = exptilde(p, flag);
2821556Srgrimes			}
2831556Srgrimes			break;
2841556Srgrimes		default:
285215783Sjilles			USTPUTC(c, expdest);
286214512Sjilles			if (split_lit && !lit_quoted)
287214512Sjilles				recordregion(expdest - stackblock() - 1,
288214512Sjilles				    expdest - stackblock(), 0);
2891556Srgrimes		}
2901556Srgrimes	}
2911556Srgrimesbreakloop:;
2921556Srgrimes}
2931556Srgrimes
294212243Sjilles/*
295212243Sjilles * Perform tilde expansion, placing the result in the stack string and
296212243Sjilles * returning the next position in the input string to process.
297212243Sjilles */
298213811Sobrienstatic char *
29990111Simpexptilde(char *p, int flag)
30017987Speter{
3011556Srgrimes	char c, *startp = p;
3021556Srgrimes	struct passwd *pw;
3031556Srgrimes	char *home;
304108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
3051556Srgrimes
30617987Speter	while ((c = *p) != '\0') {
3071556Srgrimes		switch(c) {
308200988Sjilles		case CTLESC: /* This means CTL* are always considered quoted. */
309200988Sjilles		case CTLVAR:
310200988Sjilles		case CTLBACKQ:
311200988Sjilles		case CTLBACKQ | CTLQUOTE:
312200988Sjilles		case CTLARI:
313200988Sjilles		case CTLENDARI:
31439137Stegge		case CTLQUOTEMARK:
31539137Stegge			return (startp);
3161556Srgrimes		case ':':
3171556Srgrimes			if (flag & EXP_VARTILDE)
3181556Srgrimes				goto done;
3191556Srgrimes			break;
3201556Srgrimes		case '/':
321206150Sjilles		case CTLENDVAR:
3221556Srgrimes			goto done;
3231556Srgrimes		}
3241556Srgrimes		p++;
3251556Srgrimes	}
3261556Srgrimesdone:
3271556Srgrimes	*p = '\0';
3281556Srgrimes	if (*(startp+1) == '\0') {
3291556Srgrimes		if ((home = lookupvar("HOME")) == NULL)
3301556Srgrimes			goto lose;
3311556Srgrimes	} else {
3321556Srgrimes		if ((pw = getpwnam(startp+1)) == NULL)
3331556Srgrimes			goto lose;
3341556Srgrimes		home = pw->pw_dir;
3351556Srgrimes	}
3361556Srgrimes	if (*home == '\0')
3371556Srgrimes		goto lose;
3381556Srgrimes	*p = c;
339216384Sjilles	if (quotes)
340216384Sjilles		STPUTS_QUOTES(home, SQSYNTAX, expdest);
341216384Sjilles	else
342216384Sjilles		STPUTS(home, expdest);
3431556Srgrimes	return (p);
3441556Srgrimeslose:
3451556Srgrimes	*p = c;
3461556Srgrimes	return (startp);
3471556Srgrimes}
3481556Srgrimes
3491556Srgrimes
350213811Sobrienstatic void
35190111Simpremoverecordregions(int endoff)
35238887Stegge{
35338887Stegge	if (ifslastp == NULL)
35438887Stegge		return;
35538887Stegge
35638887Stegge	if (ifsfirst.endoff > endoff) {
35738887Stegge		while (ifsfirst.next != NULL) {
35838887Stegge			struct ifsregion *ifsp;
35938887Stegge			INTOFF;
36038887Stegge			ifsp = ifsfirst.next->next;
36138887Stegge			ckfree(ifsfirst.next);
36238887Stegge			ifsfirst.next = ifsp;
36338887Stegge			INTON;
36438887Stegge		}
36538887Stegge		if (ifsfirst.begoff > endoff)
36638887Stegge			ifslastp = NULL;
36738887Stegge		else {
36838887Stegge			ifslastp = &ifsfirst;
36938887Stegge			ifsfirst.endoff = endoff;
37038887Stegge		}
37138887Stegge		return;
37238887Stegge	}
373155301Sschweikh
37438887Stegge	ifslastp = &ifsfirst;
37538887Stegge	while (ifslastp->next && ifslastp->next->begoff < endoff)
37638887Stegge		ifslastp=ifslastp->next;
37738887Stegge	while (ifslastp->next != NULL) {
37838887Stegge		struct ifsregion *ifsp;
37938887Stegge		INTOFF;
38038887Stegge		ifsp = ifslastp->next->next;
38138887Stegge		ckfree(ifslastp->next);
38238887Stegge		ifslastp->next = ifsp;
38338887Stegge		INTON;
38438887Stegge	}
38538887Stegge	if (ifslastp->endoff > endoff)
38638887Stegge		ifslastp->endoff = endoff;
38738887Stegge}
38838887Stegge
3891556Srgrimes/*
3901556Srgrimes * Expand arithmetic expression.  Backup to start of expression,
3911556Srgrimes * evaluate, place result in (backed up) result, adjust string position.
3921556Srgrimes */
3931556Srgrimesvoid
39490111Simpexpari(int flag)
39517987Speter{
396207206Sjilles	char *p, *q, *start;
397178631Sstefanf	arith_t result;
39838887Stegge	int begoff;
399108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
40038887Stegge	int quoted;
4011556Srgrimes
4021556Srgrimes	/*
40346684Skris	 * This routine is slightly over-complicated for
4041556Srgrimes	 * efficiency.  First we make sure there is
4051556Srgrimes	 * enough space for the result, which may be bigger
406212243Sjilles	 * than the expression.  Next we
4071556Srgrimes	 * scan backwards looking for the start of arithmetic.  If the
4081556Srgrimes	 * next previous character is a CTLESC character, then we
4091556Srgrimes	 * have to rescan starting from the beginning since CTLESC
4108855Srgrimes	 * characters have to be processed left to right.
4111556Srgrimes	 */
412178631Sstefanf	CHECKSTRSPACE(DIGITS(result) - 2, expdest);
4138855Srgrimes	USTPUTC('\0', expdest);
4141556Srgrimes	start = stackblock();
41583676Stegge	p = expdest - 2;
41683676Stegge	while (p >= start && *p != CTLARI)
4171556Srgrimes		--p;
41883676Stegge	if (p < start || *p != CTLARI)
4191556Srgrimes		error("missing CTLARI (shouldn't happen)");
42083676Stegge	if (p > start && *(p - 1) == CTLESC)
4211556Srgrimes		for (p = start; *p != CTLARI; p++)
4221556Srgrimes			if (*p == CTLESC)
4231556Srgrimes				p++;
42438887Stegge
42538887Stegge	if (p[1] == '"')
42638887Stegge		quoted=1;
42738887Stegge	else
42838887Stegge		quoted=0;
42938887Stegge	begoff = p - start;
43038887Stegge	removerecordregions(begoff);
4311556Srgrimes	if (quotes)
43238887Stegge		rmescapes(p+2);
433207206Sjilles	q = grabstackstr(expdest);
43438887Stegge	result = arith(p+2);
435207206Sjilles	ungrabstackstr(q, expdest);
436178631Sstefanf	fmtstr(p, DIGITS(result), ARITH_FORMAT_STR, result);
4371556Srgrimes	while (*p++)
4381556Srgrimes		;
43938887Stegge	if (quoted == 0)
44038887Stegge		recordregion(begoff, p - 1 - start, 0);
4411556Srgrimes	result = expdest - p + 1;
4421556Srgrimes	STADJUST(-result, expdest);
4431556Srgrimes}
4441556Srgrimes
4451556Srgrimes
4461556Srgrimes/*
447212243Sjilles * Perform command substitution.
4481556Srgrimes */
449213811Sobrienstatic void
45090111Simpexpbackq(union node *cmd, int quoted, int flag)
45117987Speter{
4521556Srgrimes	struct backcmd in;
4531556Srgrimes	int i;
4541556Srgrimes	char buf[128];
4551556Srgrimes	char *p;
4561556Srgrimes	char *dest = expdest;
4571556Srgrimes	struct ifsregion saveifs, *savelastp;
4581556Srgrimes	struct nodelist *saveargbackq;
4591556Srgrimes	char lastc;
4601556Srgrimes	int startloc = dest - stackblock();
4611556Srgrimes	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
462108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
463248980Sjilles	size_t nnl;
4641556Srgrimes
4651556Srgrimes	INTOFF;
4661556Srgrimes	saveifs = ifsfirst;
4671556Srgrimes	savelastp = ifslastp;
4681556Srgrimes	saveargbackq = argbackq;
4691556Srgrimes	p = grabstackstr(dest);
4701556Srgrimes	evalbackcmd(cmd, &in);
4711556Srgrimes	ungrabstackstr(p, dest);
4721556Srgrimes	ifsfirst = saveifs;
4731556Srgrimes	ifslastp = savelastp;
4741556Srgrimes	argbackq = saveargbackq;
4751556Srgrimes
4761556Srgrimes	p = in.buf;
4771556Srgrimes	lastc = '\0';
478115424Sfenner	nnl = 0;
479115424Sfenner	/* Don't copy trailing newlines */
4801556Srgrimes	for (;;) {
4811556Srgrimes		if (--in.nleft < 0) {
4821556Srgrimes			if (in.fd < 0)
4831556Srgrimes				break;
4841556Srgrimes			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
4851556Srgrimes			TRACE(("expbackq: read returns %d\n", i));
4861556Srgrimes			if (i <= 0)
4871556Srgrimes				break;
4881556Srgrimes			p = buf;
4891556Srgrimes			in.nleft = i - 1;
4901556Srgrimes		}
4911556Srgrimes		lastc = *p++;
4921556Srgrimes		if (lastc != '\0') {
493115424Sfenner			if (lastc == '\n') {
494115424Sfenner				nnl++;
495115424Sfenner			} else {
496216706Sjilles				CHECKSTRSPACE(nnl + 2, dest);
497115424Sfenner				while (nnl > 0) {
498115424Sfenner					nnl--;
499216706Sjilles					USTPUTC('\n', dest);
500115424Sfenner				}
501216496Sjilles				if (quotes && syntax[(int)lastc] == CCTL)
502216706Sjilles					USTPUTC(CTLESC, dest);
503216706Sjilles				USTPUTC(lastc, dest);
504115424Sfenner			}
5051556Srgrimes		}
5061556Srgrimes	}
50717987Speter
5081556Srgrimes	if (in.fd >= 0)
5091556Srgrimes		close(in.fd);
5101556Srgrimes	if (in.buf)
5111556Srgrimes		ckfree(in.buf);
5121556Srgrimes	if (in.jp)
51345916Scracauer		exitstatus = waitforjob(in.jp, (int *)NULL);
5141556Srgrimes	if (quoted == 0)
5151556Srgrimes		recordregion(startloc, dest - stackblock(), 0);
516213775Sjhb	TRACE(("expbackq: size=%td: \"%.*s\"\n",
517213775Sjhb		((dest - stackblock()) - startloc),
518213775Sjhb		(int)((dest - stackblock()) - startloc),
5191556Srgrimes		stackblock() + startloc));
5201556Srgrimes	expdest = dest;
5211556Srgrimes	INTON;
5221556Srgrimes}
5231556Srgrimes
5241556Srgrimes
5251556Srgrimes
526213811Sobrienstatic int
52790111Simpsubevalvar(char *p, char *str, int strloc, int subtype, int startloc,
528214524Sjilles  int varflags, int quotes)
52917987Speter{
53017987Speter	char *startp;
53117987Speter	char *loc = NULL;
53245514Stegge	char *q;
53317987Speter	int c = 0;
53417987Speter	struct nodelist *saveargbackq = argbackq;
53520425Ssteve	int amount;
53620425Ssteve
537206150Sjilles	argstr(p, (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX ||
538206147Sjilles	    subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX ?
539206150Sjilles	    EXP_CASE : 0) | EXP_TILDE);
54017987Speter	STACKSTRNUL(expdest);
54117987Speter	argbackq = saveargbackq;
54217987Speter	startp = stackblock() + startloc;
54320425Ssteve	if (str == NULL)
54420425Ssteve	    str = stackblock() + strloc;
54517987Speter
54617987Speter	switch (subtype) {
54717987Speter	case VSASSIGN:
54817987Speter		setvar(str, startp, 0);
54920425Ssteve		amount = startp - expdest;
55020425Ssteve		STADJUST(amount, expdest);
55117987Speter		varflags &= ~VSNUL;
55217987Speter		return 1;
55317987Speter
55417987Speter	case VSQUESTION:
55517987Speter		if (*p != CTLENDVAR) {
556201366Sjilles			outfmt(out2, "%s\n", startp);
55717987Speter			error((char *)NULL);
55817987Speter		}
559112254Sru		error("%.*s: parameter %snot set", (int)(p - str - 1),
56017987Speter		      str, (varflags & VSNUL) ? "null or "
56117987Speter					      : nullstr);
56217987Speter		return 0;
56317987Speter
56417987Speter	case VSTRIMLEFT:
56525233Ssteve		for (loc = startp; loc < str; loc++) {
56617987Speter			c = *loc;
56717987Speter			*loc = '\0';
568214524Sjilles			if (patmatch(str, startp, quotes)) {
56917987Speter				*loc = c;
57017987Speter				goto recordleft;
57117987Speter			}
57217987Speter			*loc = c;
573214524Sjilles			if (quotes && *loc == CTLESC)
57445514Stegge				loc++;
57517987Speter		}
57617987Speter		return 0;
57717987Speter
57817987Speter	case VSTRIMLEFTMAX:
57945514Stegge		for (loc = str - 1; loc >= startp;) {
58017987Speter			c = *loc;
58117987Speter			*loc = '\0';
582214524Sjilles			if (patmatch(str, startp, quotes)) {
58317987Speter				*loc = c;
58417987Speter				goto recordleft;
58517987Speter			}
58617987Speter			*loc = c;
58745514Stegge			loc--;
588214524Sjilles			if (quotes && loc > startp && *(loc - 1) == CTLESC) {
58945514Stegge				for (q = startp; q < loc; q++)
59045514Stegge					if (*q == CTLESC)
59145514Stegge						q++;
59245514Stegge				if (q > loc)
59345514Stegge					loc--;
59445514Stegge			}
59517987Speter		}
59617987Speter		return 0;
59717987Speter
59817987Speter	case VSTRIMRIGHT:
59945514Stegge		for (loc = str - 1; loc >= startp;) {
600214524Sjilles			if (patmatch(str, loc, quotes)) {
60120425Ssteve				amount = loc - expdest;
60220425Ssteve				STADJUST(amount, expdest);
60317987Speter				return 1;
60417987Speter			}
60545514Stegge			loc--;
606214524Sjilles			if (quotes && loc > startp && *(loc - 1) == CTLESC) {
60745514Stegge				for (q = startp; q < loc; q++)
60845514Stegge					if (*q == CTLESC)
60945514Stegge						q++;
61045514Stegge				if (q > loc)
61145514Stegge					loc--;
61245514Stegge			}
61317987Speter		}
61417987Speter		return 0;
61517987Speter
61617987Speter	case VSTRIMRIGHTMAX:
61717987Speter		for (loc = startp; loc < str - 1; loc++) {
618214524Sjilles			if (patmatch(str, loc, quotes)) {
61920425Ssteve				amount = loc - expdest;
62020425Ssteve				STADJUST(amount, expdest);
62117987Speter				return 1;
62217987Speter			}
623214524Sjilles			if (quotes && *loc == CTLESC)
62445514Stegge				loc++;
62517987Speter		}
62617987Speter		return 0;
62717987Speter
62817987Speter
62917987Speter	default:
63017987Speter		abort();
63117987Speter	}
63217987Speter
63317987Speterrecordleft:
63420425Ssteve	amount = ((str - 1) - (loc - startp)) - expdest;
63520425Ssteve	STADJUST(amount, expdest);
63617987Speter	while (loc != str - 1)
63717987Speter		*startp++ = *loc++;
63817987Speter	return 1;
63917987Speter}
64017987Speter
64117987Speter
6421556Srgrimes/*
6431556Srgrimes * Expand a variable, and return a pointer to the next character in the
6441556Srgrimes * input string.
6451556Srgrimes */
6461556Srgrimes
647213811Sobrienstatic char *
64890111Simpevalvar(char *p, int flag)
64917987Speter{
6501556Srgrimes	int subtype;
6511556Srgrimes	int varflags;
6521556Srgrimes	char *var;
6531556Srgrimes	char *val;
65445644Stegge	int patloc;
6551556Srgrimes	int c;
6561556Srgrimes	int set;
6571556Srgrimes	int special;
6581556Srgrimes	int startloc;
65917987Speter	int varlen;
660221602Sjilles	int varlenb;
66117987Speter	int easy;
662108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
6631556Srgrimes
664164081Sstefanf	varflags = (unsigned char)*p++;
6651556Srgrimes	subtype = varflags & VSTYPE;
6661556Srgrimes	var = p;
6671556Srgrimes	special = 0;
6681556Srgrimes	if (! is_name(*p))
6691556Srgrimes		special = 1;
6701556Srgrimes	p = strchr(p, '=') + 1;
6711556Srgrimesagain: /* jump here after setting a variable with ${var=text} */
672179022Sstefanf	if (varflags & VSLINENO) {
673179022Sstefanf		set = 1;
674179022Sstefanf		special = 0;
675179022Sstefanf		val = var;
676179022Sstefanf		p[-1] = '\0';	/* temporarily overwrite '=' to have \0
677179022Sstefanf				   terminated string */
678179022Sstefanf	} else if (special) {
67925233Ssteve		set = varisset(var, varflags & VSNUL);
6801556Srgrimes		val = NULL;
6811556Srgrimes	} else {
68260592Scracauer		val = bltinlookup(var, 1);
68317987Speter		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
6841556Srgrimes			val = NULL;
6851556Srgrimes			set = 0;
6861556Srgrimes		} else
6871556Srgrimes			set = 1;
6881556Srgrimes	}
68917987Speter	varlen = 0;
6901556Srgrimes	startloc = expdest - stackblock();
691198454Sjilles	if (!set && uflag && *var != '@' && *var != '*') {
69296939Stjr		switch (subtype) {
69396939Stjr		case VSNORMAL:
69496939Stjr		case VSTRIMLEFT:
69596939Stjr		case VSTRIMLEFTMAX:
69696939Stjr		case VSTRIMRIGHT:
69796939Stjr		case VSTRIMRIGHTMAX:
69896939Stjr		case VSLENGTH:
699112254Sru			error("%.*s: parameter not set", (int)(p - var - 1),
700112254Sru			    var);
70196939Stjr		}
70296939Stjr	}
7031556Srgrimes	if (set && subtype != VSPLUS) {
7041556Srgrimes		/* insert the value of the variable */
7051556Srgrimes		if (special) {
706164081Sstefanf			varvalue(var, varflags & VSQUOTE, subtype, flag);
70717987Speter			if (subtype == VSLENGTH) {
708221602Sjilles				varlenb = expdest - stackblock() - startloc;
709221602Sjilles				varlen = varlenb;
710221602Sjilles				if (localeisutf8) {
711221602Sjilles					val = stackblock() + startloc;
712221602Sjilles					for (;val != expdest; val++)
713221602Sjilles						if ((*val & 0xC0) == 0x80)
714221602Sjilles							varlen--;
715221602Sjilles				}
716221602Sjilles				STADJUST(-varlenb, expdest);
71717987Speter			}
7181556Srgrimes		} else {
71920425Ssteve			char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
72017987Speter								  : BASESYNTAX;
7211556Srgrimes
72217987Speter			if (subtype == VSLENGTH) {
72317987Speter				for (;*val; val++)
724221602Sjilles					if (!localeisutf8 ||
725221602Sjilles					    (*val & 0xC0) != 0x80)
726221602Sjilles						varlen++;
7271556Srgrimes			}
72817987Speter			else {
729216384Sjilles				if (quotes)
730216384Sjilles					STPUTS_QUOTES(val, syntax, expdest);
731216384Sjilles				else
732216384Sjilles					STPUTS(val, expdest);
73317987Speter
73417987Speter			}
7351556Srgrimes		}
7361556Srgrimes	}
73720425Ssteve
7381556Srgrimes	if (subtype == VSPLUS)
7391556Srgrimes		set = ! set;
74017987Speter
74120425Ssteve	easy = ((varflags & VSQUOTE) == 0 ||
74217987Speter		(*var == '@' && shellparam.nparam != 1));
74317987Speter
74417987Speter
74517987Speter	switch (subtype) {
74617987Speter	case VSLENGTH:
74717987Speter		expdest = cvtnum(varlen, expdest);
74817987Speter		goto record;
74917987Speter
75017987Speter	case VSNORMAL:
75117987Speter		if (!easy)
75217987Speter			break;
75317987Speterrecord:
75420425Ssteve		recordregion(startloc, expdest - stackblock(),
755222361Sjilles		    varflags & VSQUOTE || (ifsset() && ifsval()[0] == '\0' &&
756222361Sjilles		    (*var == '@' || *var == '*')));
75717987Speter		break;
75817987Speter
75917987Speter	case VSPLUS:
76017987Speter	case VSMINUS:
76117987Speter		if (!set) {
762214512Sjilles			argstr(p, flag | (flag & EXP_FULL ? EXP_SPLIT_LIT : 0) |
763214512Sjilles			    (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0));
76417987Speter			break;
76517987Speter		}
76617987Speter		if (easy)
76717987Speter			goto record;
76817987Speter		break;
76917987Speter
77017987Speter	case VSTRIMLEFT:
77117987Speter	case VSTRIMLEFTMAX:
77217987Speter	case VSTRIMRIGHT:
77317987Speter	case VSTRIMRIGHTMAX:
77417987Speter		if (!set)
77517987Speter			break;
77617987Speter		/*
77717987Speter		 * Terminate the string and start recording the pattern
77817987Speter		 * right after it
77917987Speter		 */
78017987Speter		STPUTC('\0', expdest);
78145644Stegge		patloc = expdest - stackblock();
78245644Stegge		if (subevalvar(p, NULL, patloc, subtype,
783214524Sjilles		    startloc, varflags, quotes) == 0) {
78445644Stegge			int amount = (expdest - stackblock() - patloc) + 1;
78525233Ssteve			STADJUST(-amount, expdest);
78625233Ssteve		}
78738887Stegge		/* Remove any recorded regions beyond start of variable */
78838887Stegge		removerecordregions(startloc);
78938887Stegge		goto record;
79017987Speter
79117987Speter	case VSASSIGN:
79217987Speter	case VSQUESTION:
79317987Speter		if (!set) {
794214524Sjilles			if (subevalvar(p, var, 0, subtype, startloc, varflags,
795214524Sjilles			    quotes)) {
79620425Ssteve				varflags &= ~VSNUL;
797155301Sschweikh				/*
798155301Sschweikh				 * Remove any recorded regions beyond
799155301Sschweikh				 * start of variable
80038887Stegge				 */
80138887Stegge				removerecordregions(startloc);
8021556Srgrimes				goto again;
80320425Ssteve			}
80417987Speter			break;
8051556Srgrimes		}
80617987Speter		if (easy)
80717987Speter			goto record;
80817987Speter		break;
80917987Speter
810164003Sstefanf	case VSERROR:
811164003Sstefanf		c = p - var - 1;
812164003Sstefanf		error("${%.*s%s}: Bad substitution", c, var,
813164003Sstefanf		    (c > 0 && *p != CTLENDVAR) ? "..." : "");
814164003Sstefanf
81517987Speter	default:
81617987Speter		abort();
8171556Srgrimes	}
818179022Sstefanf	p[-1] = '=';	/* recover overwritten '=' */
81917987Speter
8201556Srgrimes	if (subtype != VSNORMAL) {	/* skip to end of alternative */
8211556Srgrimes		int nesting = 1;
8221556Srgrimes		for (;;) {
8231556Srgrimes			if ((c = *p++) == CTLESC)
8241556Srgrimes				p++;
8251556Srgrimes			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
8261556Srgrimes				if (set)
8271556Srgrimes					argbackq = argbackq->next;
8281556Srgrimes			} else if (c == CTLVAR) {
8291556Srgrimes				if ((*p++ & VSTYPE) != VSNORMAL)
8301556Srgrimes					nesting++;
8311556Srgrimes			} else if (c == CTLENDVAR) {
8321556Srgrimes				if (--nesting == 0)
8331556Srgrimes					break;
8341556Srgrimes			}
8351556Srgrimes		}
8361556Srgrimes	}
8371556Srgrimes	return p;
8381556Srgrimes}
8391556Srgrimes
8401556Srgrimes
8411556Srgrimes
8421556Srgrimes/*
8431556Srgrimes * Test whether a specialized variable is set.
8441556Srgrimes */
8451556Srgrimes
846213811Sobrienstatic int
84790111Simpvarisset(char *name, int nulok)
84825233Ssteve{
8491556Srgrimes
85025233Ssteve	if (*name == '!')
851209600Sjilles		return backgndpidset();
85225233Ssteve	else if (*name == '@' || *name == '*') {
8531556Srgrimes		if (*shellparam.p == NULL)
8541556Srgrimes			return 0;
85525233Ssteve
85625233Ssteve		if (nulok) {
85725233Ssteve			char **av;
85825233Ssteve
85925233Ssteve			for (av = shellparam.p; *av; av++)
86025233Ssteve				if (**av != '\0')
86125233Ssteve					return 1;
86225233Ssteve			return 0;
86325233Ssteve		}
86418202Speter	} else if (is_digit(*name)) {
86525233Ssteve		char *ap;
86620425Ssteve		int num = atoi(name);
86725233Ssteve
86825233Ssteve		if (num > shellparam.nparam)
86925233Ssteve			return 0;
87025233Ssteve
87125233Ssteve		if (num == 0)
87225233Ssteve			ap = arg0;
87325233Ssteve		else
87425233Ssteve			ap = shellparam.p[num - 1];
87525233Ssteve
87625233Ssteve		if (nulok && (ap == NULL || *ap == '\0'))
87725233Ssteve			return 0;
8781556Srgrimes	}
8791556Srgrimes	return 1;
8801556Srgrimes}
8811556Srgrimes
882216384Sjillesstatic void
883216384Sjillesstrtodest(const char *p, int flag, int subtype, int quoted)
884216384Sjilles{
885216384Sjilles	if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH)
886216384Sjilles		STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest);
887216384Sjilles	else
888216384Sjilles		STPUTS(p, expdest);
889216384Sjilles}
8901556Srgrimes
8911556Srgrimes/*
8921556Srgrimes * Add the value of a specialized variable to the stack string.
8931556Srgrimes */
8941556Srgrimes
895213811Sobrienstatic void
896164081Sstefanfvarvalue(char *name, int quoted, int subtype, int flag)
89717987Speter{
8981556Srgrimes	int num;
8991556Srgrimes	char *p;
9001556Srgrimes	int i;
9011556Srgrimes	char sep;
9021556Srgrimes	char **ap;
9031556Srgrimes
90418202Speter	switch (*name) {
9051556Srgrimes	case '$':
9061556Srgrimes		num = rootpid;
9071556Srgrimes		goto numvar;
9081556Srgrimes	case '?':
90917987Speter		num = oexitstatus;
9101556Srgrimes		goto numvar;
9111556Srgrimes	case '#':
9121556Srgrimes		num = shellparam.nparam;
9131556Srgrimes		goto numvar;
9141556Srgrimes	case '!':
915209600Sjilles		num = backgndpidval();
9161556Srgrimesnumvar:
91717987Speter		expdest = cvtnum(num, expdest);
9181556Srgrimes		break;
9191556Srgrimes	case '-':
9201556Srgrimes		for (i = 0 ; i < NOPTS ; i++) {
9211556Srgrimes			if (optlist[i].val)
9221556Srgrimes				STPUTC(optlist[i].letter, expdest);
9231556Srgrimes		}
9241556Srgrimes		break;
9251556Srgrimes	case '@':
926164081Sstefanf		if (flag & EXP_FULL && quoted) {
92738887Stegge			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
928216384Sjilles				strtodest(p, flag, subtype, quoted);
92938887Stegge				if (*ap)
93038887Stegge					STPUTC('\0', expdest);
93138887Stegge			}
93238887Stegge			break;
9331556Srgrimes		}
934102410Scharnier		/* FALLTHROUGH */
9351556Srgrimes	case '*':
936149825Srse		if (ifsset())
93738887Stegge			sep = ifsval()[0];
93838887Stegge		else
93938887Stegge			sep = ' ';
9401556Srgrimes		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
941216384Sjilles			strtodest(p, flag, subtype, quoted);
942222361Sjilles			if (!*ap)
943222361Sjilles				break;
944222361Sjilles			if (sep || (flag & EXP_FULL && !quoted && **ap != '\0'))
9451556Srgrimes				STPUTC(sep, expdest);
9461556Srgrimes		}
9471556Srgrimes		break;
9481556Srgrimes	case '0':
9491556Srgrimes		p = arg0;
950216384Sjilles		strtodest(p, flag, subtype, quoted);
9511556Srgrimes		break;
9521556Srgrimes	default:
95318202Speter		if (is_digit(*name)) {
95418202Speter			num = atoi(name);
95518202Speter			if (num > 0 && num <= shellparam.nparam) {
95618202Speter				p = shellparam.p[num - 1];
957216384Sjilles				strtodest(p, flag, subtype, quoted);
95818202Speter			}
9591556Srgrimes		}
9601556Srgrimes		break;
9611556Srgrimes	}
9621556Srgrimes}
9631556Srgrimes
9641556Srgrimes
9651556Srgrimes
9661556Srgrimes/*
967218909Sbrucec * Record the fact that we have to scan this region of the
9681556Srgrimes * string for IFS characters.
9691556Srgrimes */
9701556Srgrimes
971213811Sobrienstatic void
972194975Sjillesrecordregion(int start, int end, int inquotes)
97317987Speter{
97425233Ssteve	struct ifsregion *ifsp;
9751556Srgrimes
9761556Srgrimes	if (ifslastp == NULL) {
9771556Srgrimes		ifsp = &ifsfirst;
9781556Srgrimes	} else {
979194975Sjilles		if (ifslastp->endoff == start
980194975Sjilles		    && ifslastp->inquotes == inquotes) {
981194975Sjilles			/* extend previous area */
982194975Sjilles			ifslastp->endoff = end;
983194975Sjilles			return;
984194975Sjilles		}
9851556Srgrimes		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
9861556Srgrimes		ifslastp->next = ifsp;
9871556Srgrimes	}
9881556Srgrimes	ifslastp = ifsp;
9891556Srgrimes	ifslastp->next = NULL;
9901556Srgrimes	ifslastp->begoff = start;
9911556Srgrimes	ifslastp->endoff = end;
992194975Sjilles	ifslastp->inquotes = inquotes;
9931556Srgrimes}
9941556Srgrimes
9951556Srgrimes
9961556Srgrimes
9971556Srgrimes/*
9981556Srgrimes * Break the argument string into pieces based upon IFS and add the
9991556Srgrimes * strings to the argument list.  The regions of the string to be
10001556Srgrimes * searched for IFS characters have been stored by recordregion.
1001212243Sjilles * CTLESC characters are preserved but have little effect in this pass
1002212243Sjilles * other than escaping CTL* characters.  In particular, they do not escape
1003212243Sjilles * IFS characters: that should be done with the ifsregion mechanism.
1004212243Sjilles * CTLQUOTEMARK characters are used to preserve empty quoted strings.
1005212243Sjilles * This pass treats them as a regular character, making the string non-empty.
1006212243Sjilles * Later, they are removed along with the other CTL* characters.
10071556Srgrimes */
1008213811Sobrienstatic void
100990111Simpifsbreakup(char *string, struct arglist *arglist)
101090111Simp{
10111556Srgrimes	struct ifsregion *ifsp;
10121556Srgrimes	struct strlist *sp;
10131556Srgrimes	char *start;
101425233Ssteve	char *p;
10151556Srgrimes	char *q;
1016201053Sjilles	const char *ifs;
1017194975Sjilles	const char *ifsspc;
1018194975Sjilles	int had_param_ch = 0;
10191556Srgrimes
1020194975Sjilles	start = string;
102117987Speter
1022194975Sjilles	if (ifslastp == NULL) {
1023194975Sjilles		/* Return entire argument, IFS doesn't apply to any of it */
1024194975Sjilles		sp = (struct strlist *)stalloc(sizeof *sp);
1025194975Sjilles		sp->text = start;
1026194975Sjilles		*arglist->lastp = sp;
1027194975Sjilles		arglist->lastp = &sp->next;
1028194975Sjilles		return;
1029194975Sjilles	}
1030194975Sjilles
1031194975Sjilles	ifs = ifsset() ? ifsval() : " \t\n";
1032194975Sjilles
1033194975Sjilles	for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
1034194975Sjilles		p = string + ifsp->begoff;
1035194975Sjilles		while (p < string + ifsp->endoff) {
1036194975Sjilles			q = p;
1037194975Sjilles			if (*p == CTLESC)
1038194975Sjilles				p++;
1039194975Sjilles			if (ifsp->inquotes) {
1040194975Sjilles				/* Only NULs (should be from "$@") end args */
1041194977Sjilles				had_param_ch = 1;
1042194975Sjilles				if (*p != 0) {
10431556Srgrimes					p++;
1044194975Sjilles					continue;
1045194975Sjilles				}
1046194975Sjilles				ifsspc = NULL;
1047194975Sjilles			} else {
1048194975Sjilles				if (!strchr(ifs, *p)) {
1049194977Sjilles					had_param_ch = 1;
105038887Stegge					p++;
1051194975Sjilles					continue;
1052194975Sjilles				}
1053194975Sjilles				ifsspc = strchr(" \t\n", *p);
1054194975Sjilles
1055194975Sjilles				/* Ignore IFS whitespace at start */
1056194975Sjilles				if (q == start && ifsspc != NULL) {
1057194975Sjilles					p++;
10581556Srgrimes					start = p;
1059194975Sjilles					continue;
1060194975Sjilles				}
1061194977Sjilles				had_param_ch = 0;
10621556Srgrimes			}
1063194975Sjilles
1064194975Sjilles			/* Save this argument... */
1065194975Sjilles			*q = '\0';
10661556Srgrimes			sp = (struct strlist *)stalloc(sizeof *sp);
10671556Srgrimes			sp->text = start;
10681556Srgrimes			*arglist->lastp = sp;
10691556Srgrimes			arglist->lastp = &sp->next;
1070194975Sjilles			p++;
1071194975Sjilles
1072194975Sjilles			if (ifsspc != NULL) {
1073194975Sjilles				/* Ignore further trailing IFS whitespace */
1074194975Sjilles				for (; p < string + ifsp->endoff; p++) {
1075194975Sjilles					q = p;
1076194975Sjilles					if (*p == CTLESC)
1077194975Sjilles						p++;
1078194975Sjilles					if (strchr(ifs, *p) == NULL) {
1079194975Sjilles						p = q;
1080194975Sjilles						break;
1081194975Sjilles					}
1082194975Sjilles					if (strchr(" \t\n", *p) == NULL) {
1083194975Sjilles						p++;
1084194975Sjilles						break;
1085194975Sjilles					}
1086194975Sjilles				}
1087194975Sjilles			}
1088194975Sjilles			start = p;
10891556Srgrimes		}
1090194975Sjilles	}
1091194975Sjilles
1092194975Sjilles	/*
1093194975Sjilles	 * Save anything left as an argument.
1094194975Sjilles	 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
1095194975Sjilles	 * generating 2 arguments, the second of which is empty.
1096194975Sjilles	 * Some recent clarification of the Posix spec say that it
1097194975Sjilles	 * should only generate one....
1098194975Sjilles	 */
1099194975Sjilles	if (had_param_ch || *start != 0) {
11001556Srgrimes		sp = (struct strlist *)stalloc(sizeof *sp);
11011556Srgrimes		sp->text = start;
11021556Srgrimes		*arglist->lastp = sp;
11031556Srgrimes		arglist->lastp = &sp->next;
11041556Srgrimes	}
11051556Srgrimes}
11061556Srgrimes
11071556Srgrimes
1108213760Sobrienstatic char expdir[PATH_MAX];
1109212243Sjilles#define expdir_end (expdir + sizeof(expdir))
11101556Srgrimes
11111556Srgrimes/*
1112212243Sjilles * Perform pathname generation and remove control characters.
1113212243Sjilles * At this point, the only control characters should be CTLESC and CTLQUOTEMARK.
1114212243Sjilles * The results are stored in the list exparg.
11151556Srgrimes */
1116213811Sobrienstatic void
111790111Simpexpandmeta(struct strlist *str, int flag __unused)
111817987Speter{
11191556Srgrimes	char *p;
11201556Srgrimes	struct strlist **savelastp;
11211556Srgrimes	struct strlist *sp;
11221556Srgrimes	char c;
11231556Srgrimes	/* TODO - EXP_REDIR */
11241556Srgrimes
11251556Srgrimes	while (str) {
11261556Srgrimes		if (fflag)
11271556Srgrimes			goto nometa;
11281556Srgrimes		p = str->text;
11291556Srgrimes		for (;;) {			/* fast check for meta chars */
11301556Srgrimes			if ((c = *p++) == '\0')
11311556Srgrimes				goto nometa;
1132211646Sjilles			if (c == '*' || c == '?' || c == '[')
11331556Srgrimes				break;
11341556Srgrimes		}
11351556Srgrimes		savelastp = exparg.lastp;
11361556Srgrimes		INTOFF;
11371556Srgrimes		expmeta(expdir, str->text);
11381556Srgrimes		INTON;
11391556Srgrimes		if (exparg.lastp == savelastp) {
11408855Srgrimes			/*
11418855Srgrimes			 * no matches
11421556Srgrimes			 */
11431556Srgrimesnometa:
11441556Srgrimes			*exparg.lastp = str;
11451556Srgrimes			rmescapes(str->text);
11461556Srgrimes			exparg.lastp = &str->next;
11471556Srgrimes		} else {
11481556Srgrimes			*exparg.lastp = NULL;
11491556Srgrimes			*savelastp = sp = expsort(*savelastp);
11501556Srgrimes			while (sp->next != NULL)
11511556Srgrimes				sp = sp->next;
11521556Srgrimes			exparg.lastp = &sp->next;
11531556Srgrimes		}
11541556Srgrimes		str = str->next;
11551556Srgrimes	}
11561556Srgrimes}
11571556Srgrimes
11581556Srgrimes
11591556Srgrimes/*
11601556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion.
11611556Srgrimes */
11621556Srgrimes
1163213811Sobrienstatic void
116490111Simpexpmeta(char *enddir, char *name)
116590111Simp{
1166248980Sjilles	const char *p;
1167248980Sjilles	const char *q;
1168248980Sjilles	const char *start;
11691556Srgrimes	char *endname;
11701556Srgrimes	int metaflag;
11711556Srgrimes	struct stat statb;
11721556Srgrimes	DIR *dirp;
11731556Srgrimes	struct dirent *dp;
11741556Srgrimes	int atend;
11751556Srgrimes	int matchdot;
1176207944Sjilles	int esc;
1177228941Sjilles	int namlen;
11781556Srgrimes
11791556Srgrimes	metaflag = 0;
11801556Srgrimes	start = name;
1181207944Sjilles	for (p = name; esc = 0, *p; p += esc + 1) {
11821556Srgrimes		if (*p == '*' || *p == '?')
11831556Srgrimes			metaflag = 1;
11841556Srgrimes		else if (*p == '[') {
11851556Srgrimes			q = p + 1;
118626488Sache			if (*q == '!' || *q == '^')
11871556Srgrimes				q++;
11881556Srgrimes			for (;;) {
118938887Stegge				while (*q == CTLQUOTEMARK)
119038887Stegge					q++;
11911556Srgrimes				if (*q == CTLESC)
11921556Srgrimes					q++;
11931556Srgrimes				if (*q == '/' || *q == '\0')
11941556Srgrimes					break;
11951556Srgrimes				if (*++q == ']') {
11961556Srgrimes					metaflag = 1;
11971556Srgrimes					break;
11981556Srgrimes				}
11991556Srgrimes			}
12001556Srgrimes		} else if (*p == '\0')
12011556Srgrimes			break;
120238887Stegge		else if (*p == CTLQUOTEMARK)
120338887Stegge			continue;
1204207944Sjilles		else {
1205207944Sjilles			if (*p == CTLESC)
1206207944Sjilles				esc++;
1207207944Sjilles			if (p[esc] == '/') {
1208207944Sjilles				if (metaflag)
1209207944Sjilles					break;
1210207944Sjilles				start = p + esc + 1;
1211207944Sjilles			}
12121556Srgrimes		}
12131556Srgrimes	}
12141556Srgrimes	if (metaflag == 0) {	/* we've reached the end of the file name */
12151556Srgrimes		if (enddir != expdir)
12161556Srgrimes			metaflag++;
12171556Srgrimes		for (p = name ; ; p++) {
121838887Stegge			if (*p == CTLQUOTEMARK)
121938887Stegge				continue;
12201556Srgrimes			if (*p == CTLESC)
12211556Srgrimes				p++;
12221556Srgrimes			*enddir++ = *p;
12231556Srgrimes			if (*p == '\0')
12241556Srgrimes				break;
1225211155Sjilles			if (enddir == expdir_end)
1226211155Sjilles				return;
12271556Srgrimes		}
1228147812Sdelphij		if (metaflag == 0 || lstat(expdir, &statb) >= 0)
12291556Srgrimes			addfname(expdir);
12301556Srgrimes		return;
12311556Srgrimes	}
1232248980Sjilles	endname = name + (p - name);
12331556Srgrimes	if (start != name) {
12341556Srgrimes		p = name;
12351556Srgrimes		while (p < start) {
123638887Stegge			while (*p == CTLQUOTEMARK)
123738887Stegge				p++;
12381556Srgrimes			if (*p == CTLESC)
12391556Srgrimes				p++;
12401556Srgrimes			*enddir++ = *p++;
1241211155Sjilles			if (enddir == expdir_end)
1242211155Sjilles				return;
12431556Srgrimes		}
12441556Srgrimes	}
12451556Srgrimes	if (enddir == expdir) {
12461556Srgrimes		p = ".";
12471556Srgrimes	} else if (enddir == expdir + 1 && *expdir == '/') {
12481556Srgrimes		p = "/";
12491556Srgrimes	} else {
12501556Srgrimes		p = expdir;
12511556Srgrimes		enddir[-1] = '\0';
12521556Srgrimes	}
12531556Srgrimes	if ((dirp = opendir(p)) == NULL)
12541556Srgrimes		return;
12551556Srgrimes	if (enddir != expdir)
12561556Srgrimes		enddir[-1] = '/';
12571556Srgrimes	if (*endname == 0) {
12581556Srgrimes		atend = 1;
12591556Srgrimes	} else {
12601556Srgrimes		atend = 0;
1261207944Sjilles		*endname = '\0';
1262207944Sjilles		endname += esc + 1;
12631556Srgrimes	}
12641556Srgrimes	matchdot = 0;
126538887Stegge	p = start;
126638887Stegge	while (*p == CTLQUOTEMARK)
126738887Stegge		p++;
126838887Stegge	if (*p == CTLESC)
126938887Stegge		p++;
127038887Stegge	if (*p == '.')
12711556Srgrimes		matchdot++;
12721556Srgrimes	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
12731556Srgrimes		if (dp->d_name[0] == '.' && ! matchdot)
12741556Srgrimes			continue;
127545514Stegge		if (patmatch(start, dp->d_name, 0)) {
1276228941Sjilles			namlen = dp->d_namlen;
1277228941Sjilles			if (enddir + namlen + 1 > expdir_end)
1278211155Sjilles				continue;
1279228941Sjilles			memcpy(enddir, dp->d_name, namlen + 1);
1280211155Sjilles			if (atend)
12811556Srgrimes				addfname(expdir);
1282211155Sjilles			else {
1283228942Sjilles				if (dp->d_type != DT_UNKNOWN &&
1284228942Sjilles				    dp->d_type != DT_DIR &&
1285228942Sjilles				    dp->d_type != DT_LNK)
1286228942Sjilles					continue;
1287228941Sjilles				if (enddir + namlen + 2 > expdir_end)
128817987Speter					continue;
1289228941Sjilles				enddir[namlen] = '/';
1290228941Sjilles				enddir[namlen + 1] = '\0';
1291228941Sjilles				expmeta(enddir + namlen + 1, endname);
12921556Srgrimes			}
12931556Srgrimes		}
12941556Srgrimes	}
12951556Srgrimes	closedir(dirp);
12961556Srgrimes	if (! atend)
1297207944Sjilles		endname[-esc - 1] = esc ? CTLESC : '/';
12981556Srgrimes}
12991556Srgrimes
13001556Srgrimes
13011556Srgrimes/*
13021556Srgrimes * Add a file name to the list.
13031556Srgrimes */
13041556Srgrimes
1305213811Sobrienstatic void
130690111Simpaddfname(char *name)
130790111Simp{
13081556Srgrimes	char *p;
13091556Srgrimes	struct strlist *sp;
13101556Srgrimes
13111556Srgrimes	p = stalloc(strlen(name) + 1);
13121556Srgrimes	scopy(name, p);
13131556Srgrimes	sp = (struct strlist *)stalloc(sizeof *sp);
13141556Srgrimes	sp->text = p;
13151556Srgrimes	*exparg.lastp = sp;
13161556Srgrimes	exparg.lastp = &sp->next;
13171556Srgrimes}
13181556Srgrimes
13191556Srgrimes
13201556Srgrimes/*
13211556Srgrimes * Sort the results of file name expansion.  It calculates the number of
13221556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the
13231556Srgrimes * work.
13241556Srgrimes */
13251556Srgrimes
1326213811Sobrienstatic struct strlist *
132790111Simpexpsort(struct strlist *str)
132890111Simp{
13291556Srgrimes	int len;
13301556Srgrimes	struct strlist *sp;
13311556Srgrimes
13321556Srgrimes	len = 0;
13331556Srgrimes	for (sp = str ; sp ; sp = sp->next)
13341556Srgrimes		len++;
13351556Srgrimes	return msort(str, len);
13361556Srgrimes}
13371556Srgrimes
13381556Srgrimes
1339213811Sobrienstatic struct strlist *
134090111Simpmsort(struct strlist *list, int len)
134117987Speter{
134217987Speter	struct strlist *p, *q = NULL;
13431556Srgrimes	struct strlist **lpp;
13441556Srgrimes	int half;
13451556Srgrimes	int n;
13461556Srgrimes
13471556Srgrimes	if (len <= 1)
13481556Srgrimes		return list;
13498855Srgrimes	half = len >> 1;
13501556Srgrimes	p = list;
13511556Srgrimes	for (n = half ; --n >= 0 ; ) {
13521556Srgrimes		q = p;
13531556Srgrimes		p = p->next;
13541556Srgrimes	}
13551556Srgrimes	q->next = NULL;			/* terminate first half of list */
13561556Srgrimes	q = msort(list, half);		/* sort first half of list */
13571556Srgrimes	p = msort(p, len - half);		/* sort second half */
13581556Srgrimes	lpp = &list;
13591556Srgrimes	for (;;) {
13601556Srgrimes		if (strcmp(p->text, q->text) < 0) {
13611556Srgrimes			*lpp = p;
13621556Srgrimes			lpp = &p->next;
13631556Srgrimes			if ((p = *lpp) == NULL) {
13641556Srgrimes				*lpp = q;
13651556Srgrimes				break;
13661556Srgrimes			}
13671556Srgrimes		} else {
13681556Srgrimes			*lpp = q;
13691556Srgrimes			lpp = &q->next;
13701556Srgrimes			if ((q = *lpp) == NULL) {
13711556Srgrimes				*lpp = p;
13721556Srgrimes				break;
13731556Srgrimes			}
13741556Srgrimes		}
13751556Srgrimes	}
13761556Srgrimes	return list;
13771556Srgrimes}
13781556Srgrimes
13791556Srgrimes
13801556Srgrimes
1381221646Sjillesstatic wchar_t
1382221646Sjillesget_wc(const char **p)
1383221646Sjilles{
1384221646Sjilles	wchar_t c;
1385221646Sjilles	int chrlen;
1386221646Sjilles
1387221646Sjilles	chrlen = mbtowc(&c, *p, 4);
1388221646Sjilles	if (chrlen == 0)
1389221646Sjilles		return 0;
1390221646Sjilles	else if (chrlen == -1)
1391221646Sjilles		c = 0;
1392221646Sjilles	else
1393221646Sjilles		*p += chrlen;
1394221646Sjilles	return c;
1395221646Sjilles}
1396221646Sjilles
1397221646Sjilles
13981556Srgrimes/*
1399223120Sjilles * See if a character matches a character class, starting at the first colon
1400223120Sjilles * of "[:class:]".
1401223120Sjilles * If a valid character class is recognized, a pointer to the next character
1402223120Sjilles * after the final closing bracket is stored into *end, otherwise a null
1403223120Sjilles * pointer is stored into *end.
1404223120Sjilles */
1405223120Sjillesstatic int
1406223120Sjillesmatch_charclass(const char *p, wchar_t chr, const char **end)
1407223120Sjilles{
1408223120Sjilles	char name[20];
1409223120Sjilles	const char *nameend;
1410223120Sjilles	wctype_t cclass;
1411223120Sjilles
1412223120Sjilles	*end = NULL;
1413223120Sjilles	p++;
1414223120Sjilles	nameend = strstr(p, ":]");
1415248980Sjilles	if (nameend == NULL || (size_t)(nameend - p) >= sizeof(name) ||
1416248980Sjilles	    nameend == p)
1417223120Sjilles		return 0;
1418223120Sjilles	memcpy(name, p, nameend - p);
1419223120Sjilles	name[nameend - p] = '\0';
1420223120Sjilles	*end = nameend + 2;
1421223120Sjilles	cclass = wctype(name);
1422223120Sjilles	/* An unknown class matches nothing but is valid nevertheless. */
1423223120Sjilles	if (cclass == 0)
1424223120Sjilles		return 0;
1425223120Sjilles	return iswctype(chr, cclass);
1426223120Sjilles}
1427223120Sjilles
1428223120Sjilles
1429223120Sjilles/*
14301556Srgrimes * Returns true if the pattern matches the string.
14311556Srgrimes */
14321556Srgrimes
1433229220Sjillesstatic int
1434200956Sjillespatmatch(const char *pattern, const char *string, int squoted)
143590111Simp{
1436223120Sjilles	const char *p, *q, *end;
1437229201Sjilles	const char *bt_p, *bt_q;
143825233Ssteve	char c;
1439221646Sjilles	wchar_t wc, wc2;
14401556Srgrimes
14411556Srgrimes	p = pattern;
14421556Srgrimes	q = string;
1443229201Sjilles	bt_p = NULL;
1444229201Sjilles	bt_q = NULL;
14451556Srgrimes	for (;;) {
14461556Srgrimes		switch (c = *p++) {
14471556Srgrimes		case '\0':
1448229201Sjilles			if (*q != '\0')
1449229201Sjilles				goto backtrack;
1450229201Sjilles			return 1;
14511556Srgrimes		case CTLESC:
145245514Stegge			if (squoted && *q == CTLESC)
145345514Stegge				q++;
14541556Srgrimes			if (*q++ != *p++)
1455229201Sjilles				goto backtrack;
14561556Srgrimes			break;
145738887Stegge		case CTLQUOTEMARK:
145838887Stegge			continue;
14591556Srgrimes		case '?':
146045514Stegge			if (squoted && *q == CTLESC)
146145514Stegge				q++;
1462229201Sjilles			if (*q == '\0')
1463229201Sjilles				return 0;
1464229201Sjilles			if (localeisutf8) {
1465221646Sjilles				wc = get_wc(&q);
1466229201Sjilles				/*
1467229201Sjilles				 * A '?' does not match invalid UTF-8 but a
1468229201Sjilles				 * '*' does, so backtrack.
1469229201Sjilles				 */
1470229201Sjilles				if (wc == 0)
1471229201Sjilles					goto backtrack;
1472229201Sjilles			} else
1473223010Sjilles				wc = (unsigned char)*q++;
14741556Srgrimes			break;
14751556Srgrimes		case '*':
14761556Srgrimes			c = *p;
147738887Stegge			while (c == CTLQUOTEMARK || c == '*')
147838887Stegge				c = *++p;
1479229201Sjilles			/*
1480229201Sjilles			 * If the pattern ends here, we know the string
1481229201Sjilles			 * matches without needing to look at the rest of it.
1482229201Sjilles			 */
1483229201Sjilles			if (c == '\0')
1484229201Sjilles				return 1;
1485229201Sjilles			/*
1486229201Sjilles			 * First try the shortest match for the '*' that
1487229201Sjilles			 * could work. We can forget any earlier '*' since
1488229201Sjilles			 * there is no way having it match more characters
1489229201Sjilles			 * can help us, given that we are already here.
1490229201Sjilles			 */
1491229201Sjilles			bt_p = p;
1492229201Sjilles			bt_q = q;
1493229201Sjilles			break;
14941556Srgrimes		case '[': {
1495200956Sjilles			const char *endp;
14961556Srgrimes			int invert, found;
1497221646Sjilles			wchar_t chr;
14981556Srgrimes
14991556Srgrimes			endp = p;
150026488Sache			if (*endp == '!' || *endp == '^')
15011556Srgrimes				endp++;
15021556Srgrimes			for (;;) {
150338887Stegge				while (*endp == CTLQUOTEMARK)
150438887Stegge					endp++;
1505229201Sjilles				if (*endp == 0)
15061556Srgrimes					goto dft;		/* no matching ] */
15071556Srgrimes				if (*endp == CTLESC)
15081556Srgrimes					endp++;
15091556Srgrimes				if (*++endp == ']')
15101556Srgrimes					break;
15111556Srgrimes			}
15121556Srgrimes			invert = 0;
151326488Sache			if (*p == '!' || *p == '^') {
15141556Srgrimes				invert++;
15151556Srgrimes				p++;
15161556Srgrimes			}
15171556Srgrimes			found = 0;
1518221646Sjilles			if (squoted && *q == CTLESC)
1519221646Sjilles				q++;
1520229201Sjilles			if (*q == '\0')
1521229201Sjilles				return 0;
1522229201Sjilles			if (localeisutf8) {
1523221646Sjilles				chr = get_wc(&q);
1524229201Sjilles				if (chr == 0)
1525229201Sjilles					goto backtrack;
1526229201Sjilles			} else
1527223010Sjilles				chr = (unsigned char)*q++;
15281556Srgrimes			c = *p++;
15291556Srgrimes			do {
153038887Stegge				if (c == CTLQUOTEMARK)
153138887Stegge					continue;
1532223120Sjilles				if (c == '[' && *p == ':') {
1533223120Sjilles					found |= match_charclass(p, chr, &end);
1534223120Sjilles					if (end != NULL)
1535223120Sjilles						p = end;
1536223120Sjilles				}
15371556Srgrimes				if (c == CTLESC)
15381556Srgrimes					c = *p++;
1539221646Sjilles				if (localeisutf8 && c & 0x80) {
1540221646Sjilles					p--;
1541221646Sjilles					wc = get_wc(&p);
1542221646Sjilles					if (wc == 0) /* bad utf-8 */
1543221646Sjilles						return 0;
1544221646Sjilles				} else
1545223010Sjilles					wc = (unsigned char)c;
15461556Srgrimes				if (*p == '-' && p[1] != ']') {
15471556Srgrimes					p++;
154838887Stegge					while (*p == CTLQUOTEMARK)
154938887Stegge						p++;
15501556Srgrimes					if (*p == CTLESC)
15511556Srgrimes						p++;
1552221646Sjilles					if (localeisutf8) {
1553221646Sjilles						wc2 = get_wc(&p);
1554221646Sjilles						if (wc2 == 0) /* bad utf-8 */
1555221646Sjilles							return 0;
1556221646Sjilles					} else
1557223010Sjilles						wc2 = (unsigned char)*p++;
1558221646Sjilles					if (   collate_range_cmp(chr, wc) >= 0
1559221646Sjilles					    && collate_range_cmp(chr, wc2) <= 0
156017525Sache					   )
15611556Srgrimes						found = 1;
15621556Srgrimes				} else {
1563221646Sjilles					if (chr == wc)
15641556Srgrimes						found = 1;
15651556Srgrimes				}
15661556Srgrimes			} while ((c = *p++) != ']');
15671556Srgrimes			if (found == invert)
1568229201Sjilles				goto backtrack;
15691556Srgrimes			break;
15701556Srgrimes		}
15711556Srgrimesdft:	        default:
157245514Stegge			if (squoted && *q == CTLESC)
157345514Stegge				q++;
1574229201Sjilles			if (*q == '\0')
15751556Srgrimes				return 0;
1576229201Sjilles			if (*q++ == c)
1577229201Sjilles				break;
1578229201Sjillesbacktrack:
1579229201Sjilles			/*
1580229201Sjilles			 * If we have a mismatch (other than hitting the end
1581229201Sjilles			 * of the string), go back to the last '*' seen and
1582229201Sjilles			 * have it match one additional character.
1583229201Sjilles			 */
1584229201Sjilles			if (bt_p == NULL)
1585229201Sjilles				return 0;
1586229201Sjilles			if (squoted && *bt_q == CTLESC)
1587229201Sjilles				bt_q++;
1588229201Sjilles			if (*bt_q == '\0')
1589229201Sjilles				return 0;
1590229201Sjilles			bt_q++;
1591229201Sjilles			p = bt_p;
1592229201Sjilles			q = bt_q;
15931556Srgrimes			break;
15941556Srgrimes		}
15951556Srgrimes	}
15961556Srgrimes}
15971556Srgrimes
15981556Srgrimes
15991556Srgrimes
16001556Srgrimes/*
1601212243Sjilles * Remove any CTLESC and CTLQUOTEMARK characters from a string.
16021556Srgrimes */
16031556Srgrimes
16041556Srgrimesvoid
160590111Simprmescapes(char *str)
160638887Stegge{
160725233Ssteve	char *p, *q;
16081556Srgrimes
16091556Srgrimes	p = str;
1610214512Sjilles	while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) {
16111556Srgrimes		if (*p++ == '\0')
16121556Srgrimes			return;
16131556Srgrimes	}
16141556Srgrimes	q = p;
16151556Srgrimes	while (*p) {
1616214512Sjilles		if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) {
161738887Stegge			p++;
161838887Stegge			continue;
161938887Stegge		}
16201556Srgrimes		if (*p == CTLESC)
16211556Srgrimes			p++;
16221556Srgrimes		*q++ = *p++;
16231556Srgrimes	}
16241556Srgrimes	*q = '\0';
16251556Srgrimes}
16261556Srgrimes
16271556Srgrimes
16281556Srgrimes
16291556Srgrimes/*
16301556Srgrimes * See if a pattern matches in a case statement.
16311556Srgrimes */
16321556Srgrimes
16331556Srgrimesint
1634200956Sjillescasematch(union node *pattern, const char *val)
163590111Simp{
16361556Srgrimes	struct stackmark smark;
16371556Srgrimes	int result;
16381556Srgrimes	char *p;
16391556Srgrimes
16401556Srgrimes	setstackmark(&smark);
16411556Srgrimes	argbackq = pattern->narg.backquote;
16421556Srgrimes	STARTSTACKSTR(expdest);
16431556Srgrimes	ifslastp = NULL;
16441556Srgrimes	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
16451556Srgrimes	STPUTC('\0', expdest);
16461556Srgrimes	p = grabstackstr(expdest);
164745514Stegge	result = patmatch(p, val, 0);
16481556Srgrimes	popstackmark(&smark);
16491556Srgrimes	return result;
16501556Srgrimes}
165117987Speter
165217987Speter/*
165317987Speter * Our own itoa().
165417987Speter */
165517987Speter
1656213811Sobrienstatic char *
165790111Simpcvtnum(int num, char *buf)
165890111Simp{
165917987Speter	char temp[32];
166017987Speter	int neg = num < 0;
166117987Speter	char *p = temp + 31;
166217987Speter
166317987Speter	temp[31] = '\0';
166417987Speter
166517987Speter	do {
166617987Speter		*--p = num % 10 + '0';
166717987Speter	} while ((num /= 10) != 0);
166817987Speter
166917987Speter	if (neg)
167017987Speter		*--p = '-';
167117987Speter
1672215783Sjilles	STPUTS(p, buf);
167317987Speter	return buf;
167417987Speter}
1675108286Stjr
1676108286Stjr/*
1677108286Stjr * Do most of the work for wordexp(3).
1678108286Stjr */
1679108286Stjr
1680108286Stjrint
1681108286Stjrwordexpcmd(int argc, char **argv)
1682108286Stjr{
1683108286Stjr	size_t len;
1684108286Stjr	int i;
1685108286Stjr
1686108286Stjr	out1fmt("%08x", argc - 1);
1687108286Stjr	for (i = 1, len = 0; i < argc; i++)
1688108286Stjr		len += strlen(argv[i]);
1689108286Stjr	out1fmt("%08x", (int)len);
1690215567Sjilles	for (i = 1; i < argc; i++)
1691215567Sjilles		outbin(argv[i], strlen(argv[i]) + 1, out1);
1692108286Stjr        return (0);
1693108286Stjr}
1694