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
101264166Sjillesstatic char *argstr(char *, int);
102213811Sobrienstatic char *exptilde(char *, int);
103262951Sjmmvstatic char *expari(char *);
104213811Sobrienstatic void expbackq(union node *, int, int);
105214524Sjillesstatic int subevalvar(char *, char *, int, int, int, int, int);
106213811Sobrienstatic char *evalvar(char *, int);
107264168Sjillesstatic int varisset(const char *, int);
108264168Sjillesstatic void varvalue(const char *, int, int, int);
109213811Sobrienstatic void recordregion(int, int, int);
110213811Sobrienstatic void removerecordregions(int);
111213811Sobrienstatic void ifsbreakup(char *, struct arglist *);
112213811Sobrienstatic void expandmeta(struct strlist *, int);
113213811Sobrienstatic void expmeta(char *, char *);
114213811Sobrienstatic void addfname(char *);
115213811Sobrienstatic struct strlist *expsort(struct strlist *);
116213811Sobrienstatic struct strlist *msort(struct strlist *, int);
117229220Sjillesstatic int patmatch(const char *, const char *, int);
118213811Sobrienstatic char *cvtnum(int, char *);
119221646Sjillesstatic int collate_range_cmp(wchar_t, wchar_t);
1201556Srgrimes
121213811Sobrienstatic int
122221646Sjillescollate_range_cmp(wchar_t c1, wchar_t c2)
12319281Sache{
124221646Sjilles	static wchar_t s1[2], s2[2];
12519281Sache
12619281Sache	s1[0] = c1;
12719281Sache	s2[0] = c2;
128221646Sjilles	return (wcscoll(s1, s2));
12919281Sache}
13019281Sache
131216384Sjillesstatic char *
132216384Sjillesstputs_quotes(const char *data, const char *syntax, char *p)
133216384Sjilles{
134216384Sjilles	while (*data) {
135216384Sjilles		CHECKSTRSPACE(2, p);
136216384Sjilles		if (syntax[(int)*data] == CCTL)
137216384Sjilles			USTPUTC(CTLESC, p);
138216384Sjilles		USTPUTC(*data++, p);
139216384Sjilles	}
140216384Sjilles	return (p);
141216384Sjilles}
142216384Sjilles#define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p)
1431556Srgrimes
1441556Srgrimes/*
145212243Sjilles * Perform expansions on an argument, placing the resulting list of arguments
146212243Sjilles * in arglist.  Parameter expansion, command substitution and arithmetic
147212243Sjilles * expansion are always performed; additional expansions can be requested
148212243Sjilles * via flag (EXP_*).
149212243Sjilles * The result is left in the stack string.
150218203Sjilles * When arglist is NULL, perform here document expansion.
151212243Sjilles *
152212243Sjilles * Caution: this function uses global state and is not reentrant.
153212243Sjilles * However, a new invocation after an interrupted invocation is safe
154212243Sjilles * and will reset the global state for the new call.
1551556Srgrimes */
1561556Srgrimesvoid
15790111Simpexpandarg(union node *arg, struct arglist *arglist, int flag)
15817987Speter{
1591556Srgrimes	struct strlist *sp;
1601556Srgrimes	char *p;
1611556Srgrimes
1621556Srgrimes	argbackq = arg->narg.backquote;
1631556Srgrimes	STARTSTACKSTR(expdest);
1641556Srgrimes	ifsfirst.next = NULL;
1651556Srgrimes	ifslastp = NULL;
1661556Srgrimes	argstr(arg->narg.text, flag);
1671556Srgrimes	if (arglist == NULL) {
168222907Sjilles		STACKSTRNUL(expdest);
1691556Srgrimes		return;			/* here document expanded */
1701556Srgrimes	}
1711556Srgrimes	STPUTC('\0', expdest);
1721556Srgrimes	p = grabstackstr(expdest);
1731556Srgrimes	exparg.lastp = &exparg.list;
1741556Srgrimes	/*
1751556Srgrimes	 * TODO - EXP_REDIR
1761556Srgrimes	 */
1771556Srgrimes	if (flag & EXP_FULL) {
1781556Srgrimes		ifsbreakup(p, &exparg);
1791556Srgrimes		*exparg.lastp = NULL;
1801556Srgrimes		exparg.lastp = &exparg.list;
1811556Srgrimes		expandmeta(exparg.list, flag);
1821556Srgrimes	} else {
1831556Srgrimes		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
1841556Srgrimes			rmescapes(p);
1851556Srgrimes		sp = (struct strlist *)stalloc(sizeof (struct strlist));
1861556Srgrimes		sp->text = p;
1871556Srgrimes		*exparg.lastp = sp;
1881556Srgrimes		exparg.lastp = &sp->next;
1891556Srgrimes	}
1901556Srgrimes	while (ifsfirst.next != NULL) {
1911556Srgrimes		struct ifsregion *ifsp;
1921556Srgrimes		INTOFF;
1931556Srgrimes		ifsp = ifsfirst.next->next;
1941556Srgrimes		ckfree(ifsfirst.next);
1951556Srgrimes		ifsfirst.next = ifsp;
1961556Srgrimes		INTON;
1971556Srgrimes	}
1981556Srgrimes	*exparg.lastp = NULL;
1991556Srgrimes	if (exparg.list) {
2001556Srgrimes		*arglist->lastp = exparg.list;
2011556Srgrimes		arglist->lastp = exparg.lastp;
2021556Srgrimes	}
2031556Srgrimes}
2041556Srgrimes
2051556Srgrimes
2061556Srgrimes
2071556Srgrimes/*
208212243Sjilles * Perform parameter expansion, command substitution and arithmetic
209212243Sjilles * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE.
210262951Sjmmv * Processing ends at a CTLENDVAR or CTLENDARI character as well as '\0'.
211212243Sjilles * This is used to expand word in ${var+word} etc.
212212243Sjilles * If EXP_FULL, EXP_CASE or EXP_REDIR are set, keep and/or generate CTLESC
213212243Sjilles * characters to allow for further processing.
214212243Sjilles * If EXP_FULL is set, also preserve CTLQUOTEMARK characters.
2151556Srgrimes */
216264166Sjillesstatic char *
21790111Simpargstr(char *p, int flag)
21817987Speter{
21925233Ssteve	char c;
220104672Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);	/* do CTLESC */
2211556Srgrimes	int firsteq = 1;
222214512Sjilles	int split_lit;
223214512Sjilles	int lit_quoted;
2241556Srgrimes
225214512Sjilles	split_lit = flag & EXP_SPLIT_LIT;
226214512Sjilles	lit_quoted = flag & EXP_LIT_QUOTED;
227214512Sjilles	flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED);
2281556Srgrimes	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
2291556Srgrimes		p = exptilde(p, flag);
2301556Srgrimes	for (;;) {
231215783Sjilles		CHECKSTRSPACE(2, expdest);
2321556Srgrimes		switch (c = *p++) {
2331556Srgrimes		case '\0':
234264166Sjilles			return (p - 1);
235212243Sjilles		case CTLENDVAR:
236262951Sjmmv		case CTLENDARI:
237264166Sjilles			return (p);
23838887Stegge		case CTLQUOTEMARK:
239214512Sjilles			lit_quoted = 1;
24038887Stegge			/* "$@" syntax adherence hack */
24138887Stegge			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
24238887Stegge				break;
24339137Stegge			if ((flag & EXP_FULL) != 0)
244215783Sjilles				USTPUTC(c, expdest);
24538887Stegge			break;
246214512Sjilles		case CTLQUOTEEND:
247214512Sjilles			lit_quoted = 0;
248214512Sjilles			break;
2491556Srgrimes		case CTLESC:
2501556Srgrimes			if (quotes)
251215783Sjilles				USTPUTC(c, expdest);
2521556Srgrimes			c = *p++;
253215783Sjilles			USTPUTC(c, expdest);
254214512Sjilles			if (split_lit && !lit_quoted)
255214512Sjilles				recordregion(expdest - stackblock() -
256214512Sjilles				    (quotes ? 2 : 1),
257214512Sjilles				    expdest - stackblock(), 0);
2581556Srgrimes			break;
2591556Srgrimes		case CTLVAR:
2601556Srgrimes			p = evalvar(p, flag);
2611556Srgrimes			break;
2621556Srgrimes		case CTLBACKQ:
2631556Srgrimes		case CTLBACKQ|CTLQUOTE:
2641556Srgrimes			expbackq(argbackq->n, c & CTLQUOTE, flag);
2651556Srgrimes			argbackq = argbackq->next;
2661556Srgrimes			break;
267262951Sjmmv		case CTLARI:
268262951Sjmmv			p = expari(p);
2691556Srgrimes			break;
2701556Srgrimes		case ':':
2711556Srgrimes		case '=':
2721556Srgrimes			/*
2731556Srgrimes			 * sort of a hack - expand tildes in variable
2741556Srgrimes			 * assignments (after the first '=' and after ':'s).
2751556Srgrimes			 */
276215783Sjilles			USTPUTC(c, expdest);
277214512Sjilles			if (split_lit && !lit_quoted)
278214512Sjilles				recordregion(expdest - stackblock() - 1,
279214512Sjilles				    expdest - stackblock(), 0);
280214512Sjilles			if (flag & EXP_VARTILDE && *p == '~' &&
281214512Sjilles			    (c != '=' || firsteq)) {
282214512Sjilles				if (c == '=')
283214512Sjilles					firsteq = 0;
2841556Srgrimes				p = exptilde(p, flag);
2851556Srgrimes			}
2861556Srgrimes			break;
2871556Srgrimes		default:
288215783Sjilles			USTPUTC(c, expdest);
289214512Sjilles			if (split_lit && !lit_quoted)
290214512Sjilles				recordregion(expdest - stackblock() - 1,
291214512Sjilles				    expdest - stackblock(), 0);
2921556Srgrimes		}
2931556Srgrimes	}
2941556Srgrimes}
2951556Srgrimes
296212243Sjilles/*
297212243Sjilles * Perform tilde expansion, placing the result in the stack string and
298212243Sjilles * returning the next position in the input string to process.
299212243Sjilles */
300213811Sobrienstatic char *
30190111Simpexptilde(char *p, int flag)
30217987Speter{
3031556Srgrimes	char c, *startp = p;
3041556Srgrimes	struct passwd *pw;
3051556Srgrimes	char *home;
306108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
3071556Srgrimes
30817987Speter	while ((c = *p) != '\0') {
3091556Srgrimes		switch(c) {
310200988Sjilles		case CTLESC: /* This means CTL* are always considered quoted. */
311200988Sjilles		case CTLVAR:
312200988Sjilles		case CTLBACKQ:
313200988Sjilles		case CTLBACKQ | CTLQUOTE:
314200988Sjilles		case CTLARI:
315200988Sjilles		case CTLENDARI:
31639137Stegge		case CTLQUOTEMARK:
31739137Stegge			return (startp);
3181556Srgrimes		case ':':
3191556Srgrimes			if (flag & EXP_VARTILDE)
3201556Srgrimes				goto done;
3211556Srgrimes			break;
3221556Srgrimes		case '/':
323206150Sjilles		case CTLENDVAR:
3241556Srgrimes			goto done;
3251556Srgrimes		}
3261556Srgrimes		p++;
3271556Srgrimes	}
3281556Srgrimesdone:
3291556Srgrimes	*p = '\0';
3301556Srgrimes	if (*(startp+1) == '\0') {
3311556Srgrimes		if ((home = lookupvar("HOME")) == NULL)
3321556Srgrimes			goto lose;
3331556Srgrimes	} else {
3341556Srgrimes		if ((pw = getpwnam(startp+1)) == NULL)
3351556Srgrimes			goto lose;
3361556Srgrimes		home = pw->pw_dir;
3371556Srgrimes	}
3381556Srgrimes	if (*home == '\0')
3391556Srgrimes		goto lose;
3401556Srgrimes	*p = c;
341216384Sjilles	if (quotes)
342216384Sjilles		STPUTS_QUOTES(home, SQSYNTAX, expdest);
343216384Sjilles	else
344216384Sjilles		STPUTS(home, expdest);
3451556Srgrimes	return (p);
3461556Srgrimeslose:
3471556Srgrimes	*p = c;
3481556Srgrimes	return (startp);
3491556Srgrimes}
3501556Srgrimes
3511556Srgrimes
352213811Sobrienstatic void
35390111Simpremoverecordregions(int endoff)
35438887Stegge{
35538887Stegge	if (ifslastp == NULL)
35638887Stegge		return;
35738887Stegge
35838887Stegge	if (ifsfirst.endoff > endoff) {
35938887Stegge		while (ifsfirst.next != NULL) {
36038887Stegge			struct ifsregion *ifsp;
36138887Stegge			INTOFF;
36238887Stegge			ifsp = ifsfirst.next->next;
36338887Stegge			ckfree(ifsfirst.next);
36438887Stegge			ifsfirst.next = ifsp;
36538887Stegge			INTON;
36638887Stegge		}
36738887Stegge		if (ifsfirst.begoff > endoff)
36838887Stegge			ifslastp = NULL;
36938887Stegge		else {
37038887Stegge			ifslastp = &ifsfirst;
37138887Stegge			ifsfirst.endoff = endoff;
37238887Stegge		}
37338887Stegge		return;
37438887Stegge	}
375155301Sschweikh
37638887Stegge	ifslastp = &ifsfirst;
37738887Stegge	while (ifslastp->next && ifslastp->next->begoff < endoff)
37838887Stegge		ifslastp=ifslastp->next;
37938887Stegge	while (ifslastp->next != NULL) {
38038887Stegge		struct ifsregion *ifsp;
38138887Stegge		INTOFF;
38238887Stegge		ifsp = ifslastp->next->next;
38338887Stegge		ckfree(ifslastp->next);
38438887Stegge		ifslastp->next = ifsp;
38538887Stegge		INTON;
38638887Stegge	}
38738887Stegge	if (ifslastp->endoff > endoff)
38838887Stegge		ifslastp->endoff = endoff;
38938887Stegge}
39038887Stegge
3911556Srgrimes/*
392262951Sjmmv * Expand arithmetic expression.
393262951Sjmmv * Note that flag is not required as digits never require CTLESC characters.
3941556Srgrimes */
395262951Sjmmvstatic char *
396262951Sjmmvexpari(char *p)
39717987Speter{
398262951Sjmmv	char *q, *start;
399178631Sstefanf	arith_t result;
40038887Stegge	int begoff;
40138887Stegge	int quoted;
402262951Sjmmv	int adj;
4031556Srgrimes
404262951Sjmmv	quoted = *p++ == '"';
405262951Sjmmv	begoff = expdest - stackblock();
406264166Sjilles	p = argstr(p, 0);
407262951Sjmmv	removerecordregions(begoff);
408262951Sjmmv	STPUTC('\0', expdest);
409262951Sjmmv	start = stackblock() + begoff;
41038887Stegge
411207206Sjilles	q = grabstackstr(expdest);
412262951Sjmmv	result = arith(start);
413207206Sjilles	ungrabstackstr(q, expdest);
414262951Sjmmv
415262951Sjmmv	start = stackblock() + begoff;
416262951Sjmmv	adj = start - expdest;
417262951Sjmmv	STADJUST(adj, expdest);
418262951Sjmmv
419262951Sjmmv	CHECKSTRSPACE((int)(DIGITS(result) + 1), expdest);
420262951Sjmmv	fmtstr(expdest, DIGITS(result), ARITH_FORMAT_STR, result);
421262951Sjmmv	adj = strlen(expdest);
422262951Sjmmv	STADJUST(adj, expdest);
423262951Sjmmv	if (!quoted)
424262951Sjmmv		recordregion(begoff, expdest - stackblock(), 0);
425262951Sjmmv	return p;
4261556Srgrimes}
4271556Srgrimes
4281556Srgrimes
4291556Srgrimes/*
430212243Sjilles * Perform command substitution.
4311556Srgrimes */
432213811Sobrienstatic void
43390111Simpexpbackq(union node *cmd, int quoted, int flag)
43417987Speter{
4351556Srgrimes	struct backcmd in;
4361556Srgrimes	int i;
4371556Srgrimes	char buf[128];
4381556Srgrimes	char *p;
4391556Srgrimes	char *dest = expdest;
4401556Srgrimes	struct ifsregion saveifs, *savelastp;
4411556Srgrimes	struct nodelist *saveargbackq;
4421556Srgrimes	char lastc;
4431556Srgrimes	int startloc = dest - stackblock();
4441556Srgrimes	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
445108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
446248980Sjilles	size_t nnl;
4471556Srgrimes
4481556Srgrimes	INTOFF;
4491556Srgrimes	saveifs = ifsfirst;
4501556Srgrimes	savelastp = ifslastp;
4511556Srgrimes	saveargbackq = argbackq;
4521556Srgrimes	p = grabstackstr(dest);
4531556Srgrimes	evalbackcmd(cmd, &in);
4541556Srgrimes	ungrabstackstr(p, dest);
4551556Srgrimes	ifsfirst = saveifs;
4561556Srgrimes	ifslastp = savelastp;
4571556Srgrimes	argbackq = saveargbackq;
4581556Srgrimes
4591556Srgrimes	p = in.buf;
4601556Srgrimes	lastc = '\0';
461115424Sfenner	nnl = 0;
462115424Sfenner	/* Don't copy trailing newlines */
4631556Srgrimes	for (;;) {
4641556Srgrimes		if (--in.nleft < 0) {
4651556Srgrimes			if (in.fd < 0)
4661556Srgrimes				break;
4671556Srgrimes			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
4681556Srgrimes			TRACE(("expbackq: read returns %d\n", i));
4691556Srgrimes			if (i <= 0)
4701556Srgrimes				break;
4711556Srgrimes			p = buf;
4721556Srgrimes			in.nleft = i - 1;
4731556Srgrimes		}
4741556Srgrimes		lastc = *p++;
4751556Srgrimes		if (lastc != '\0') {
476115424Sfenner			if (lastc == '\n') {
477115424Sfenner				nnl++;
478115424Sfenner			} else {
479216706Sjilles				CHECKSTRSPACE(nnl + 2, dest);
480115424Sfenner				while (nnl > 0) {
481115424Sfenner					nnl--;
482216706Sjilles					USTPUTC('\n', dest);
483115424Sfenner				}
484216496Sjilles				if (quotes && syntax[(int)lastc] == CCTL)
485216706Sjilles					USTPUTC(CTLESC, dest);
486216706Sjilles				USTPUTC(lastc, dest);
487115424Sfenner			}
4881556Srgrimes		}
4891556Srgrimes	}
49017987Speter
4911556Srgrimes	if (in.fd >= 0)
4921556Srgrimes		close(in.fd);
4931556Srgrimes	if (in.buf)
4941556Srgrimes		ckfree(in.buf);
4951556Srgrimes	if (in.jp)
49645916Scracauer		exitstatus = waitforjob(in.jp, (int *)NULL);
4971556Srgrimes	if (quoted == 0)
4981556Srgrimes		recordregion(startloc, dest - stackblock(), 0);
499213775Sjhb	TRACE(("expbackq: size=%td: \"%.*s\"\n",
500213775Sjhb		((dest - stackblock()) - startloc),
501213775Sjhb		(int)((dest - stackblock()) - startloc),
5021556Srgrimes		stackblock() + startloc));
5031556Srgrimes	expdest = dest;
5041556Srgrimes	INTON;
5051556Srgrimes}
5061556Srgrimes
5071556Srgrimes
5081556Srgrimes
509213811Sobrienstatic int
51090111Simpsubevalvar(char *p, char *str, int strloc, int subtype, int startloc,
511214524Sjilles  int varflags, int quotes)
51217987Speter{
51317987Speter	char *startp;
51417987Speter	char *loc = NULL;
51545514Stegge	char *q;
51617987Speter	int c = 0;
51717987Speter	struct nodelist *saveargbackq = argbackq;
51820425Ssteve	int amount;
51920425Ssteve
520206150Sjilles	argstr(p, (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX ||
521206147Sjilles	    subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX ?
522206150Sjilles	    EXP_CASE : 0) | EXP_TILDE);
52317987Speter	STACKSTRNUL(expdest);
52417987Speter	argbackq = saveargbackq;
52517987Speter	startp = stackblock() + startloc;
52620425Ssteve	if (str == NULL)
52720425Ssteve	    str = stackblock() + strloc;
52817987Speter
52917987Speter	switch (subtype) {
53017987Speter	case VSASSIGN:
53117987Speter		setvar(str, startp, 0);
53220425Ssteve		amount = startp - expdest;
53320425Ssteve		STADJUST(amount, expdest);
53417987Speter		varflags &= ~VSNUL;
53517987Speter		return 1;
53617987Speter
53717987Speter	case VSQUESTION:
53817987Speter		if (*p != CTLENDVAR) {
539201366Sjilles			outfmt(out2, "%s\n", startp);
54017987Speter			error((char *)NULL);
54117987Speter		}
542112254Sru		error("%.*s: parameter %snot set", (int)(p - str - 1),
54317987Speter		      str, (varflags & VSNUL) ? "null or "
54417987Speter					      : nullstr);
54517987Speter		return 0;
54617987Speter
54717987Speter	case VSTRIMLEFT:
54825233Ssteve		for (loc = startp; loc < str; loc++) {
54917987Speter			c = *loc;
55017987Speter			*loc = '\0';
551214524Sjilles			if (patmatch(str, startp, quotes)) {
55217987Speter				*loc = c;
55317987Speter				goto recordleft;
55417987Speter			}
55517987Speter			*loc = c;
556214524Sjilles			if (quotes && *loc == CTLESC)
55745514Stegge				loc++;
55817987Speter		}
55917987Speter		return 0;
56017987Speter
56117987Speter	case VSTRIMLEFTMAX:
56245514Stegge		for (loc = str - 1; loc >= startp;) {
56317987Speter			c = *loc;
56417987Speter			*loc = '\0';
565214524Sjilles			if (patmatch(str, startp, quotes)) {
56617987Speter				*loc = c;
56717987Speter				goto recordleft;
56817987Speter			}
56917987Speter			*loc = c;
57045514Stegge			loc--;
571214524Sjilles			if (quotes && loc > startp && *(loc - 1) == CTLESC) {
57245514Stegge				for (q = startp; q < loc; q++)
57345514Stegge					if (*q == CTLESC)
57445514Stegge						q++;
57545514Stegge				if (q > loc)
57645514Stegge					loc--;
57745514Stegge			}
57817987Speter		}
57917987Speter		return 0;
58017987Speter
58117987Speter	case VSTRIMRIGHT:
58245514Stegge		for (loc = str - 1; loc >= startp;) {
583214524Sjilles			if (patmatch(str, loc, quotes)) {
58420425Ssteve				amount = loc - expdest;
58520425Ssteve				STADJUST(amount, expdest);
58617987Speter				return 1;
58717987Speter			}
58845514Stegge			loc--;
589214524Sjilles			if (quotes && loc > startp && *(loc - 1) == CTLESC) {
59045514Stegge				for (q = startp; q < loc; q++)
59145514Stegge					if (*q == CTLESC)
59245514Stegge						q++;
59345514Stegge				if (q > loc)
59445514Stegge					loc--;
59545514Stegge			}
59617987Speter		}
59717987Speter		return 0;
59817987Speter
59917987Speter	case VSTRIMRIGHTMAX:
60017987Speter		for (loc = startp; loc < str - 1; loc++) {
601214524Sjilles			if (patmatch(str, loc, quotes)) {
60220425Ssteve				amount = loc - expdest;
60320425Ssteve				STADJUST(amount, expdest);
60417987Speter				return 1;
60517987Speter			}
606214524Sjilles			if (quotes && *loc == CTLESC)
60745514Stegge				loc++;
60817987Speter		}
60917987Speter		return 0;
61017987Speter
61117987Speter
61217987Speter	default:
61317987Speter		abort();
61417987Speter	}
61517987Speter
61617987Speterrecordleft:
61720425Ssteve	amount = ((str - 1) - (loc - startp)) - expdest;
61820425Ssteve	STADJUST(amount, expdest);
61917987Speter	while (loc != str - 1)
62017987Speter		*startp++ = *loc++;
62117987Speter	return 1;
62217987Speter}
62317987Speter
62417987Speter
6251556Srgrimes/*
6261556Srgrimes * Expand a variable, and return a pointer to the next character in the
6271556Srgrimes * input string.
6281556Srgrimes */
6291556Srgrimes
630213811Sobrienstatic char *
63190111Simpevalvar(char *p, int flag)
63217987Speter{
6331556Srgrimes	int subtype;
6341556Srgrimes	int varflags;
6351556Srgrimes	char *var;
636264168Sjilles	const char *val;
63745644Stegge	int patloc;
6381556Srgrimes	int c;
6391556Srgrimes	int set;
6401556Srgrimes	int special;
6411556Srgrimes	int startloc;
64217987Speter	int varlen;
643221602Sjilles	int varlenb;
64417987Speter	int easy;
645108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
6461556Srgrimes
647164081Sstefanf	varflags = (unsigned char)*p++;
6481556Srgrimes	subtype = varflags & VSTYPE;
6491556Srgrimes	var = p;
6501556Srgrimes	special = 0;
6511556Srgrimes	if (! is_name(*p))
6521556Srgrimes		special = 1;
6531556Srgrimes	p = strchr(p, '=') + 1;
6541556Srgrimesagain: /* jump here after setting a variable with ${var=text} */
655179022Sstefanf	if (varflags & VSLINENO) {
656179022Sstefanf		set = 1;
657262951Sjmmv		special = 1;
658262951Sjmmv		val = NULL;
659179022Sstefanf	} else if (special) {
66025233Ssteve		set = varisset(var, varflags & VSNUL);
6611556Srgrimes		val = NULL;
6621556Srgrimes	} else {
66360592Scracauer		val = bltinlookup(var, 1);
66417987Speter		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
6651556Srgrimes			val = NULL;
6661556Srgrimes			set = 0;
6671556Srgrimes		} else
6681556Srgrimes			set = 1;
6691556Srgrimes	}
67017987Speter	varlen = 0;
6711556Srgrimes	startloc = expdest - stackblock();
672198454Sjilles	if (!set && uflag && *var != '@' && *var != '*') {
67396939Stjr		switch (subtype) {
67496939Stjr		case VSNORMAL:
67596939Stjr		case VSTRIMLEFT:
67696939Stjr		case VSTRIMLEFTMAX:
67796939Stjr		case VSTRIMRIGHT:
67896939Stjr		case VSTRIMRIGHTMAX:
67996939Stjr		case VSLENGTH:
680112254Sru			error("%.*s: parameter not set", (int)(p - var - 1),
681112254Sru			    var);
68296939Stjr		}
68396939Stjr	}
6841556Srgrimes	if (set && subtype != VSPLUS) {
6851556Srgrimes		/* insert the value of the variable */
6861556Srgrimes		if (special) {
687262951Sjmmv			if (varflags & VSLINENO)
688262951Sjmmv				STPUTBIN(var, p - var - 1, expdest);
689262951Sjmmv			else
690262951Sjmmv				varvalue(var, varflags & VSQUOTE, subtype, flag);
69117987Speter			if (subtype == VSLENGTH) {
692221602Sjilles				varlenb = expdest - stackblock() - startloc;
693221602Sjilles				varlen = varlenb;
694221602Sjilles				if (localeisutf8) {
695221602Sjilles					val = stackblock() + startloc;
696221602Sjilles					for (;val != expdest; val++)
697221602Sjilles						if ((*val & 0xC0) == 0x80)
698221602Sjilles							varlen--;
699221602Sjilles				}
700221602Sjilles				STADJUST(-varlenb, expdest);
70117987Speter			}
7021556Srgrimes		} else {
70320425Ssteve			char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
70417987Speter								  : BASESYNTAX;
7051556Srgrimes
70617987Speter			if (subtype == VSLENGTH) {
70717987Speter				for (;*val; val++)
708221602Sjilles					if (!localeisutf8 ||
709221602Sjilles					    (*val & 0xC0) != 0x80)
710221602Sjilles						varlen++;
7111556Srgrimes			}
71217987Speter			else {
713216384Sjilles				if (quotes)
714216384Sjilles					STPUTS_QUOTES(val, syntax, expdest);
715216384Sjilles				else
716216384Sjilles					STPUTS(val, expdest);
71717987Speter
71817987Speter			}
7191556Srgrimes		}
7201556Srgrimes	}
72120425Ssteve
7221556Srgrimes	if (subtype == VSPLUS)
7231556Srgrimes		set = ! set;
72417987Speter
72520425Ssteve	easy = ((varflags & VSQUOTE) == 0 ||
72617987Speter		(*var == '@' && shellparam.nparam != 1));
72717987Speter
72817987Speter
72917987Speter	switch (subtype) {
73017987Speter	case VSLENGTH:
73117987Speter		expdest = cvtnum(varlen, expdest);
73217987Speter		goto record;
73317987Speter
73417987Speter	case VSNORMAL:
73517987Speter		if (!easy)
73617987Speter			break;
73717987Speterrecord:
73820425Ssteve		recordregion(startloc, expdest - stackblock(),
739222361Sjilles		    varflags & VSQUOTE || (ifsset() && ifsval()[0] == '\0' &&
740222361Sjilles		    (*var == '@' || *var == '*')));
74117987Speter		break;
74217987Speter
74317987Speter	case VSPLUS:
74417987Speter	case VSMINUS:
74517987Speter		if (!set) {
746214512Sjilles			argstr(p, flag | (flag & EXP_FULL ? EXP_SPLIT_LIT : 0) |
747214512Sjilles			    (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0));
74817987Speter			break;
74917987Speter		}
75017987Speter		if (easy)
75117987Speter			goto record;
75217987Speter		break;
75317987Speter
75417987Speter	case VSTRIMLEFT:
75517987Speter	case VSTRIMLEFTMAX:
75617987Speter	case VSTRIMRIGHT:
75717987Speter	case VSTRIMRIGHTMAX:
75817987Speter		if (!set)
75917987Speter			break;
76017987Speter		/*
76117987Speter		 * Terminate the string and start recording the pattern
76217987Speter		 * right after it
76317987Speter		 */
76417987Speter		STPUTC('\0', expdest);
76545644Stegge		patloc = expdest - stackblock();
76645644Stegge		if (subevalvar(p, NULL, patloc, subtype,
767214524Sjilles		    startloc, varflags, quotes) == 0) {
76845644Stegge			int amount = (expdest - stackblock() - patloc) + 1;
76925233Ssteve			STADJUST(-amount, expdest);
77025233Ssteve		}
77138887Stegge		/* Remove any recorded regions beyond start of variable */
77238887Stegge		removerecordregions(startloc);
77338887Stegge		goto record;
77417987Speter
77517987Speter	case VSASSIGN:
77617987Speter	case VSQUESTION:
77717987Speter		if (!set) {
778214524Sjilles			if (subevalvar(p, var, 0, subtype, startloc, varflags,
779214524Sjilles			    quotes)) {
78020425Ssteve				varflags &= ~VSNUL;
781155301Sschweikh				/*
782155301Sschweikh				 * Remove any recorded regions beyond
783155301Sschweikh				 * start of variable
78438887Stegge				 */
78538887Stegge				removerecordregions(startloc);
7861556Srgrimes				goto again;
78720425Ssteve			}
78817987Speter			break;
7891556Srgrimes		}
79017987Speter		if (easy)
79117987Speter			goto record;
79217987Speter		break;
79317987Speter
794164003Sstefanf	case VSERROR:
795164003Sstefanf		c = p - var - 1;
796164003Sstefanf		error("${%.*s%s}: Bad substitution", c, var,
797164003Sstefanf		    (c > 0 && *p != CTLENDVAR) ? "..." : "");
798164003Sstefanf
79917987Speter	default:
80017987Speter		abort();
8011556Srgrimes	}
80217987Speter
8031556Srgrimes	if (subtype != VSNORMAL) {	/* skip to end of alternative */
8041556Srgrimes		int nesting = 1;
8051556Srgrimes		for (;;) {
8061556Srgrimes			if ((c = *p++) == CTLESC)
8071556Srgrimes				p++;
8081556Srgrimes			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
8091556Srgrimes				if (set)
8101556Srgrimes					argbackq = argbackq->next;
8111556Srgrimes			} else if (c == CTLVAR) {
8121556Srgrimes				if ((*p++ & VSTYPE) != VSNORMAL)
8131556Srgrimes					nesting++;
8141556Srgrimes			} else if (c == CTLENDVAR) {
8151556Srgrimes				if (--nesting == 0)
8161556Srgrimes					break;
8171556Srgrimes			}
8181556Srgrimes		}
8191556Srgrimes	}
8201556Srgrimes	return p;
8211556Srgrimes}
8221556Srgrimes
8231556Srgrimes
8241556Srgrimes
8251556Srgrimes/*
8261556Srgrimes * Test whether a specialized variable is set.
8271556Srgrimes */
8281556Srgrimes
829213811Sobrienstatic int
830264168Sjillesvarisset(const char *name, int nulok)
83125233Ssteve{
8321556Srgrimes
83325233Ssteve	if (*name == '!')
834209600Sjilles		return backgndpidset();
83525233Ssteve	else if (*name == '@' || *name == '*') {
8361556Srgrimes		if (*shellparam.p == NULL)
8371556Srgrimes			return 0;
83825233Ssteve
83925233Ssteve		if (nulok) {
84025233Ssteve			char **av;
84125233Ssteve
84225233Ssteve			for (av = shellparam.p; *av; av++)
84325233Ssteve				if (**av != '\0')
84425233Ssteve					return 1;
84525233Ssteve			return 0;
84625233Ssteve		}
84718202Speter	} else if (is_digit(*name)) {
84825233Ssteve		char *ap;
84920425Ssteve		int num = atoi(name);
85025233Ssteve
85125233Ssteve		if (num > shellparam.nparam)
85225233Ssteve			return 0;
85325233Ssteve
85425233Ssteve		if (num == 0)
85525233Ssteve			ap = arg0;
85625233Ssteve		else
85725233Ssteve			ap = shellparam.p[num - 1];
85825233Ssteve
85925233Ssteve		if (nulok && (ap == NULL || *ap == '\0'))
86025233Ssteve			return 0;
8611556Srgrimes	}
8621556Srgrimes	return 1;
8631556Srgrimes}
8641556Srgrimes
865216384Sjillesstatic void
866216384Sjillesstrtodest(const char *p, int flag, int subtype, int quoted)
867216384Sjilles{
868216384Sjilles	if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH)
869216384Sjilles		STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest);
870216384Sjilles	else
871216384Sjilles		STPUTS(p, expdest);
872216384Sjilles}
8731556Srgrimes
8741556Srgrimes/*
8751556Srgrimes * Add the value of a specialized variable to the stack string.
8761556Srgrimes */
8771556Srgrimes
878213811Sobrienstatic void
879264168Sjillesvarvalue(const char *name, int quoted, int subtype, int flag)
88017987Speter{
8811556Srgrimes	int num;
8821556Srgrimes	char *p;
8831556Srgrimes	int i;
8841556Srgrimes	char sep;
8851556Srgrimes	char **ap;
8861556Srgrimes
88718202Speter	switch (*name) {
8881556Srgrimes	case '$':
8891556Srgrimes		num = rootpid;
8901556Srgrimes		goto numvar;
8911556Srgrimes	case '?':
89217987Speter		num = oexitstatus;
8931556Srgrimes		goto numvar;
8941556Srgrimes	case '#':
8951556Srgrimes		num = shellparam.nparam;
8961556Srgrimes		goto numvar;
8971556Srgrimes	case '!':
898209600Sjilles		num = backgndpidval();
8991556Srgrimesnumvar:
90017987Speter		expdest = cvtnum(num, expdest);
9011556Srgrimes		break;
9021556Srgrimes	case '-':
9031556Srgrimes		for (i = 0 ; i < NOPTS ; i++) {
9041556Srgrimes			if (optlist[i].val)
9051556Srgrimes				STPUTC(optlist[i].letter, expdest);
9061556Srgrimes		}
9071556Srgrimes		break;
9081556Srgrimes	case '@':
909164081Sstefanf		if (flag & EXP_FULL && quoted) {
91038887Stegge			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
911216384Sjilles				strtodest(p, flag, subtype, quoted);
91238887Stegge				if (*ap)
91338887Stegge					STPUTC('\0', expdest);
91438887Stegge			}
91538887Stegge			break;
9161556Srgrimes		}
917102410Scharnier		/* FALLTHROUGH */
9181556Srgrimes	case '*':
919149825Srse		if (ifsset())
92038887Stegge			sep = ifsval()[0];
92138887Stegge		else
92238887Stegge			sep = ' ';
9231556Srgrimes		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
924216384Sjilles			strtodest(p, flag, subtype, quoted);
925222361Sjilles			if (!*ap)
926222361Sjilles				break;
927222361Sjilles			if (sep || (flag & EXP_FULL && !quoted && **ap != '\0'))
9281556Srgrimes				STPUTC(sep, expdest);
9291556Srgrimes		}
9301556Srgrimes		break;
9311556Srgrimes	case '0':
9321556Srgrimes		p = arg0;
933216384Sjilles		strtodest(p, flag, subtype, quoted);
9341556Srgrimes		break;
9351556Srgrimes	default:
93618202Speter		if (is_digit(*name)) {
93718202Speter			num = atoi(name);
93818202Speter			if (num > 0 && num <= shellparam.nparam) {
93918202Speter				p = shellparam.p[num - 1];
940216384Sjilles				strtodest(p, flag, subtype, quoted);
94118202Speter			}
9421556Srgrimes		}
9431556Srgrimes		break;
9441556Srgrimes	}
9451556Srgrimes}
9461556Srgrimes
9471556Srgrimes
9481556Srgrimes
9491556Srgrimes/*
950218909Sbrucec * Record the fact that we have to scan this region of the
9511556Srgrimes * string for IFS characters.
9521556Srgrimes */
9531556Srgrimes
954213811Sobrienstatic void
955194975Sjillesrecordregion(int start, int end, int inquotes)
95617987Speter{
95725233Ssteve	struct ifsregion *ifsp;
9581556Srgrimes
959264478Sjilles	INTOFF;
9601556Srgrimes	if (ifslastp == NULL) {
9611556Srgrimes		ifsp = &ifsfirst;
9621556Srgrimes	} else {
963194975Sjilles		if (ifslastp->endoff == start
964194975Sjilles		    && ifslastp->inquotes == inquotes) {
965194975Sjilles			/* extend previous area */
966194975Sjilles			ifslastp->endoff = end;
967264478Sjilles			INTON;
968194975Sjilles			return;
969194975Sjilles		}
9701556Srgrimes		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
9711556Srgrimes		ifslastp->next = ifsp;
9721556Srgrimes	}
9731556Srgrimes	ifslastp = ifsp;
9741556Srgrimes	ifslastp->next = NULL;
9751556Srgrimes	ifslastp->begoff = start;
9761556Srgrimes	ifslastp->endoff = end;
977194975Sjilles	ifslastp->inquotes = inquotes;
978264478Sjilles	INTON;
9791556Srgrimes}
9801556Srgrimes
9811556Srgrimes
9821556Srgrimes
9831556Srgrimes/*
9841556Srgrimes * Break the argument string into pieces based upon IFS and add the
9851556Srgrimes * strings to the argument list.  The regions of the string to be
9861556Srgrimes * searched for IFS characters have been stored by recordregion.
987212243Sjilles * CTLESC characters are preserved but have little effect in this pass
988212243Sjilles * other than escaping CTL* characters.  In particular, they do not escape
989212243Sjilles * IFS characters: that should be done with the ifsregion mechanism.
990212243Sjilles * CTLQUOTEMARK characters are used to preserve empty quoted strings.
991212243Sjilles * This pass treats them as a regular character, making the string non-empty.
992212243Sjilles * Later, they are removed along with the other CTL* characters.
9931556Srgrimes */
994213811Sobrienstatic void
99590111Simpifsbreakup(char *string, struct arglist *arglist)
99690111Simp{
9971556Srgrimes	struct ifsregion *ifsp;
9981556Srgrimes	struct strlist *sp;
9991556Srgrimes	char *start;
100025233Ssteve	char *p;
10011556Srgrimes	char *q;
1002201053Sjilles	const char *ifs;
1003194975Sjilles	const char *ifsspc;
1004194975Sjilles	int had_param_ch = 0;
10051556Srgrimes
1006194975Sjilles	start = string;
100717987Speter
1008194975Sjilles	if (ifslastp == NULL) {
1009194975Sjilles		/* Return entire argument, IFS doesn't apply to any of it */
1010194975Sjilles		sp = (struct strlist *)stalloc(sizeof *sp);
1011194975Sjilles		sp->text = start;
1012194975Sjilles		*arglist->lastp = sp;
1013194975Sjilles		arglist->lastp = &sp->next;
1014194975Sjilles		return;
1015194975Sjilles	}
1016194975Sjilles
1017194975Sjilles	ifs = ifsset() ? ifsval() : " \t\n";
1018194975Sjilles
1019194975Sjilles	for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
1020194975Sjilles		p = string + ifsp->begoff;
1021194975Sjilles		while (p < string + ifsp->endoff) {
1022194975Sjilles			q = p;
1023194975Sjilles			if (*p == CTLESC)
1024194975Sjilles				p++;
1025194975Sjilles			if (ifsp->inquotes) {
1026194975Sjilles				/* Only NULs (should be from "$@") end args */
1027194977Sjilles				had_param_ch = 1;
1028194975Sjilles				if (*p != 0) {
10291556Srgrimes					p++;
1030194975Sjilles					continue;
1031194975Sjilles				}
1032194975Sjilles				ifsspc = NULL;
1033194975Sjilles			} else {
1034194975Sjilles				if (!strchr(ifs, *p)) {
1035194977Sjilles					had_param_ch = 1;
103638887Stegge					p++;
1037194975Sjilles					continue;
1038194975Sjilles				}
1039194975Sjilles				ifsspc = strchr(" \t\n", *p);
1040194975Sjilles
1041194975Sjilles				/* Ignore IFS whitespace at start */
1042194975Sjilles				if (q == start && ifsspc != NULL) {
1043194975Sjilles					p++;
10441556Srgrimes					start = p;
1045194975Sjilles					continue;
1046194975Sjilles				}
1047194977Sjilles				had_param_ch = 0;
10481556Srgrimes			}
1049194975Sjilles
1050194975Sjilles			/* Save this argument... */
1051194975Sjilles			*q = '\0';
10521556Srgrimes			sp = (struct strlist *)stalloc(sizeof *sp);
10531556Srgrimes			sp->text = start;
10541556Srgrimes			*arglist->lastp = sp;
10551556Srgrimes			arglist->lastp = &sp->next;
1056194975Sjilles			p++;
1057194975Sjilles
1058194975Sjilles			if (ifsspc != NULL) {
1059194975Sjilles				/* Ignore further trailing IFS whitespace */
1060194975Sjilles				for (; p < string + ifsp->endoff; p++) {
1061194975Sjilles					q = p;
1062194975Sjilles					if (*p == CTLESC)
1063194975Sjilles						p++;
1064194975Sjilles					if (strchr(ifs, *p) == NULL) {
1065194975Sjilles						p = q;
1066194975Sjilles						break;
1067194975Sjilles					}
1068194975Sjilles					if (strchr(" \t\n", *p) == NULL) {
1069194975Sjilles						p++;
1070194975Sjilles						break;
1071194975Sjilles					}
1072194975Sjilles				}
1073194975Sjilles			}
1074194975Sjilles			start = p;
10751556Srgrimes		}
1076194975Sjilles	}
1077194975Sjilles
1078194975Sjilles	/*
1079194975Sjilles	 * Save anything left as an argument.
1080194975Sjilles	 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
1081194975Sjilles	 * generating 2 arguments, the second of which is empty.
1082194975Sjilles	 * Some recent clarification of the Posix spec say that it
1083194975Sjilles	 * should only generate one....
1084194975Sjilles	 */
1085194975Sjilles	if (had_param_ch || *start != 0) {
10861556Srgrimes		sp = (struct strlist *)stalloc(sizeof *sp);
10871556Srgrimes		sp->text = start;
10881556Srgrimes		*arglist->lastp = sp;
10891556Srgrimes		arglist->lastp = &sp->next;
10901556Srgrimes	}
10911556Srgrimes}
10921556Srgrimes
10931556Srgrimes
1094213760Sobrienstatic char expdir[PATH_MAX];
1095212243Sjilles#define expdir_end (expdir + sizeof(expdir))
10961556Srgrimes
10971556Srgrimes/*
1098212243Sjilles * Perform pathname generation and remove control characters.
1099212243Sjilles * At this point, the only control characters should be CTLESC and CTLQUOTEMARK.
1100212243Sjilles * The results are stored in the list exparg.
11011556Srgrimes */
1102213811Sobrienstatic void
110390111Simpexpandmeta(struct strlist *str, int flag __unused)
110417987Speter{
11051556Srgrimes	char *p;
11061556Srgrimes	struct strlist **savelastp;
11071556Srgrimes	struct strlist *sp;
11081556Srgrimes	char c;
11091556Srgrimes	/* TODO - EXP_REDIR */
11101556Srgrimes
11111556Srgrimes	while (str) {
11121556Srgrimes		if (fflag)
11131556Srgrimes			goto nometa;
11141556Srgrimes		p = str->text;
11151556Srgrimes		for (;;) {			/* fast check for meta chars */
11161556Srgrimes			if ((c = *p++) == '\0')
11171556Srgrimes				goto nometa;
1118211646Sjilles			if (c == '*' || c == '?' || c == '[')
11191556Srgrimes				break;
11201556Srgrimes		}
11211556Srgrimes		savelastp = exparg.lastp;
11221556Srgrimes		INTOFF;
11231556Srgrimes		expmeta(expdir, str->text);
11241556Srgrimes		INTON;
11251556Srgrimes		if (exparg.lastp == savelastp) {
11268855Srgrimes			/*
11278855Srgrimes			 * no matches
11281556Srgrimes			 */
11291556Srgrimesnometa:
11301556Srgrimes			*exparg.lastp = str;
11311556Srgrimes			rmescapes(str->text);
11321556Srgrimes			exparg.lastp = &str->next;
11331556Srgrimes		} else {
11341556Srgrimes			*exparg.lastp = NULL;
11351556Srgrimes			*savelastp = sp = expsort(*savelastp);
11361556Srgrimes			while (sp->next != NULL)
11371556Srgrimes				sp = sp->next;
11381556Srgrimes			exparg.lastp = &sp->next;
11391556Srgrimes		}
11401556Srgrimes		str = str->next;
11411556Srgrimes	}
11421556Srgrimes}
11431556Srgrimes
11441556Srgrimes
11451556Srgrimes/*
11461556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion.
11471556Srgrimes */
11481556Srgrimes
1149213811Sobrienstatic void
115090111Simpexpmeta(char *enddir, char *name)
115190111Simp{
1152248980Sjilles	const char *p;
1153248980Sjilles	const char *q;
1154248980Sjilles	const char *start;
11551556Srgrimes	char *endname;
11561556Srgrimes	int metaflag;
11571556Srgrimes	struct stat statb;
11581556Srgrimes	DIR *dirp;
11591556Srgrimes	struct dirent *dp;
11601556Srgrimes	int atend;
11611556Srgrimes	int matchdot;
1162207944Sjilles	int esc;
1163228941Sjilles	int namlen;
11641556Srgrimes
11651556Srgrimes	metaflag = 0;
11661556Srgrimes	start = name;
1167207944Sjilles	for (p = name; esc = 0, *p; p += esc + 1) {
11681556Srgrimes		if (*p == '*' || *p == '?')
11691556Srgrimes			metaflag = 1;
11701556Srgrimes		else if (*p == '[') {
11711556Srgrimes			q = p + 1;
117226488Sache			if (*q == '!' || *q == '^')
11731556Srgrimes				q++;
11741556Srgrimes			for (;;) {
117538887Stegge				while (*q == CTLQUOTEMARK)
117638887Stegge					q++;
11771556Srgrimes				if (*q == CTLESC)
11781556Srgrimes					q++;
11791556Srgrimes				if (*q == '/' || *q == '\0')
11801556Srgrimes					break;
11811556Srgrimes				if (*++q == ']') {
11821556Srgrimes					metaflag = 1;
11831556Srgrimes					break;
11841556Srgrimes				}
11851556Srgrimes			}
11861556Srgrimes		} else if (*p == '\0')
11871556Srgrimes			break;
118838887Stegge		else if (*p == CTLQUOTEMARK)
118938887Stegge			continue;
1190207944Sjilles		else {
1191207944Sjilles			if (*p == CTLESC)
1192207944Sjilles				esc++;
1193207944Sjilles			if (p[esc] == '/') {
1194207944Sjilles				if (metaflag)
1195207944Sjilles					break;
1196207944Sjilles				start = p + esc + 1;
1197207944Sjilles			}
11981556Srgrimes		}
11991556Srgrimes	}
12001556Srgrimes	if (metaflag == 0) {	/* we've reached the end of the file name */
12011556Srgrimes		if (enddir != expdir)
12021556Srgrimes			metaflag++;
12031556Srgrimes		for (p = name ; ; p++) {
120438887Stegge			if (*p == CTLQUOTEMARK)
120538887Stegge				continue;
12061556Srgrimes			if (*p == CTLESC)
12071556Srgrimes				p++;
12081556Srgrimes			*enddir++ = *p;
12091556Srgrimes			if (*p == '\0')
12101556Srgrimes				break;
1211211155Sjilles			if (enddir == expdir_end)
1212211155Sjilles				return;
12131556Srgrimes		}
1214147812Sdelphij		if (metaflag == 0 || lstat(expdir, &statb) >= 0)
12151556Srgrimes			addfname(expdir);
12161556Srgrimes		return;
12171556Srgrimes	}
1218248980Sjilles	endname = name + (p - name);
12191556Srgrimes	if (start != name) {
12201556Srgrimes		p = name;
12211556Srgrimes		while (p < start) {
122238887Stegge			while (*p == CTLQUOTEMARK)
122338887Stegge				p++;
12241556Srgrimes			if (*p == CTLESC)
12251556Srgrimes				p++;
12261556Srgrimes			*enddir++ = *p++;
1227211155Sjilles			if (enddir == expdir_end)
1228211155Sjilles				return;
12291556Srgrimes		}
12301556Srgrimes	}
12311556Srgrimes	if (enddir == expdir) {
12321556Srgrimes		p = ".";
12331556Srgrimes	} else if (enddir == expdir + 1 && *expdir == '/') {
12341556Srgrimes		p = "/";
12351556Srgrimes	} else {
12361556Srgrimes		p = expdir;
12371556Srgrimes		enddir[-1] = '\0';
12381556Srgrimes	}
12391556Srgrimes	if ((dirp = opendir(p)) == NULL)
12401556Srgrimes		return;
12411556Srgrimes	if (enddir != expdir)
12421556Srgrimes		enddir[-1] = '/';
12431556Srgrimes	if (*endname == 0) {
12441556Srgrimes		atend = 1;
12451556Srgrimes	} else {
12461556Srgrimes		atend = 0;
1247207944Sjilles		*endname = '\0';
1248207944Sjilles		endname += esc + 1;
12491556Srgrimes	}
12501556Srgrimes	matchdot = 0;
125138887Stegge	p = start;
125238887Stegge	while (*p == CTLQUOTEMARK)
125338887Stegge		p++;
125438887Stegge	if (*p == CTLESC)
125538887Stegge		p++;
125638887Stegge	if (*p == '.')
12571556Srgrimes		matchdot++;
12581556Srgrimes	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
12591556Srgrimes		if (dp->d_name[0] == '.' && ! matchdot)
12601556Srgrimes			continue;
126145514Stegge		if (patmatch(start, dp->d_name, 0)) {
1262228941Sjilles			namlen = dp->d_namlen;
1263228941Sjilles			if (enddir + namlen + 1 > expdir_end)
1264211155Sjilles				continue;
1265228941Sjilles			memcpy(enddir, dp->d_name, namlen + 1);
1266211155Sjilles			if (atend)
12671556Srgrimes				addfname(expdir);
1268211155Sjilles			else {
1269228942Sjilles				if (dp->d_type != DT_UNKNOWN &&
1270228942Sjilles				    dp->d_type != DT_DIR &&
1271228942Sjilles				    dp->d_type != DT_LNK)
1272228942Sjilles					continue;
1273228941Sjilles				if (enddir + namlen + 2 > expdir_end)
127417987Speter					continue;
1275228941Sjilles				enddir[namlen] = '/';
1276228941Sjilles				enddir[namlen + 1] = '\0';
1277228941Sjilles				expmeta(enddir + namlen + 1, endname);
12781556Srgrimes			}
12791556Srgrimes		}
12801556Srgrimes	}
12811556Srgrimes	closedir(dirp);
12821556Srgrimes	if (! atend)
1283207944Sjilles		endname[-esc - 1] = esc ? CTLESC : '/';
12841556Srgrimes}
12851556Srgrimes
12861556Srgrimes
12871556Srgrimes/*
12881556Srgrimes * Add a file name to the list.
12891556Srgrimes */
12901556Srgrimes
1291213811Sobrienstatic void
129290111Simpaddfname(char *name)
129390111Simp{
12941556Srgrimes	char *p;
12951556Srgrimes	struct strlist *sp;
1296262951Sjmmv	size_t len;
12971556Srgrimes
1298262951Sjmmv	len = strlen(name);
1299262951Sjmmv	p = stalloc(len + 1);
1300262951Sjmmv	memcpy(p, name, len + 1);
13011556Srgrimes	sp = (struct strlist *)stalloc(sizeof *sp);
13021556Srgrimes	sp->text = p;
13031556Srgrimes	*exparg.lastp = sp;
13041556Srgrimes	exparg.lastp = &sp->next;
13051556Srgrimes}
13061556Srgrimes
13071556Srgrimes
13081556Srgrimes/*
13091556Srgrimes * Sort the results of file name expansion.  It calculates the number of
13101556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the
13111556Srgrimes * work.
13121556Srgrimes */
13131556Srgrimes
1314213811Sobrienstatic struct strlist *
131590111Simpexpsort(struct strlist *str)
131690111Simp{
13171556Srgrimes	int len;
13181556Srgrimes	struct strlist *sp;
13191556Srgrimes
13201556Srgrimes	len = 0;
13211556Srgrimes	for (sp = str ; sp ; sp = sp->next)
13221556Srgrimes		len++;
13231556Srgrimes	return msort(str, len);
13241556Srgrimes}
13251556Srgrimes
13261556Srgrimes
1327213811Sobrienstatic struct strlist *
132890111Simpmsort(struct strlist *list, int len)
132917987Speter{
133017987Speter	struct strlist *p, *q = NULL;
13311556Srgrimes	struct strlist **lpp;
13321556Srgrimes	int half;
13331556Srgrimes	int n;
13341556Srgrimes
13351556Srgrimes	if (len <= 1)
13361556Srgrimes		return list;
13378855Srgrimes	half = len >> 1;
13381556Srgrimes	p = list;
13391556Srgrimes	for (n = half ; --n >= 0 ; ) {
13401556Srgrimes		q = p;
13411556Srgrimes		p = p->next;
13421556Srgrimes	}
13431556Srgrimes	q->next = NULL;			/* terminate first half of list */
13441556Srgrimes	q = msort(list, half);		/* sort first half of list */
13451556Srgrimes	p = msort(p, len - half);		/* sort second half */
13461556Srgrimes	lpp = &list;
13471556Srgrimes	for (;;) {
13481556Srgrimes		if (strcmp(p->text, q->text) < 0) {
13491556Srgrimes			*lpp = p;
13501556Srgrimes			lpp = &p->next;
13511556Srgrimes			if ((p = *lpp) == NULL) {
13521556Srgrimes				*lpp = q;
13531556Srgrimes				break;
13541556Srgrimes			}
13551556Srgrimes		} else {
13561556Srgrimes			*lpp = q;
13571556Srgrimes			lpp = &q->next;
13581556Srgrimes			if ((q = *lpp) == NULL) {
13591556Srgrimes				*lpp = p;
13601556Srgrimes				break;
13611556Srgrimes			}
13621556Srgrimes		}
13631556Srgrimes	}
13641556Srgrimes	return list;
13651556Srgrimes}
13661556Srgrimes
13671556Srgrimes
13681556Srgrimes
1369221646Sjillesstatic wchar_t
1370221646Sjillesget_wc(const char **p)
1371221646Sjilles{
1372221646Sjilles	wchar_t c;
1373221646Sjilles	int chrlen;
1374221646Sjilles
1375221646Sjilles	chrlen = mbtowc(&c, *p, 4);
1376221646Sjilles	if (chrlen == 0)
1377221646Sjilles		return 0;
1378221646Sjilles	else if (chrlen == -1)
1379221646Sjilles		c = 0;
1380221646Sjilles	else
1381221646Sjilles		*p += chrlen;
1382221646Sjilles	return c;
1383221646Sjilles}
1384221646Sjilles
1385221646Sjilles
13861556Srgrimes/*
1387223120Sjilles * See if a character matches a character class, starting at the first colon
1388223120Sjilles * of "[:class:]".
1389223120Sjilles * If a valid character class is recognized, a pointer to the next character
1390223120Sjilles * after the final closing bracket is stored into *end, otherwise a null
1391223120Sjilles * pointer is stored into *end.
1392223120Sjilles */
1393223120Sjillesstatic int
1394223120Sjillesmatch_charclass(const char *p, wchar_t chr, const char **end)
1395223120Sjilles{
1396223120Sjilles	char name[20];
1397223120Sjilles	const char *nameend;
1398223120Sjilles	wctype_t cclass;
1399223120Sjilles
1400223120Sjilles	*end = NULL;
1401223120Sjilles	p++;
1402223120Sjilles	nameend = strstr(p, ":]");
1403248980Sjilles	if (nameend == NULL || (size_t)(nameend - p) >= sizeof(name) ||
1404248980Sjilles	    nameend == p)
1405223120Sjilles		return 0;
1406223120Sjilles	memcpy(name, p, nameend - p);
1407223120Sjilles	name[nameend - p] = '\0';
1408223120Sjilles	*end = nameend + 2;
1409223120Sjilles	cclass = wctype(name);
1410223120Sjilles	/* An unknown class matches nothing but is valid nevertheless. */
1411223120Sjilles	if (cclass == 0)
1412223120Sjilles		return 0;
1413223120Sjilles	return iswctype(chr, cclass);
1414223120Sjilles}
1415223120Sjilles
1416223120Sjilles
1417223120Sjilles/*
14181556Srgrimes * Returns true if the pattern matches the string.
14191556Srgrimes */
14201556Srgrimes
1421229220Sjillesstatic int
1422200956Sjillespatmatch(const char *pattern, const char *string, int squoted)
142390111Simp{
1424223120Sjilles	const char *p, *q, *end;
1425229201Sjilles	const char *bt_p, *bt_q;
142625233Ssteve	char c;
1427221646Sjilles	wchar_t wc, wc2;
14281556Srgrimes
14291556Srgrimes	p = pattern;
14301556Srgrimes	q = string;
1431229201Sjilles	bt_p = NULL;
1432229201Sjilles	bt_q = NULL;
14331556Srgrimes	for (;;) {
14341556Srgrimes		switch (c = *p++) {
14351556Srgrimes		case '\0':
1436229201Sjilles			if (*q != '\0')
1437229201Sjilles				goto backtrack;
1438229201Sjilles			return 1;
14391556Srgrimes		case CTLESC:
144045514Stegge			if (squoted && *q == CTLESC)
144145514Stegge				q++;
14421556Srgrimes			if (*q++ != *p++)
1443229201Sjilles				goto backtrack;
14441556Srgrimes			break;
144538887Stegge		case CTLQUOTEMARK:
144638887Stegge			continue;
14471556Srgrimes		case '?':
144845514Stegge			if (squoted && *q == CTLESC)
144945514Stegge				q++;
1450229201Sjilles			if (*q == '\0')
1451229201Sjilles				return 0;
1452229201Sjilles			if (localeisutf8) {
1453221646Sjilles				wc = get_wc(&q);
1454229201Sjilles				/*
1455229201Sjilles				 * A '?' does not match invalid UTF-8 but a
1456229201Sjilles				 * '*' does, so backtrack.
1457229201Sjilles				 */
1458229201Sjilles				if (wc == 0)
1459229201Sjilles					goto backtrack;
1460229201Sjilles			} else
1461223010Sjilles				wc = (unsigned char)*q++;
14621556Srgrimes			break;
14631556Srgrimes		case '*':
14641556Srgrimes			c = *p;
146538887Stegge			while (c == CTLQUOTEMARK || c == '*')
146638887Stegge				c = *++p;
1467229201Sjilles			/*
1468229201Sjilles			 * If the pattern ends here, we know the string
1469229201Sjilles			 * matches without needing to look at the rest of it.
1470229201Sjilles			 */
1471229201Sjilles			if (c == '\0')
1472229201Sjilles				return 1;
1473229201Sjilles			/*
1474229201Sjilles			 * First try the shortest match for the '*' that
1475229201Sjilles			 * could work. We can forget any earlier '*' since
1476229201Sjilles			 * there is no way having it match more characters
1477229201Sjilles			 * can help us, given that we are already here.
1478229201Sjilles			 */
1479229201Sjilles			bt_p = p;
1480229201Sjilles			bt_q = q;
1481229201Sjilles			break;
14821556Srgrimes		case '[': {
1483200956Sjilles			const char *endp;
14841556Srgrimes			int invert, found;
1485221646Sjilles			wchar_t chr;
14861556Srgrimes
14871556Srgrimes			endp = p;
148826488Sache			if (*endp == '!' || *endp == '^')
14891556Srgrimes				endp++;
14901556Srgrimes			for (;;) {
149138887Stegge				while (*endp == CTLQUOTEMARK)
149238887Stegge					endp++;
1493229201Sjilles				if (*endp == 0)
14941556Srgrimes					goto dft;		/* no matching ] */
14951556Srgrimes				if (*endp == CTLESC)
14961556Srgrimes					endp++;
14971556Srgrimes				if (*++endp == ']')
14981556Srgrimes					break;
14991556Srgrimes			}
15001556Srgrimes			invert = 0;
150126488Sache			if (*p == '!' || *p == '^') {
15021556Srgrimes				invert++;
15031556Srgrimes				p++;
15041556Srgrimes			}
15051556Srgrimes			found = 0;
1506221646Sjilles			if (squoted && *q == CTLESC)
1507221646Sjilles				q++;
1508229201Sjilles			if (*q == '\0')
1509229201Sjilles				return 0;
1510229201Sjilles			if (localeisutf8) {
1511221646Sjilles				chr = get_wc(&q);
1512229201Sjilles				if (chr == 0)
1513229201Sjilles					goto backtrack;
1514229201Sjilles			} else
1515223010Sjilles				chr = (unsigned char)*q++;
15161556Srgrimes			c = *p++;
15171556Srgrimes			do {
151838887Stegge				if (c == CTLQUOTEMARK)
151938887Stegge					continue;
1520223120Sjilles				if (c == '[' && *p == ':') {
1521223120Sjilles					found |= match_charclass(p, chr, &end);
1522223120Sjilles					if (end != NULL)
1523223120Sjilles						p = end;
1524223120Sjilles				}
15251556Srgrimes				if (c == CTLESC)
15261556Srgrimes					c = *p++;
1527221646Sjilles				if (localeisutf8 && c & 0x80) {
1528221646Sjilles					p--;
1529221646Sjilles					wc = get_wc(&p);
1530221646Sjilles					if (wc == 0) /* bad utf-8 */
1531221646Sjilles						return 0;
1532221646Sjilles				} else
1533223010Sjilles					wc = (unsigned char)c;
15341556Srgrimes				if (*p == '-' && p[1] != ']') {
15351556Srgrimes					p++;
153638887Stegge					while (*p == CTLQUOTEMARK)
153738887Stegge						p++;
15381556Srgrimes					if (*p == CTLESC)
15391556Srgrimes						p++;
1540221646Sjilles					if (localeisutf8) {
1541221646Sjilles						wc2 = get_wc(&p);
1542221646Sjilles						if (wc2 == 0) /* bad utf-8 */
1543221646Sjilles							return 0;
1544221646Sjilles					} else
1545223010Sjilles						wc2 = (unsigned char)*p++;
1546221646Sjilles					if (   collate_range_cmp(chr, wc) >= 0
1547221646Sjilles					    && collate_range_cmp(chr, wc2) <= 0
154817525Sache					   )
15491556Srgrimes						found = 1;
15501556Srgrimes				} else {
1551221646Sjilles					if (chr == wc)
15521556Srgrimes						found = 1;
15531556Srgrimes				}
15541556Srgrimes			} while ((c = *p++) != ']');
15551556Srgrimes			if (found == invert)
1556229201Sjilles				goto backtrack;
15571556Srgrimes			break;
15581556Srgrimes		}
15591556Srgrimesdft:	        default:
156045514Stegge			if (squoted && *q == CTLESC)
156145514Stegge				q++;
1562229201Sjilles			if (*q == '\0')
15631556Srgrimes				return 0;
1564229201Sjilles			if (*q++ == c)
1565229201Sjilles				break;
1566229201Sjillesbacktrack:
1567229201Sjilles			/*
1568229201Sjilles			 * If we have a mismatch (other than hitting the end
1569229201Sjilles			 * of the string), go back to the last '*' seen and
1570229201Sjilles			 * have it match one additional character.
1571229201Sjilles			 */
1572229201Sjilles			if (bt_p == NULL)
1573229201Sjilles				return 0;
1574229201Sjilles			if (squoted && *bt_q == CTLESC)
1575229201Sjilles				bt_q++;
1576229201Sjilles			if (*bt_q == '\0')
1577229201Sjilles				return 0;
1578229201Sjilles			bt_q++;
1579229201Sjilles			p = bt_p;
1580229201Sjilles			q = bt_q;
15811556Srgrimes			break;
15821556Srgrimes		}
15831556Srgrimes	}
15841556Srgrimes}
15851556Srgrimes
15861556Srgrimes
15871556Srgrimes
15881556Srgrimes/*
1589212243Sjilles * Remove any CTLESC and CTLQUOTEMARK characters from a string.
15901556Srgrimes */
15911556Srgrimes
15921556Srgrimesvoid
159390111Simprmescapes(char *str)
159438887Stegge{
159525233Ssteve	char *p, *q;
15961556Srgrimes
15971556Srgrimes	p = str;
1598214512Sjilles	while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) {
15991556Srgrimes		if (*p++ == '\0')
16001556Srgrimes			return;
16011556Srgrimes	}
16021556Srgrimes	q = p;
16031556Srgrimes	while (*p) {
1604214512Sjilles		if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) {
160538887Stegge			p++;
160638887Stegge			continue;
160738887Stegge		}
16081556Srgrimes		if (*p == CTLESC)
16091556Srgrimes			p++;
16101556Srgrimes		*q++ = *p++;
16111556Srgrimes	}
16121556Srgrimes	*q = '\0';
16131556Srgrimes}
16141556Srgrimes
16151556Srgrimes
16161556Srgrimes
16171556Srgrimes/*
16181556Srgrimes * See if a pattern matches in a case statement.
16191556Srgrimes */
16201556Srgrimes
16211556Srgrimesint
1622200956Sjillescasematch(union node *pattern, const char *val)
162390111Simp{
16241556Srgrimes	struct stackmark smark;
16251556Srgrimes	int result;
16261556Srgrimes	char *p;
16271556Srgrimes
16281556Srgrimes	setstackmark(&smark);
16291556Srgrimes	argbackq = pattern->narg.backquote;
16301556Srgrimes	STARTSTACKSTR(expdest);
16311556Srgrimes	ifslastp = NULL;
16321556Srgrimes	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
16331556Srgrimes	STPUTC('\0', expdest);
16341556Srgrimes	p = grabstackstr(expdest);
163545514Stegge	result = patmatch(p, val, 0);
16361556Srgrimes	popstackmark(&smark);
16371556Srgrimes	return result;
16381556Srgrimes}
163917987Speter
164017987Speter/*
164117987Speter * Our own itoa().
164217987Speter */
164317987Speter
1644213811Sobrienstatic char *
164590111Simpcvtnum(int num, char *buf)
164690111Simp{
164717987Speter	char temp[32];
164817987Speter	int neg = num < 0;
164917987Speter	char *p = temp + 31;
165017987Speter
165117987Speter	temp[31] = '\0';
165217987Speter
165317987Speter	do {
165417987Speter		*--p = num % 10 + '0';
165517987Speter	} while ((num /= 10) != 0);
165617987Speter
165717987Speter	if (neg)
165817987Speter		*--p = '-';
165917987Speter
1660215783Sjilles	STPUTS(p, buf);
166117987Speter	return buf;
166217987Speter}
1663108286Stjr
1664108286Stjr/*
1665108286Stjr * Do most of the work for wordexp(3).
1666108286Stjr */
1667108286Stjr
1668108286Stjrint
1669108286Stjrwordexpcmd(int argc, char **argv)
1670108286Stjr{
1671108286Stjr	size_t len;
1672108286Stjr	int i;
1673108286Stjr
1674108286Stjr	out1fmt("%08x", argc - 1);
1675108286Stjr	for (i = 1, len = 0; i < argc; i++)
1676108286Stjr		len += strlen(argv[i]);
1677108286Stjr	out1fmt("%08x", (int)len);
1678215567Sjilles	for (i = 1; i < argc; i++)
1679215567Sjilles		outbin(argv[i], strlen(argv[i]) + 1, out1);
1680108286Stjr        return (0);
1681108286Stjr}
1682