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);
116231790Sjillesstatic 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
1301556Srgrimes/*
1311556Srgrimes * Expand shell variables and backquotes inside a here document.
13290111Simp *	union node *arg		the document
13390111Simp *	int fd;			where to write the expanded version
1341556Srgrimes */
1351556Srgrimes
1361556Srgrimesvoid
13790111Simpexpandhere(union node *arg, int fd)
13890111Simp{
1391556Srgrimes	expandarg(arg, (struct arglist *)NULL, 0);
14039137Stegge	xwrite(fd, stackblock(), expdest - stackblock());
1411556Srgrimes}
1421556Srgrimes
143216384Sjillesstatic char *
144216384Sjillesstputs_quotes(const char *data, const char *syntax, char *p)
145216384Sjilles{
146216384Sjilles	while (*data) {
147216384Sjilles		CHECKSTRSPACE(2, p);
148216384Sjilles		if (syntax[(int)*data] == CCTL)
149216384Sjilles			USTPUTC(CTLESC, p);
150216384Sjilles		USTPUTC(*data++, p);
151216384Sjilles	}
152216384Sjilles	return (p);
153216384Sjilles}
154216384Sjilles#define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p)
1551556Srgrimes
1561556Srgrimes/*
157212243Sjilles * Perform expansions on an argument, placing the resulting list of arguments
158212243Sjilles * in arglist.  Parameter expansion, command substitution and arithmetic
159212243Sjilles * expansion are always performed; additional expansions can be requested
160212243Sjilles * via flag (EXP_*).
161212243Sjilles * The result is left in the stack string.
162218203Sjilles * When arglist is NULL, perform here document expansion.
163212243Sjilles *
164212243Sjilles * Caution: this function uses global state and is not reentrant.
165212243Sjilles * However, a new invocation after an interrupted invocation is safe
166212243Sjilles * and will reset the global state for the new call.
1671556Srgrimes */
1681556Srgrimesvoid
16990111Simpexpandarg(union node *arg, struct arglist *arglist, int flag)
17017987Speter{
1711556Srgrimes	struct strlist *sp;
1721556Srgrimes	char *p;
1731556Srgrimes
1741556Srgrimes	argbackq = arg->narg.backquote;
1751556Srgrimes	STARTSTACKSTR(expdest);
1761556Srgrimes	ifsfirst.next = NULL;
1771556Srgrimes	ifslastp = NULL;
1781556Srgrimes	argstr(arg->narg.text, flag);
1791556Srgrimes	if (arglist == NULL) {
180222907Sjilles		STACKSTRNUL(expdest);
1811556Srgrimes		return;			/* here document expanded */
1821556Srgrimes	}
1831556Srgrimes	STPUTC('\0', expdest);
1841556Srgrimes	p = grabstackstr(expdest);
1851556Srgrimes	exparg.lastp = &exparg.list;
1861556Srgrimes	/*
1871556Srgrimes	 * TODO - EXP_REDIR
1881556Srgrimes	 */
1891556Srgrimes	if (flag & EXP_FULL) {
1901556Srgrimes		ifsbreakup(p, &exparg);
1911556Srgrimes		*exparg.lastp = NULL;
1921556Srgrimes		exparg.lastp = &exparg.list;
1931556Srgrimes		expandmeta(exparg.list, flag);
1941556Srgrimes	} else {
1951556Srgrimes		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
1961556Srgrimes			rmescapes(p);
1971556Srgrimes		sp = (struct strlist *)stalloc(sizeof (struct strlist));
1981556Srgrimes		sp->text = p;
1991556Srgrimes		*exparg.lastp = sp;
2001556Srgrimes		exparg.lastp = &sp->next;
2011556Srgrimes	}
2021556Srgrimes	while (ifsfirst.next != NULL) {
2031556Srgrimes		struct ifsregion *ifsp;
2041556Srgrimes		INTOFF;
2051556Srgrimes		ifsp = ifsfirst.next->next;
2061556Srgrimes		ckfree(ifsfirst.next);
2071556Srgrimes		ifsfirst.next = ifsp;
2081556Srgrimes		INTON;
2091556Srgrimes	}
2101556Srgrimes	*exparg.lastp = NULL;
2111556Srgrimes	if (exparg.list) {
2121556Srgrimes		*arglist->lastp = exparg.list;
2131556Srgrimes		arglist->lastp = exparg.lastp;
2141556Srgrimes	}
2151556Srgrimes}
2161556Srgrimes
2171556Srgrimes
2181556Srgrimes
2191556Srgrimes/*
220212243Sjilles * Perform parameter expansion, command substitution and arithmetic
221212243Sjilles * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE.
222212243Sjilles * Processing ends at a CTLENDVAR character as well as '\0'.
223212243Sjilles * This is used to expand word in ${var+word} etc.
224212243Sjilles * If EXP_FULL, EXP_CASE or EXP_REDIR are set, keep and/or generate CTLESC
225212243Sjilles * characters to allow for further processing.
226212243Sjilles * If EXP_FULL is set, also preserve CTLQUOTEMARK characters.
2271556Srgrimes */
228213811Sobrienstatic void
22990111Simpargstr(char *p, int flag)
23017987Speter{
23125233Ssteve	char c;
232104672Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);	/* do CTLESC */
2331556Srgrimes	int firsteq = 1;
234214512Sjilles	int split_lit;
235214512Sjilles	int lit_quoted;
2361556Srgrimes
237214512Sjilles	split_lit = flag & EXP_SPLIT_LIT;
238214512Sjilles	lit_quoted = flag & EXP_LIT_QUOTED;
239214512Sjilles	flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED);
2401556Srgrimes	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
2411556Srgrimes		p = exptilde(p, flag);
2421556Srgrimes	for (;;) {
243215783Sjilles		CHECKSTRSPACE(2, expdest);
2441556Srgrimes		switch (c = *p++) {
2451556Srgrimes		case '\0':
246212243Sjilles		case CTLENDVAR:
2471556Srgrimes			goto breakloop;
24838887Stegge		case CTLQUOTEMARK:
249214512Sjilles			lit_quoted = 1;
25038887Stegge			/* "$@" syntax adherence hack */
25138887Stegge			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
25238887Stegge				break;
25339137Stegge			if ((flag & EXP_FULL) != 0)
254215783Sjilles				USTPUTC(c, expdest);
25538887Stegge			break;
256214512Sjilles		case CTLQUOTEEND:
257214512Sjilles			lit_quoted = 0;
258214512Sjilles			break;
2591556Srgrimes		case CTLESC:
2601556Srgrimes			if (quotes)
261215783Sjilles				USTPUTC(c, expdest);
2621556Srgrimes			c = *p++;
263215783Sjilles			USTPUTC(c, expdest);
264214512Sjilles			if (split_lit && !lit_quoted)
265214512Sjilles				recordregion(expdest - stackblock() -
266214512Sjilles				    (quotes ? 2 : 1),
267214512Sjilles				    expdest - stackblock(), 0);
2681556Srgrimes			break;
2691556Srgrimes		case CTLVAR:
2701556Srgrimes			p = evalvar(p, flag);
2711556Srgrimes			break;
2721556Srgrimes		case CTLBACKQ:
2731556Srgrimes		case CTLBACKQ|CTLQUOTE:
2741556Srgrimes			expbackq(argbackq->n, c & CTLQUOTE, flag);
2751556Srgrimes			argbackq = argbackq->next;
2761556Srgrimes			break;
2771556Srgrimes		case CTLENDARI:
2781556Srgrimes			expari(flag);
2791556Srgrimes			break;
2801556Srgrimes		case ':':
2811556Srgrimes		case '=':
2821556Srgrimes			/*
2831556Srgrimes			 * sort of a hack - expand tildes in variable
2841556Srgrimes			 * assignments (after the first '=' and after ':'s).
2851556Srgrimes			 */
286215783Sjilles			USTPUTC(c, expdest);
287214512Sjilles			if (split_lit && !lit_quoted)
288214512Sjilles				recordregion(expdest - stackblock() - 1,
289214512Sjilles				    expdest - stackblock(), 0);
290214512Sjilles			if (flag & EXP_VARTILDE && *p == '~' &&
291214512Sjilles			    (c != '=' || firsteq)) {
292214512Sjilles				if (c == '=')
293214512Sjilles					firsteq = 0;
2941556Srgrimes				p = exptilde(p, flag);
2951556Srgrimes			}
2961556Srgrimes			break;
2971556Srgrimes		default:
298215783Sjilles			USTPUTC(c, expdest);
299214512Sjilles			if (split_lit && !lit_quoted)
300214512Sjilles				recordregion(expdest - stackblock() - 1,
301214512Sjilles				    expdest - stackblock(), 0);
3021556Srgrimes		}
3031556Srgrimes	}
3041556Srgrimesbreakloop:;
3051556Srgrimes}
3061556Srgrimes
307212243Sjilles/*
308212243Sjilles * Perform tilde expansion, placing the result in the stack string and
309212243Sjilles * returning the next position in the input string to process.
310212243Sjilles */
311213811Sobrienstatic char *
31290111Simpexptilde(char *p, int flag)
31317987Speter{
3141556Srgrimes	char c, *startp = p;
3151556Srgrimes	struct passwd *pw;
3161556Srgrimes	char *home;
317108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
3181556Srgrimes
31917987Speter	while ((c = *p) != '\0') {
3201556Srgrimes		switch(c) {
321200988Sjilles		case CTLESC: /* This means CTL* are always considered quoted. */
322200988Sjilles		case CTLVAR:
323200988Sjilles		case CTLBACKQ:
324200988Sjilles		case CTLBACKQ | CTLQUOTE:
325200988Sjilles		case CTLARI:
326200988Sjilles		case CTLENDARI:
32739137Stegge		case CTLQUOTEMARK:
32839137Stegge			return (startp);
3291556Srgrimes		case ':':
3301556Srgrimes			if (flag & EXP_VARTILDE)
3311556Srgrimes				goto done;
3321556Srgrimes			break;
3331556Srgrimes		case '/':
334206150Sjilles		case CTLENDVAR:
3351556Srgrimes			goto done;
3361556Srgrimes		}
3371556Srgrimes		p++;
3381556Srgrimes	}
3391556Srgrimesdone:
3401556Srgrimes	*p = '\0';
3411556Srgrimes	if (*(startp+1) == '\0') {
3421556Srgrimes		if ((home = lookupvar("HOME")) == NULL)
3431556Srgrimes			goto lose;
3441556Srgrimes	} else {
3451556Srgrimes		if ((pw = getpwnam(startp+1)) == NULL)
3461556Srgrimes			goto lose;
3471556Srgrimes		home = pw->pw_dir;
3481556Srgrimes	}
3491556Srgrimes	if (*home == '\0')
3501556Srgrimes		goto lose;
3511556Srgrimes	*p = c;
352216384Sjilles	if (quotes)
353216384Sjilles		STPUTS_QUOTES(home, SQSYNTAX, expdest);
354216384Sjilles	else
355216384Sjilles		STPUTS(home, expdest);
3561556Srgrimes	return (p);
3571556Srgrimeslose:
3581556Srgrimes	*p = c;
3591556Srgrimes	return (startp);
3601556Srgrimes}
3611556Srgrimes
3621556Srgrimes
363213811Sobrienstatic void
36490111Simpremoverecordregions(int endoff)
36538887Stegge{
36638887Stegge	if (ifslastp == NULL)
36738887Stegge		return;
36838887Stegge
36938887Stegge	if (ifsfirst.endoff > endoff) {
37038887Stegge		while (ifsfirst.next != NULL) {
37138887Stegge			struct ifsregion *ifsp;
37238887Stegge			INTOFF;
37338887Stegge			ifsp = ifsfirst.next->next;
37438887Stegge			ckfree(ifsfirst.next);
37538887Stegge			ifsfirst.next = ifsp;
37638887Stegge			INTON;
37738887Stegge		}
37838887Stegge		if (ifsfirst.begoff > endoff)
37938887Stegge			ifslastp = NULL;
38038887Stegge		else {
38138887Stegge			ifslastp = &ifsfirst;
38238887Stegge			ifsfirst.endoff = endoff;
38338887Stegge		}
38438887Stegge		return;
38538887Stegge	}
386155301Sschweikh
38738887Stegge	ifslastp = &ifsfirst;
38838887Stegge	while (ifslastp->next && ifslastp->next->begoff < endoff)
38938887Stegge		ifslastp=ifslastp->next;
39038887Stegge	while (ifslastp->next != NULL) {
39138887Stegge		struct ifsregion *ifsp;
39238887Stegge		INTOFF;
39338887Stegge		ifsp = ifslastp->next->next;
39438887Stegge		ckfree(ifslastp->next);
39538887Stegge		ifslastp->next = ifsp;
39638887Stegge		INTON;
39738887Stegge	}
39838887Stegge	if (ifslastp->endoff > endoff)
39938887Stegge		ifslastp->endoff = endoff;
40038887Stegge}
40138887Stegge
4021556Srgrimes/*
4031556Srgrimes * Expand arithmetic expression.  Backup to start of expression,
4041556Srgrimes * evaluate, place result in (backed up) result, adjust string position.
4051556Srgrimes */
4061556Srgrimesvoid
40790111Simpexpari(int flag)
40817987Speter{
409207206Sjilles	char *p, *q, *start;
410178631Sstefanf	arith_t result;
41138887Stegge	int begoff;
412108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
41338887Stegge	int quoted;
4141556Srgrimes
4151556Srgrimes	/*
41646684Skris	 * This routine is slightly over-complicated for
4171556Srgrimes	 * efficiency.  First we make sure there is
4181556Srgrimes	 * enough space for the result, which may be bigger
419212243Sjilles	 * than the expression.  Next we
4201556Srgrimes	 * scan backwards looking for the start of arithmetic.  If the
4211556Srgrimes	 * next previous character is a CTLESC character, then we
4221556Srgrimes	 * have to rescan starting from the beginning since CTLESC
4238855Srgrimes	 * characters have to be processed left to right.
4241556Srgrimes	 */
425178631Sstefanf	CHECKSTRSPACE(DIGITS(result) - 2, expdest);
4268855Srgrimes	USTPUTC('\0', expdest);
4271556Srgrimes	start = stackblock();
42883676Stegge	p = expdest - 2;
42983676Stegge	while (p >= start && *p != CTLARI)
4301556Srgrimes		--p;
43183676Stegge	if (p < start || *p != CTLARI)
4321556Srgrimes		error("missing CTLARI (shouldn't happen)");
43383676Stegge	if (p > start && *(p - 1) == CTLESC)
4341556Srgrimes		for (p = start; *p != CTLARI; p++)
4351556Srgrimes			if (*p == CTLESC)
4361556Srgrimes				p++;
43738887Stegge
43838887Stegge	if (p[1] == '"')
43938887Stegge		quoted=1;
44038887Stegge	else
44138887Stegge		quoted=0;
44238887Stegge	begoff = p - start;
44338887Stegge	removerecordregions(begoff);
4441556Srgrimes	if (quotes)
44538887Stegge		rmescapes(p+2);
446207206Sjilles	q = grabstackstr(expdest);
44738887Stegge	result = arith(p+2);
448207206Sjilles	ungrabstackstr(q, expdest);
449178631Sstefanf	fmtstr(p, DIGITS(result), ARITH_FORMAT_STR, result);
4501556Srgrimes	while (*p++)
4511556Srgrimes		;
45238887Stegge	if (quoted == 0)
45338887Stegge		recordregion(begoff, p - 1 - start, 0);
4541556Srgrimes	result = expdest - p + 1;
4551556Srgrimes	STADJUST(-result, expdest);
4561556Srgrimes}
4571556Srgrimes
4581556Srgrimes
4591556Srgrimes/*
460212243Sjilles * Perform command substitution.
4611556Srgrimes */
462213811Sobrienstatic void
46390111Simpexpbackq(union node *cmd, int quoted, int flag)
46417987Speter{
4651556Srgrimes	struct backcmd in;
4661556Srgrimes	int i;
4671556Srgrimes	char buf[128];
4681556Srgrimes	char *p;
4691556Srgrimes	char *dest = expdest;
4701556Srgrimes	struct ifsregion saveifs, *savelastp;
4711556Srgrimes	struct nodelist *saveargbackq;
4721556Srgrimes	char lastc;
4731556Srgrimes	int startloc = dest - stackblock();
4741556Srgrimes	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
475108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
476115424Sfenner	int nnl;
4771556Srgrimes
4781556Srgrimes	INTOFF;
4791556Srgrimes	saveifs = ifsfirst;
4801556Srgrimes	savelastp = ifslastp;
4811556Srgrimes	saveargbackq = argbackq;
4821556Srgrimes	p = grabstackstr(dest);
4831556Srgrimes	evalbackcmd(cmd, &in);
4841556Srgrimes	ungrabstackstr(p, dest);
4851556Srgrimes	ifsfirst = saveifs;
4861556Srgrimes	ifslastp = savelastp;
4871556Srgrimes	argbackq = saveargbackq;
4881556Srgrimes
4891556Srgrimes	p = in.buf;
4901556Srgrimes	lastc = '\0';
491115424Sfenner	nnl = 0;
492115424Sfenner	/* Don't copy trailing newlines */
4931556Srgrimes	for (;;) {
4941556Srgrimes		if (--in.nleft < 0) {
4951556Srgrimes			if (in.fd < 0)
4961556Srgrimes				break;
4971556Srgrimes			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
4981556Srgrimes			TRACE(("expbackq: read returns %d\n", i));
4991556Srgrimes			if (i <= 0)
5001556Srgrimes				break;
5011556Srgrimes			p = buf;
5021556Srgrimes			in.nleft = i - 1;
5031556Srgrimes		}
5041556Srgrimes		lastc = *p++;
5051556Srgrimes		if (lastc != '\0') {
506115424Sfenner			if (lastc == '\n') {
507115424Sfenner				nnl++;
508115424Sfenner			} else {
509216706Sjilles				CHECKSTRSPACE(nnl + 2, dest);
510115424Sfenner				while (nnl > 0) {
511115424Sfenner					nnl--;
512216706Sjilles					USTPUTC('\n', dest);
513115424Sfenner				}
514216496Sjilles				if (quotes && syntax[(int)lastc] == CCTL)
515216706Sjilles					USTPUTC(CTLESC, dest);
516216706Sjilles				USTPUTC(lastc, dest);
517115424Sfenner			}
5181556Srgrimes		}
5191556Srgrimes	}
52017987Speter
5211556Srgrimes	if (in.fd >= 0)
5221556Srgrimes		close(in.fd);
5231556Srgrimes	if (in.buf)
5241556Srgrimes		ckfree(in.buf);
5251556Srgrimes	if (in.jp)
52645916Scracauer		exitstatus = waitforjob(in.jp, (int *)NULL);
5271556Srgrimes	if (quoted == 0)
5281556Srgrimes		recordregion(startloc, dest - stackblock(), 0);
529213775Sjhb	TRACE(("expbackq: size=%td: \"%.*s\"\n",
530213775Sjhb		((dest - stackblock()) - startloc),
531213775Sjhb		(int)((dest - stackblock()) - startloc),
5321556Srgrimes		stackblock() + startloc));
5331556Srgrimes	expdest = dest;
5341556Srgrimes	INTON;
5351556Srgrimes}
5361556Srgrimes
5371556Srgrimes
5381556Srgrimes
539213811Sobrienstatic int
54090111Simpsubevalvar(char *p, char *str, int strloc, int subtype, int startloc,
541214524Sjilles  int varflags, int quotes)
54217987Speter{
54317987Speter	char *startp;
54417987Speter	char *loc = NULL;
54545514Stegge	char *q;
54617987Speter	int c = 0;
54717987Speter	struct nodelist *saveargbackq = argbackq;
54820425Ssteve	int amount;
54920425Ssteve
550206150Sjilles	argstr(p, (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX ||
551206147Sjilles	    subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX ?
552206150Sjilles	    EXP_CASE : 0) | EXP_TILDE);
55317987Speter	STACKSTRNUL(expdest);
55417987Speter	argbackq = saveargbackq;
55517987Speter	startp = stackblock() + startloc;
55620425Ssteve	if (str == NULL)
55720425Ssteve	    str = stackblock() + strloc;
55817987Speter
55917987Speter	switch (subtype) {
56017987Speter	case VSASSIGN:
56117987Speter		setvar(str, startp, 0);
56220425Ssteve		amount = startp - expdest;
56320425Ssteve		STADJUST(amount, expdest);
56417987Speter		varflags &= ~VSNUL;
56517987Speter		return 1;
56617987Speter
56717987Speter	case VSQUESTION:
56817987Speter		if (*p != CTLENDVAR) {
569201366Sjilles			outfmt(out2, "%s\n", startp);
57017987Speter			error((char *)NULL);
57117987Speter		}
572112254Sru		error("%.*s: parameter %snot set", (int)(p - str - 1),
57317987Speter		      str, (varflags & VSNUL) ? "null or "
57417987Speter					      : nullstr);
57517987Speter		return 0;
57617987Speter
57717987Speter	case VSTRIMLEFT:
57825233Ssteve		for (loc = startp; loc < str; loc++) {
57917987Speter			c = *loc;
58017987Speter			*loc = '\0';
581214524Sjilles			if (patmatch(str, startp, quotes)) {
58217987Speter				*loc = c;
58317987Speter				goto recordleft;
58417987Speter			}
58517987Speter			*loc = c;
586214524Sjilles			if (quotes && *loc == CTLESC)
58745514Stegge				loc++;
58817987Speter		}
58917987Speter		return 0;
59017987Speter
59117987Speter	case VSTRIMLEFTMAX:
59245514Stegge		for (loc = str - 1; loc >= startp;) {
59317987Speter			c = *loc;
59417987Speter			*loc = '\0';
595214524Sjilles			if (patmatch(str, startp, quotes)) {
59617987Speter				*loc = c;
59717987Speter				goto recordleft;
59817987Speter			}
59917987Speter			*loc = c;
60045514Stegge			loc--;
601214524Sjilles			if (quotes && loc > startp && *(loc - 1) == CTLESC) {
60245514Stegge				for (q = startp; q < loc; q++)
60345514Stegge					if (*q == CTLESC)
60445514Stegge						q++;
60545514Stegge				if (q > loc)
60645514Stegge					loc--;
60745514Stegge			}
60817987Speter		}
60917987Speter		return 0;
61017987Speter
61117987Speter	case VSTRIMRIGHT:
61245514Stegge		for (loc = str - 1; loc >= startp;) {
613214524Sjilles			if (patmatch(str, loc, quotes)) {
61420425Ssteve				amount = loc - expdest;
61520425Ssteve				STADJUST(amount, expdest);
61617987Speter				return 1;
61717987Speter			}
61845514Stegge			loc--;
619214524Sjilles			if (quotes && loc > startp && *(loc - 1) == CTLESC) {
62045514Stegge				for (q = startp; q < loc; q++)
62145514Stegge					if (*q == CTLESC)
62245514Stegge						q++;
62345514Stegge				if (q > loc)
62445514Stegge					loc--;
62545514Stegge			}
62617987Speter		}
62717987Speter		return 0;
62817987Speter
62917987Speter	case VSTRIMRIGHTMAX:
63017987Speter		for (loc = startp; loc < str - 1; loc++) {
631214524Sjilles			if (patmatch(str, loc, quotes)) {
63220425Ssteve				amount = loc - expdest;
63320425Ssteve				STADJUST(amount, expdest);
63417987Speter				return 1;
63517987Speter			}
636214524Sjilles			if (quotes && *loc == CTLESC)
63745514Stegge				loc++;
63817987Speter		}
63917987Speter		return 0;
64017987Speter
64117987Speter
64217987Speter	default:
64317987Speter		abort();
64417987Speter	}
64517987Speter
64617987Speterrecordleft:
64720425Ssteve	amount = ((str - 1) - (loc - startp)) - expdest;
64820425Ssteve	STADJUST(amount, expdest);
64917987Speter	while (loc != str - 1)
65017987Speter		*startp++ = *loc++;
65117987Speter	return 1;
65217987Speter}
65317987Speter
65417987Speter
6551556Srgrimes/*
6561556Srgrimes * Expand a variable, and return a pointer to the next character in the
6571556Srgrimes * input string.
6581556Srgrimes */
6591556Srgrimes
660213811Sobrienstatic char *
66190111Simpevalvar(char *p, int flag)
66217987Speter{
6631556Srgrimes	int subtype;
6641556Srgrimes	int varflags;
6651556Srgrimes	char *var;
6661556Srgrimes	char *val;
66745644Stegge	int patloc;
6681556Srgrimes	int c;
6691556Srgrimes	int set;
6701556Srgrimes	int special;
6711556Srgrimes	int startloc;
67217987Speter	int varlen;
673221602Sjilles	int varlenb;
67417987Speter	int easy;
675108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
6761556Srgrimes
677164081Sstefanf	varflags = (unsigned char)*p++;
6781556Srgrimes	subtype = varflags & VSTYPE;
6791556Srgrimes	var = p;
6801556Srgrimes	special = 0;
6811556Srgrimes	if (! is_name(*p))
6821556Srgrimes		special = 1;
6831556Srgrimes	p = strchr(p, '=') + 1;
6841556Srgrimesagain: /* jump here after setting a variable with ${var=text} */
685179022Sstefanf	if (varflags & VSLINENO) {
686179022Sstefanf		set = 1;
687179022Sstefanf		special = 0;
688179022Sstefanf		val = var;
689179022Sstefanf		p[-1] = '\0';	/* temporarily overwrite '=' to have \0
690179022Sstefanf				   terminated string */
691179022Sstefanf	} else if (special) {
69225233Ssteve		set = varisset(var, varflags & VSNUL);
6931556Srgrimes		val = NULL;
6941556Srgrimes	} else {
69560592Scracauer		val = bltinlookup(var, 1);
69617987Speter		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
6971556Srgrimes			val = NULL;
6981556Srgrimes			set = 0;
6991556Srgrimes		} else
7001556Srgrimes			set = 1;
7011556Srgrimes	}
70217987Speter	varlen = 0;
7031556Srgrimes	startloc = expdest - stackblock();
704198454Sjilles	if (!set && uflag && *var != '@' && *var != '*') {
70596939Stjr		switch (subtype) {
70696939Stjr		case VSNORMAL:
70796939Stjr		case VSTRIMLEFT:
70896939Stjr		case VSTRIMLEFTMAX:
70996939Stjr		case VSTRIMRIGHT:
71096939Stjr		case VSTRIMRIGHTMAX:
71196939Stjr		case VSLENGTH:
712112254Sru			error("%.*s: parameter not set", (int)(p - var - 1),
713112254Sru			    var);
71496939Stjr		}
71596939Stjr	}
7161556Srgrimes	if (set && subtype != VSPLUS) {
7171556Srgrimes		/* insert the value of the variable */
7181556Srgrimes		if (special) {
719164081Sstefanf			varvalue(var, varflags & VSQUOTE, subtype, flag);
72017987Speter			if (subtype == VSLENGTH) {
721221602Sjilles				varlenb = expdest - stackblock() - startloc;
722221602Sjilles				varlen = varlenb;
723221602Sjilles				if (localeisutf8) {
724221602Sjilles					val = stackblock() + startloc;
725221602Sjilles					for (;val != expdest; val++)
726221602Sjilles						if ((*val & 0xC0) == 0x80)
727221602Sjilles							varlen--;
728221602Sjilles				}
729221602Sjilles				STADJUST(-varlenb, expdest);
73017987Speter			}
7311556Srgrimes		} else {
73220425Ssteve			char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
73317987Speter								  : BASESYNTAX;
7341556Srgrimes
73517987Speter			if (subtype == VSLENGTH) {
73617987Speter				for (;*val; val++)
737221602Sjilles					if (!localeisutf8 ||
738221602Sjilles					    (*val & 0xC0) != 0x80)
739221602Sjilles						varlen++;
7401556Srgrimes			}
74117987Speter			else {
742216384Sjilles				if (quotes)
743216384Sjilles					STPUTS_QUOTES(val, syntax, expdest);
744216384Sjilles				else
745216384Sjilles					STPUTS(val, expdest);
74617987Speter
74717987Speter			}
7481556Srgrimes		}
7491556Srgrimes	}
75020425Ssteve
7511556Srgrimes	if (subtype == VSPLUS)
7521556Srgrimes		set = ! set;
75317987Speter
75420425Ssteve	easy = ((varflags & VSQUOTE) == 0 ||
75517987Speter		(*var == '@' && shellparam.nparam != 1));
75617987Speter
75717987Speter
75817987Speter	switch (subtype) {
75917987Speter	case VSLENGTH:
76017987Speter		expdest = cvtnum(varlen, expdest);
76117987Speter		goto record;
76217987Speter
76317987Speter	case VSNORMAL:
76417987Speter		if (!easy)
76517987Speter			break;
76617987Speterrecord:
76720425Ssteve		recordregion(startloc, expdest - stackblock(),
768222361Sjilles		    varflags & VSQUOTE || (ifsset() && ifsval()[0] == '\0' &&
769222361Sjilles		    (*var == '@' || *var == '*')));
77017987Speter		break;
77117987Speter
77217987Speter	case VSPLUS:
77317987Speter	case VSMINUS:
77417987Speter		if (!set) {
775214512Sjilles			argstr(p, flag | (flag & EXP_FULL ? EXP_SPLIT_LIT : 0) |
776214512Sjilles			    (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0));
77717987Speter			break;
77817987Speter		}
77917987Speter		if (easy)
78017987Speter			goto record;
78117987Speter		break;
78217987Speter
78317987Speter	case VSTRIMLEFT:
78417987Speter	case VSTRIMLEFTMAX:
78517987Speter	case VSTRIMRIGHT:
78617987Speter	case VSTRIMRIGHTMAX:
78717987Speter		if (!set)
78817987Speter			break;
78917987Speter		/*
79017987Speter		 * Terminate the string and start recording the pattern
79117987Speter		 * right after it
79217987Speter		 */
79317987Speter		STPUTC('\0', expdest);
79445644Stegge		patloc = expdest - stackblock();
79545644Stegge		if (subevalvar(p, NULL, patloc, subtype,
796214524Sjilles		    startloc, varflags, quotes) == 0) {
79745644Stegge			int amount = (expdest - stackblock() - patloc) + 1;
79825233Ssteve			STADJUST(-amount, expdest);
79925233Ssteve		}
80038887Stegge		/* Remove any recorded regions beyond start of variable */
80138887Stegge		removerecordregions(startloc);
80238887Stegge		goto record;
80317987Speter
80417987Speter	case VSASSIGN:
80517987Speter	case VSQUESTION:
80617987Speter		if (!set) {
807214524Sjilles			if (subevalvar(p, var, 0, subtype, startloc, varflags,
808214524Sjilles			    quotes)) {
80920425Ssteve				varflags &= ~VSNUL;
810155301Sschweikh				/*
811155301Sschweikh				 * Remove any recorded regions beyond
812155301Sschweikh				 * start of variable
81338887Stegge				 */
81438887Stegge				removerecordregions(startloc);
8151556Srgrimes				goto again;
81620425Ssteve			}
81717987Speter			break;
8181556Srgrimes		}
81917987Speter		if (easy)
82017987Speter			goto record;
82117987Speter		break;
82217987Speter
823164003Sstefanf	case VSERROR:
824164003Sstefanf		c = p - var - 1;
825164003Sstefanf		error("${%.*s%s}: Bad substitution", c, var,
826164003Sstefanf		    (c > 0 && *p != CTLENDVAR) ? "..." : "");
827164003Sstefanf
82817987Speter	default:
82917987Speter		abort();
8301556Srgrimes	}
831179022Sstefanf	p[-1] = '=';	/* recover overwritten '=' */
83217987Speter
8331556Srgrimes	if (subtype != VSNORMAL) {	/* skip to end of alternative */
8341556Srgrimes		int nesting = 1;
8351556Srgrimes		for (;;) {
8361556Srgrimes			if ((c = *p++) == CTLESC)
8371556Srgrimes				p++;
8381556Srgrimes			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
8391556Srgrimes				if (set)
8401556Srgrimes					argbackq = argbackq->next;
8411556Srgrimes			} else if (c == CTLVAR) {
8421556Srgrimes				if ((*p++ & VSTYPE) != VSNORMAL)
8431556Srgrimes					nesting++;
8441556Srgrimes			} else if (c == CTLENDVAR) {
8451556Srgrimes				if (--nesting == 0)
8461556Srgrimes					break;
8471556Srgrimes			}
8481556Srgrimes		}
8491556Srgrimes	}
8501556Srgrimes	return p;
8511556Srgrimes}
8521556Srgrimes
8531556Srgrimes
8541556Srgrimes
8551556Srgrimes/*
8561556Srgrimes * Test whether a specialized variable is set.
8571556Srgrimes */
8581556Srgrimes
859213811Sobrienstatic int
86090111Simpvarisset(char *name, int nulok)
86125233Ssteve{
8621556Srgrimes
86325233Ssteve	if (*name == '!')
864209600Sjilles		return backgndpidset();
86525233Ssteve	else if (*name == '@' || *name == '*') {
8661556Srgrimes		if (*shellparam.p == NULL)
8671556Srgrimes			return 0;
86825233Ssteve
86925233Ssteve		if (nulok) {
87025233Ssteve			char **av;
87125233Ssteve
87225233Ssteve			for (av = shellparam.p; *av; av++)
87325233Ssteve				if (**av != '\0')
87425233Ssteve					return 1;
87525233Ssteve			return 0;
87625233Ssteve		}
87718202Speter	} else if (is_digit(*name)) {
87825233Ssteve		char *ap;
87920425Ssteve		int num = atoi(name);
88025233Ssteve
88125233Ssteve		if (num > shellparam.nparam)
88225233Ssteve			return 0;
88325233Ssteve
88425233Ssteve		if (num == 0)
88525233Ssteve			ap = arg0;
88625233Ssteve		else
88725233Ssteve			ap = shellparam.p[num - 1];
88825233Ssteve
88925233Ssteve		if (nulok && (ap == NULL || *ap == '\0'))
89025233Ssteve			return 0;
8911556Srgrimes	}
8921556Srgrimes	return 1;
8931556Srgrimes}
8941556Srgrimes
895216384Sjillesstatic void
896216384Sjillesstrtodest(const char *p, int flag, int subtype, int quoted)
897216384Sjilles{
898216384Sjilles	if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH)
899216384Sjilles		STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest);
900216384Sjilles	else
901216384Sjilles		STPUTS(p, expdest);
902216384Sjilles}
9031556Srgrimes
9041556Srgrimes/*
9051556Srgrimes * Add the value of a specialized variable to the stack string.
9061556Srgrimes */
9071556Srgrimes
908213811Sobrienstatic void
909164081Sstefanfvarvalue(char *name, int quoted, int subtype, int flag)
91017987Speter{
9111556Srgrimes	int num;
9121556Srgrimes	char *p;
9131556Srgrimes	int i;
9141556Srgrimes	char sep;
9151556Srgrimes	char **ap;
9161556Srgrimes
91718202Speter	switch (*name) {
9181556Srgrimes	case '$':
9191556Srgrimes		num = rootpid;
9201556Srgrimes		goto numvar;
9211556Srgrimes	case '?':
92217987Speter		num = oexitstatus;
9231556Srgrimes		goto numvar;
9241556Srgrimes	case '#':
9251556Srgrimes		num = shellparam.nparam;
9261556Srgrimes		goto numvar;
9271556Srgrimes	case '!':
928209600Sjilles		num = backgndpidval();
9291556Srgrimesnumvar:
93017987Speter		expdest = cvtnum(num, expdest);
9311556Srgrimes		break;
9321556Srgrimes	case '-':
9331556Srgrimes		for (i = 0 ; i < NOPTS ; i++) {
9341556Srgrimes			if (optlist[i].val)
9351556Srgrimes				STPUTC(optlist[i].letter, expdest);
9361556Srgrimes		}
9371556Srgrimes		break;
9381556Srgrimes	case '@':
939164081Sstefanf		if (flag & EXP_FULL && quoted) {
94038887Stegge			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
941216384Sjilles				strtodest(p, flag, subtype, quoted);
94238887Stegge				if (*ap)
94338887Stegge					STPUTC('\0', expdest);
94438887Stegge			}
94538887Stegge			break;
9461556Srgrimes		}
947102410Scharnier		/* FALLTHROUGH */
9481556Srgrimes	case '*':
949149825Srse		if (ifsset())
95038887Stegge			sep = ifsval()[0];
95138887Stegge		else
95238887Stegge			sep = ' ';
9531556Srgrimes		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
954216384Sjilles			strtodest(p, flag, subtype, quoted);
955222361Sjilles			if (!*ap)
956222361Sjilles				break;
957222361Sjilles			if (sep || (flag & EXP_FULL && !quoted && **ap != '\0'))
9581556Srgrimes				STPUTC(sep, expdest);
9591556Srgrimes		}
9601556Srgrimes		break;
9611556Srgrimes	case '0':
9621556Srgrimes		p = arg0;
963216384Sjilles		strtodest(p, flag, subtype, quoted);
9641556Srgrimes		break;
9651556Srgrimes	default:
96618202Speter		if (is_digit(*name)) {
96718202Speter			num = atoi(name);
96818202Speter			if (num > 0 && num <= shellparam.nparam) {
96918202Speter				p = shellparam.p[num - 1];
970216384Sjilles				strtodest(p, flag, subtype, quoted);
97118202Speter			}
9721556Srgrimes		}
9731556Srgrimes		break;
9741556Srgrimes	}
9751556Srgrimes}
9761556Srgrimes
9771556Srgrimes
9781556Srgrimes
9791556Srgrimes/*
980218909Sbrucec * Record the fact that we have to scan this region of the
9811556Srgrimes * string for IFS characters.
9821556Srgrimes */
9831556Srgrimes
984213811Sobrienstatic void
985194975Sjillesrecordregion(int start, int end, int inquotes)
98617987Speter{
98725233Ssteve	struct ifsregion *ifsp;
9881556Srgrimes
989264629Sjilles	INTOFF;
9901556Srgrimes	if (ifslastp == NULL) {
9911556Srgrimes		ifsp = &ifsfirst;
9921556Srgrimes	} else {
993194975Sjilles		if (ifslastp->endoff == start
994194975Sjilles		    && ifslastp->inquotes == inquotes) {
995194975Sjilles			/* extend previous area */
996194975Sjilles			ifslastp->endoff = end;
997264629Sjilles			INTON;
998194975Sjilles			return;
999194975Sjilles		}
10001556Srgrimes		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
10011556Srgrimes		ifslastp->next = ifsp;
10021556Srgrimes	}
10031556Srgrimes	ifslastp = ifsp;
10041556Srgrimes	ifslastp->next = NULL;
10051556Srgrimes	ifslastp->begoff = start;
10061556Srgrimes	ifslastp->endoff = end;
1007194975Sjilles	ifslastp->inquotes = inquotes;
1008264629Sjilles	INTON;
10091556Srgrimes}
10101556Srgrimes
10111556Srgrimes
10121556Srgrimes
10131556Srgrimes/*
10141556Srgrimes * Break the argument string into pieces based upon IFS and add the
10151556Srgrimes * strings to the argument list.  The regions of the string to be
10161556Srgrimes * searched for IFS characters have been stored by recordregion.
1017212243Sjilles * CTLESC characters are preserved but have little effect in this pass
1018212243Sjilles * other than escaping CTL* characters.  In particular, they do not escape
1019212243Sjilles * IFS characters: that should be done with the ifsregion mechanism.
1020212243Sjilles * CTLQUOTEMARK characters are used to preserve empty quoted strings.
1021212243Sjilles * This pass treats them as a regular character, making the string non-empty.
1022212243Sjilles * Later, they are removed along with the other CTL* characters.
10231556Srgrimes */
1024213811Sobrienstatic void
102590111Simpifsbreakup(char *string, struct arglist *arglist)
102690111Simp{
10271556Srgrimes	struct ifsregion *ifsp;
10281556Srgrimes	struct strlist *sp;
10291556Srgrimes	char *start;
103025233Ssteve	char *p;
10311556Srgrimes	char *q;
1032201053Sjilles	const char *ifs;
1033194975Sjilles	const char *ifsspc;
1034194975Sjilles	int had_param_ch = 0;
10351556Srgrimes
1036194975Sjilles	start = string;
103717987Speter
1038194975Sjilles	if (ifslastp == NULL) {
1039194975Sjilles		/* Return entire argument, IFS doesn't apply to any of it */
1040194975Sjilles		sp = (struct strlist *)stalloc(sizeof *sp);
1041194975Sjilles		sp->text = start;
1042194975Sjilles		*arglist->lastp = sp;
1043194975Sjilles		arglist->lastp = &sp->next;
1044194975Sjilles		return;
1045194975Sjilles	}
1046194975Sjilles
1047194975Sjilles	ifs = ifsset() ? ifsval() : " \t\n";
1048194975Sjilles
1049194975Sjilles	for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
1050194975Sjilles		p = string + ifsp->begoff;
1051194975Sjilles		while (p < string + ifsp->endoff) {
1052194975Sjilles			q = p;
1053194975Sjilles			if (*p == CTLESC)
1054194975Sjilles				p++;
1055194975Sjilles			if (ifsp->inquotes) {
1056194975Sjilles				/* Only NULs (should be from "$@") end args */
1057194977Sjilles				had_param_ch = 1;
1058194975Sjilles				if (*p != 0) {
10591556Srgrimes					p++;
1060194975Sjilles					continue;
1061194975Sjilles				}
1062194975Sjilles				ifsspc = NULL;
1063194975Sjilles			} else {
1064194975Sjilles				if (!strchr(ifs, *p)) {
1065194977Sjilles					had_param_ch = 1;
106638887Stegge					p++;
1067194975Sjilles					continue;
1068194975Sjilles				}
1069194975Sjilles				ifsspc = strchr(" \t\n", *p);
1070194975Sjilles
1071194975Sjilles				/* Ignore IFS whitespace at start */
1072194975Sjilles				if (q == start && ifsspc != NULL) {
1073194975Sjilles					p++;
10741556Srgrimes					start = p;
1075194975Sjilles					continue;
1076194975Sjilles				}
1077194977Sjilles				had_param_ch = 0;
10781556Srgrimes			}
1079194975Sjilles
1080194975Sjilles			/* Save this argument... */
1081194975Sjilles			*q = '\0';
10821556Srgrimes			sp = (struct strlist *)stalloc(sizeof *sp);
10831556Srgrimes			sp->text = start;
10841556Srgrimes			*arglist->lastp = sp;
10851556Srgrimes			arglist->lastp = &sp->next;
1086194975Sjilles			p++;
1087194975Sjilles
1088194975Sjilles			if (ifsspc != NULL) {
1089194975Sjilles				/* Ignore further trailing IFS whitespace */
1090194975Sjilles				for (; p < string + ifsp->endoff; p++) {
1091194975Sjilles					q = p;
1092194975Sjilles					if (*p == CTLESC)
1093194975Sjilles						p++;
1094194975Sjilles					if (strchr(ifs, *p) == NULL) {
1095194975Sjilles						p = q;
1096194975Sjilles						break;
1097194975Sjilles					}
1098194975Sjilles					if (strchr(" \t\n", *p) == NULL) {
1099194975Sjilles						p++;
1100194975Sjilles						break;
1101194975Sjilles					}
1102194975Sjilles				}
1103194975Sjilles			}
1104194975Sjilles			start = p;
11051556Srgrimes		}
1106194975Sjilles	}
1107194975Sjilles
1108194975Sjilles	/*
1109194975Sjilles	 * Save anything left as an argument.
1110194975Sjilles	 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
1111194975Sjilles	 * generating 2 arguments, the second of which is empty.
1112194975Sjilles	 * Some recent clarification of the Posix spec say that it
1113194975Sjilles	 * should only generate one....
1114194975Sjilles	 */
1115194975Sjilles	if (had_param_ch || *start != 0) {
11161556Srgrimes		sp = (struct strlist *)stalloc(sizeof *sp);
11171556Srgrimes		sp->text = start;
11181556Srgrimes		*arglist->lastp = sp;
11191556Srgrimes		arglist->lastp = &sp->next;
11201556Srgrimes	}
11211556Srgrimes}
11221556Srgrimes
11231556Srgrimes
1124213760Sobrienstatic char expdir[PATH_MAX];
1125212243Sjilles#define expdir_end (expdir + sizeof(expdir))
11261556Srgrimes
11271556Srgrimes/*
1128212243Sjilles * Perform pathname generation and remove control characters.
1129212243Sjilles * At this point, the only control characters should be CTLESC and CTLQUOTEMARK.
1130212243Sjilles * The results are stored in the list exparg.
11311556Srgrimes */
1132213811Sobrienstatic void
113390111Simpexpandmeta(struct strlist *str, int flag __unused)
113417987Speter{
11351556Srgrimes	char *p;
11361556Srgrimes	struct strlist **savelastp;
11371556Srgrimes	struct strlist *sp;
11381556Srgrimes	char c;
11391556Srgrimes	/* TODO - EXP_REDIR */
11401556Srgrimes
11411556Srgrimes	while (str) {
11421556Srgrimes		if (fflag)
11431556Srgrimes			goto nometa;
11441556Srgrimes		p = str->text;
11451556Srgrimes		for (;;) {			/* fast check for meta chars */
11461556Srgrimes			if ((c = *p++) == '\0')
11471556Srgrimes				goto nometa;
1148211646Sjilles			if (c == '*' || c == '?' || c == '[')
11491556Srgrimes				break;
11501556Srgrimes		}
11511556Srgrimes		savelastp = exparg.lastp;
11521556Srgrimes		INTOFF;
11531556Srgrimes		expmeta(expdir, str->text);
11541556Srgrimes		INTON;
11551556Srgrimes		if (exparg.lastp == savelastp) {
11568855Srgrimes			/*
11578855Srgrimes			 * no matches
11581556Srgrimes			 */
11591556Srgrimesnometa:
11601556Srgrimes			*exparg.lastp = str;
11611556Srgrimes			rmescapes(str->text);
11621556Srgrimes			exparg.lastp = &str->next;
11631556Srgrimes		} else {
11641556Srgrimes			*exparg.lastp = NULL;
11651556Srgrimes			*savelastp = sp = expsort(*savelastp);
11661556Srgrimes			while (sp->next != NULL)
11671556Srgrimes				sp = sp->next;
11681556Srgrimes			exparg.lastp = &sp->next;
11691556Srgrimes		}
11701556Srgrimes		str = str->next;
11711556Srgrimes	}
11721556Srgrimes}
11731556Srgrimes
11741556Srgrimes
11751556Srgrimes/*
11761556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion.
11771556Srgrimes */
11781556Srgrimes
1179213811Sobrienstatic void
118090111Simpexpmeta(char *enddir, char *name)
118190111Simp{
118225233Ssteve	char *p;
11831556Srgrimes	char *q;
11841556Srgrimes	char *start;
11851556Srgrimes	char *endname;
11861556Srgrimes	int metaflag;
11871556Srgrimes	struct stat statb;
11881556Srgrimes	DIR *dirp;
11891556Srgrimes	struct dirent *dp;
11901556Srgrimes	int atend;
11911556Srgrimes	int matchdot;
1192207944Sjilles	int esc;
11931556Srgrimes
11941556Srgrimes	metaflag = 0;
11951556Srgrimes	start = name;
1196207944Sjilles	for (p = name; esc = 0, *p; p += esc + 1) {
11971556Srgrimes		if (*p == '*' || *p == '?')
11981556Srgrimes			metaflag = 1;
11991556Srgrimes		else if (*p == '[') {
12001556Srgrimes			q = p + 1;
120126488Sache			if (*q == '!' || *q == '^')
12021556Srgrimes				q++;
12031556Srgrimes			for (;;) {
120438887Stegge				while (*q == CTLQUOTEMARK)
120538887Stegge					q++;
12061556Srgrimes				if (*q == CTLESC)
12071556Srgrimes					q++;
12081556Srgrimes				if (*q == '/' || *q == '\0')
12091556Srgrimes					break;
12101556Srgrimes				if (*++q == ']') {
12111556Srgrimes					metaflag = 1;
12121556Srgrimes					break;
12131556Srgrimes				}
12141556Srgrimes			}
12151556Srgrimes		} else if (*p == '\0')
12161556Srgrimes			break;
121738887Stegge		else if (*p == CTLQUOTEMARK)
121838887Stegge			continue;
1219207944Sjilles		else {
1220207944Sjilles			if (*p == CTLESC)
1221207944Sjilles				esc++;
1222207944Sjilles			if (p[esc] == '/') {
1223207944Sjilles				if (metaflag)
1224207944Sjilles					break;
1225207944Sjilles				start = p + esc + 1;
1226207944Sjilles			}
12271556Srgrimes		}
12281556Srgrimes	}
12291556Srgrimes	if (metaflag == 0) {	/* we've reached the end of the file name */
12301556Srgrimes		if (enddir != expdir)
12311556Srgrimes			metaflag++;
12321556Srgrimes		for (p = name ; ; p++) {
123338887Stegge			if (*p == CTLQUOTEMARK)
123438887Stegge				continue;
12351556Srgrimes			if (*p == CTLESC)
12361556Srgrimes				p++;
12371556Srgrimes			*enddir++ = *p;
12381556Srgrimes			if (*p == '\0')
12391556Srgrimes				break;
1240211155Sjilles			if (enddir == expdir_end)
1241211155Sjilles				return;
12421556Srgrimes		}
1243147812Sdelphij		if (metaflag == 0 || lstat(expdir, &statb) >= 0)
12441556Srgrimes			addfname(expdir);
12451556Srgrimes		return;
12461556Srgrimes	}
12471556Srgrimes	endname = p;
12481556Srgrimes	if (start != name) {
12491556Srgrimes		p = name;
12501556Srgrimes		while (p < start) {
125138887Stegge			while (*p == CTLQUOTEMARK)
125238887Stegge				p++;
12531556Srgrimes			if (*p == CTLESC)
12541556Srgrimes				p++;
12551556Srgrimes			*enddir++ = *p++;
1256211155Sjilles			if (enddir == expdir_end)
1257211155Sjilles				return;
12581556Srgrimes		}
12591556Srgrimes	}
12601556Srgrimes	if (enddir == expdir) {
12611556Srgrimes		p = ".";
12621556Srgrimes	} else if (enddir == expdir + 1 && *expdir == '/') {
12631556Srgrimes		p = "/";
12641556Srgrimes	} else {
12651556Srgrimes		p = expdir;
12661556Srgrimes		enddir[-1] = '\0';
12671556Srgrimes	}
12681556Srgrimes	if ((dirp = opendir(p)) == NULL)
12691556Srgrimes		return;
12701556Srgrimes	if (enddir != expdir)
12711556Srgrimes		enddir[-1] = '/';
12721556Srgrimes	if (*endname == 0) {
12731556Srgrimes		atend = 1;
12741556Srgrimes	} else {
12751556Srgrimes		atend = 0;
1276207944Sjilles		*endname = '\0';
1277207944Sjilles		endname += esc + 1;
12781556Srgrimes	}
12791556Srgrimes	matchdot = 0;
128038887Stegge	p = start;
128138887Stegge	while (*p == CTLQUOTEMARK)
128238887Stegge		p++;
128338887Stegge	if (*p == CTLESC)
128438887Stegge		p++;
128538887Stegge	if (*p == '.')
12861556Srgrimes		matchdot++;
12871556Srgrimes	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
12881556Srgrimes		if (dp->d_name[0] == '.' && ! matchdot)
12891556Srgrimes			continue;
129045514Stegge		if (patmatch(start, dp->d_name, 0)) {
1291211155Sjilles			if (enddir + dp->d_namlen + 1 > expdir_end)
1292211155Sjilles				continue;
1293211155Sjilles			memcpy(enddir, dp->d_name, dp->d_namlen + 1);
1294211155Sjilles			if (atend)
12951556Srgrimes				addfname(expdir);
1296211155Sjilles			else {
1297211155Sjilles				if (enddir + dp->d_namlen + 2 > expdir_end)
129817987Speter					continue;
1299211155Sjilles				enddir[dp->d_namlen] = '/';
1300211155Sjilles				enddir[dp->d_namlen + 1] = '\0';
1301211155Sjilles				expmeta(enddir + dp->d_namlen + 1, endname);
13021556Srgrimes			}
13031556Srgrimes		}
13041556Srgrimes	}
13051556Srgrimes	closedir(dirp);
13061556Srgrimes	if (! atend)
1307207944Sjilles		endname[-esc - 1] = esc ? CTLESC : '/';
13081556Srgrimes}
13091556Srgrimes
13101556Srgrimes
13111556Srgrimes/*
13121556Srgrimes * Add a file name to the list.
13131556Srgrimes */
13141556Srgrimes
1315213811Sobrienstatic void
131690111Simpaddfname(char *name)
131790111Simp{
13181556Srgrimes	char *p;
13191556Srgrimes	struct strlist *sp;
13201556Srgrimes
13211556Srgrimes	p = stalloc(strlen(name) + 1);
13221556Srgrimes	scopy(name, p);
13231556Srgrimes	sp = (struct strlist *)stalloc(sizeof *sp);
13241556Srgrimes	sp->text = p;
13251556Srgrimes	*exparg.lastp = sp;
13261556Srgrimes	exparg.lastp = &sp->next;
13271556Srgrimes}
13281556Srgrimes
13291556Srgrimes
13301556Srgrimes/*
13311556Srgrimes * Sort the results of file name expansion.  It calculates the number of
13321556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the
13331556Srgrimes * work.
13341556Srgrimes */
13351556Srgrimes
1336213811Sobrienstatic struct strlist *
133790111Simpexpsort(struct strlist *str)
133890111Simp{
13391556Srgrimes	int len;
13401556Srgrimes	struct strlist *sp;
13411556Srgrimes
13421556Srgrimes	len = 0;
13431556Srgrimes	for (sp = str ; sp ; sp = sp->next)
13441556Srgrimes		len++;
13451556Srgrimes	return msort(str, len);
13461556Srgrimes}
13471556Srgrimes
13481556Srgrimes
1349213811Sobrienstatic struct strlist *
135090111Simpmsort(struct strlist *list, int len)
135117987Speter{
135217987Speter	struct strlist *p, *q = NULL;
13531556Srgrimes	struct strlist **lpp;
13541556Srgrimes	int half;
13551556Srgrimes	int n;
13561556Srgrimes
13571556Srgrimes	if (len <= 1)
13581556Srgrimes		return list;
13598855Srgrimes	half = len >> 1;
13601556Srgrimes	p = list;
13611556Srgrimes	for (n = half ; --n >= 0 ; ) {
13621556Srgrimes		q = p;
13631556Srgrimes		p = p->next;
13641556Srgrimes	}
13651556Srgrimes	q->next = NULL;			/* terminate first half of list */
13661556Srgrimes	q = msort(list, half);		/* sort first half of list */
13671556Srgrimes	p = msort(p, len - half);		/* sort second half */
13681556Srgrimes	lpp = &list;
13691556Srgrimes	for (;;) {
13701556Srgrimes		if (strcmp(p->text, q->text) < 0) {
13711556Srgrimes			*lpp = p;
13721556Srgrimes			lpp = &p->next;
13731556Srgrimes			if ((p = *lpp) == NULL) {
13741556Srgrimes				*lpp = q;
13751556Srgrimes				break;
13761556Srgrimes			}
13771556Srgrimes		} else {
13781556Srgrimes			*lpp = q;
13791556Srgrimes			lpp = &q->next;
13801556Srgrimes			if ((q = *lpp) == NULL) {
13811556Srgrimes				*lpp = p;
13821556Srgrimes				break;
13831556Srgrimes			}
13841556Srgrimes		}
13851556Srgrimes	}
13861556Srgrimes	return list;
13871556Srgrimes}
13881556Srgrimes
13891556Srgrimes
13901556Srgrimes
1391221646Sjillesstatic wchar_t
1392221646Sjillesget_wc(const char **p)
1393221646Sjilles{
1394221646Sjilles	wchar_t c;
1395221646Sjilles	int chrlen;
1396221646Sjilles
1397221646Sjilles	chrlen = mbtowc(&c, *p, 4);
1398221646Sjilles	if (chrlen == 0)
1399221646Sjilles		return 0;
1400221646Sjilles	else if (chrlen == -1)
1401221646Sjilles		c = 0;
1402221646Sjilles	else
1403221646Sjilles		*p += chrlen;
1404221646Sjilles	return c;
1405221646Sjilles}
1406221646Sjilles
1407221646Sjilles
14081556Srgrimes/*
1409223120Sjilles * See if a character matches a character class, starting at the first colon
1410223120Sjilles * of "[:class:]".
1411223120Sjilles * If a valid character class is recognized, a pointer to the next character
1412223120Sjilles * after the final closing bracket is stored into *end, otherwise a null
1413223120Sjilles * pointer is stored into *end.
1414223120Sjilles */
1415223120Sjillesstatic int
1416223120Sjillesmatch_charclass(const char *p, wchar_t chr, const char **end)
1417223120Sjilles{
1418223120Sjilles	char name[20];
1419223120Sjilles	const char *nameend;
1420223120Sjilles	wctype_t cclass;
1421223120Sjilles
1422223120Sjilles	*end = NULL;
1423223120Sjilles	p++;
1424223120Sjilles	nameend = strstr(p, ":]");
1425223120Sjilles	if (nameend == NULL || nameend - p >= sizeof(name) || nameend == p)
1426223120Sjilles		return 0;
1427223120Sjilles	memcpy(name, p, nameend - p);
1428223120Sjilles	name[nameend - p] = '\0';
1429223120Sjilles	*end = nameend + 2;
1430223120Sjilles	cclass = wctype(name);
1431223120Sjilles	/* An unknown class matches nothing but is valid nevertheless. */
1432223120Sjilles	if (cclass == 0)
1433223120Sjilles		return 0;
1434223120Sjilles	return iswctype(chr, cclass);
1435223120Sjilles}
1436223120Sjilles
1437223120Sjilles
1438223120Sjilles/*
14391556Srgrimes * Returns true if the pattern matches the string.
14401556Srgrimes */
14411556Srgrimes
1442231790Sjillesstatic int
1443200956Sjillespatmatch(const char *pattern, const char *string, int squoted)
144490111Simp{
1445223120Sjilles	const char *p, *q, *end;
1446233289Sjilles	const char *bt_p, *bt_q;
144725233Ssteve	char c;
1448221646Sjilles	wchar_t wc, wc2;
14491556Srgrimes
14501556Srgrimes	p = pattern;
14511556Srgrimes	q = string;
1452233289Sjilles	bt_p = NULL;
1453233289Sjilles	bt_q = NULL;
14541556Srgrimes	for (;;) {
14551556Srgrimes		switch (c = *p++) {
14561556Srgrimes		case '\0':
1457233289Sjilles			if (*q != '\0')
1458233289Sjilles				goto backtrack;
1459233289Sjilles			return 1;
14601556Srgrimes		case CTLESC:
146145514Stegge			if (squoted && *q == CTLESC)
146245514Stegge				q++;
14631556Srgrimes			if (*q++ != *p++)
1464233289Sjilles				goto backtrack;
14651556Srgrimes			break;
146638887Stegge		case CTLQUOTEMARK:
146738887Stegge			continue;
14681556Srgrimes		case '?':
146945514Stegge			if (squoted && *q == CTLESC)
147045514Stegge				q++;
1471233289Sjilles			if (*q == '\0')
1472233289Sjilles				return 0;
1473233289Sjilles			if (localeisutf8) {
1474221646Sjilles				wc = get_wc(&q);
1475233289Sjilles				/*
1476233289Sjilles				 * A '?' does not match invalid UTF-8 but a
1477233289Sjilles				 * '*' does, so backtrack.
1478233289Sjilles				 */
1479233289Sjilles				if (wc == 0)
1480233289Sjilles					goto backtrack;
1481233289Sjilles			} else
1482223010Sjilles				wc = (unsigned char)*q++;
14831556Srgrimes			break;
14841556Srgrimes		case '*':
14851556Srgrimes			c = *p;
148638887Stegge			while (c == CTLQUOTEMARK || c == '*')
148738887Stegge				c = *++p;
1488233289Sjilles			/*
1489233289Sjilles			 * If the pattern ends here, we know the string
1490233289Sjilles			 * matches without needing to look at the rest of it.
1491233289Sjilles			 */
1492233289Sjilles			if (c == '\0')
1493233289Sjilles				return 1;
1494233289Sjilles			/*
1495233289Sjilles			 * First try the shortest match for the '*' that
1496233289Sjilles			 * could work. We can forget any earlier '*' since
1497233289Sjilles			 * there is no way having it match more characters
1498233289Sjilles			 * can help us, given that we are already here.
1499233289Sjilles			 */
1500233289Sjilles			bt_p = p;
1501233289Sjilles			bt_q = q;
1502233289Sjilles			break;
15031556Srgrimes		case '[': {
1504200956Sjilles			const char *endp;
15051556Srgrimes			int invert, found;
1506221646Sjilles			wchar_t chr;
15071556Srgrimes
15081556Srgrimes			endp = p;
150926488Sache			if (*endp == '!' || *endp == '^')
15101556Srgrimes				endp++;
15111556Srgrimes			for (;;) {
151238887Stegge				while (*endp == CTLQUOTEMARK)
151338887Stegge					endp++;
1514233289Sjilles				if (*endp == 0)
15151556Srgrimes					goto dft;		/* no matching ] */
15161556Srgrimes				if (*endp == CTLESC)
15171556Srgrimes					endp++;
15181556Srgrimes				if (*++endp == ']')
15191556Srgrimes					break;
15201556Srgrimes			}
15211556Srgrimes			invert = 0;
152226488Sache			if (*p == '!' || *p == '^') {
15231556Srgrimes				invert++;
15241556Srgrimes				p++;
15251556Srgrimes			}
15261556Srgrimes			found = 0;
1527221646Sjilles			if (squoted && *q == CTLESC)
1528221646Sjilles				q++;
1529233289Sjilles			if (*q == '\0')
1530233289Sjilles				return 0;
1531233289Sjilles			if (localeisutf8) {
1532221646Sjilles				chr = get_wc(&q);
1533233289Sjilles				if (chr == 0)
1534233289Sjilles					goto backtrack;
1535233289Sjilles			} else
1536223010Sjilles				chr = (unsigned char)*q++;
15371556Srgrimes			c = *p++;
15381556Srgrimes			do {
153938887Stegge				if (c == CTLQUOTEMARK)
154038887Stegge					continue;
1541223120Sjilles				if (c == '[' && *p == ':') {
1542223120Sjilles					found |= match_charclass(p, chr, &end);
1543223120Sjilles					if (end != NULL)
1544223120Sjilles						p = end;
1545223120Sjilles				}
15461556Srgrimes				if (c == CTLESC)
15471556Srgrimes					c = *p++;
1548221646Sjilles				if (localeisutf8 && c & 0x80) {
1549221646Sjilles					p--;
1550221646Sjilles					wc = get_wc(&p);
1551221646Sjilles					if (wc == 0) /* bad utf-8 */
1552221646Sjilles						return 0;
1553221646Sjilles				} else
1554223010Sjilles					wc = (unsigned char)c;
15551556Srgrimes				if (*p == '-' && p[1] != ']') {
15561556Srgrimes					p++;
155738887Stegge					while (*p == CTLQUOTEMARK)
155838887Stegge						p++;
15591556Srgrimes					if (*p == CTLESC)
15601556Srgrimes						p++;
1561221646Sjilles					if (localeisutf8) {
1562221646Sjilles						wc2 = get_wc(&p);
1563221646Sjilles						if (wc2 == 0) /* bad utf-8 */
1564221646Sjilles							return 0;
1565221646Sjilles					} else
1566223010Sjilles						wc2 = (unsigned char)*p++;
1567221646Sjilles					if (   collate_range_cmp(chr, wc) >= 0
1568221646Sjilles					    && collate_range_cmp(chr, wc2) <= 0
156917525Sache					   )
15701556Srgrimes						found = 1;
15711556Srgrimes				} else {
1572221646Sjilles					if (chr == wc)
15731556Srgrimes						found = 1;
15741556Srgrimes				}
15751556Srgrimes			} while ((c = *p++) != ']');
15761556Srgrimes			if (found == invert)
1577233289Sjilles				goto backtrack;
15781556Srgrimes			break;
15791556Srgrimes		}
15801556Srgrimesdft:	        default:
158145514Stegge			if (squoted && *q == CTLESC)
158245514Stegge				q++;
1583233289Sjilles			if (*q == '\0')
15841556Srgrimes				return 0;
1585233289Sjilles			if (*q++ == c)
1586233289Sjilles				break;
1587233289Sjillesbacktrack:
1588233289Sjilles			/*
1589233289Sjilles			 * If we have a mismatch (other than hitting the end
1590233289Sjilles			 * of the string), go back to the last '*' seen and
1591233289Sjilles			 * have it match one additional character.
1592233289Sjilles			 */
1593233289Sjilles			if (bt_p == NULL)
1594233289Sjilles				return 0;
1595233289Sjilles			if (squoted && *bt_q == CTLESC)
1596233289Sjilles				bt_q++;
1597233289Sjilles			if (*bt_q == '\0')
1598233289Sjilles				return 0;
1599233289Sjilles			bt_q++;
1600233289Sjilles			p = bt_p;
1601233289Sjilles			q = bt_q;
16021556Srgrimes			break;
16031556Srgrimes		}
16041556Srgrimes	}
16051556Srgrimes}
16061556Srgrimes
16071556Srgrimes
16081556Srgrimes
16091556Srgrimes/*
1610212243Sjilles * Remove any CTLESC and CTLQUOTEMARK characters from a string.
16111556Srgrimes */
16121556Srgrimes
16131556Srgrimesvoid
161490111Simprmescapes(char *str)
161538887Stegge{
161625233Ssteve	char *p, *q;
16171556Srgrimes
16181556Srgrimes	p = str;
1619214512Sjilles	while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) {
16201556Srgrimes		if (*p++ == '\0')
16211556Srgrimes			return;
16221556Srgrimes	}
16231556Srgrimes	q = p;
16241556Srgrimes	while (*p) {
1625214512Sjilles		if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) {
162638887Stegge			p++;
162738887Stegge			continue;
162838887Stegge		}
16291556Srgrimes		if (*p == CTLESC)
16301556Srgrimes			p++;
16311556Srgrimes		*q++ = *p++;
16321556Srgrimes	}
16331556Srgrimes	*q = '\0';
16341556Srgrimes}
16351556Srgrimes
16361556Srgrimes
16371556Srgrimes
16381556Srgrimes/*
16391556Srgrimes * See if a pattern matches in a case statement.
16401556Srgrimes */
16411556Srgrimes
16421556Srgrimesint
1643200956Sjillescasematch(union node *pattern, const char *val)
164490111Simp{
16451556Srgrimes	struct stackmark smark;
16461556Srgrimes	int result;
16471556Srgrimes	char *p;
16481556Srgrimes
16491556Srgrimes	setstackmark(&smark);
16501556Srgrimes	argbackq = pattern->narg.backquote;
16511556Srgrimes	STARTSTACKSTR(expdest);
16521556Srgrimes	ifslastp = NULL;
16531556Srgrimes	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
16541556Srgrimes	STPUTC('\0', expdest);
16551556Srgrimes	p = grabstackstr(expdest);
165645514Stegge	result = patmatch(p, val, 0);
16571556Srgrimes	popstackmark(&smark);
16581556Srgrimes	return result;
16591556Srgrimes}
166017987Speter
166117987Speter/*
166217987Speter * Our own itoa().
166317987Speter */
166417987Speter
1665213811Sobrienstatic char *
166690111Simpcvtnum(int num, char *buf)
166790111Simp{
166817987Speter	char temp[32];
166917987Speter	int neg = num < 0;
167017987Speter	char *p = temp + 31;
167117987Speter
167217987Speter	temp[31] = '\0';
167317987Speter
167417987Speter	do {
167517987Speter		*--p = num % 10 + '0';
167617987Speter	} while ((num /= 10) != 0);
167717987Speter
167817987Speter	if (neg)
167917987Speter		*--p = '-';
168017987Speter
1681215783Sjilles	STPUTS(p, buf);
168217987Speter	return buf;
168317987Speter}
1684108286Stjr
1685108286Stjr/*
1686108286Stjr * Do most of the work for wordexp(3).
1687108286Stjr */
1688108286Stjr
1689108286Stjrint
1690108286Stjrwordexpcmd(int argc, char **argv)
1691108286Stjr{
1692108286Stjr	size_t len;
1693108286Stjr	int i;
1694108286Stjr
1695108286Stjr	out1fmt("%08x", argc - 1);
1696108286Stjr	for (i = 1, len = 0; i < argc; i++)
1697108286Stjr		len += strlen(argv[i]);
1698108286Stjr	out1fmt("%08x", (int)len);
1699215567Sjilles	for (i = 1; i < argc; i++)
1700215567Sjilles		outbin(argv[i], strlen(argv[i]) + 1, out1);
1701108286Stjr        return (0);
1702108286Stjr}
1703