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: stable/10/bin/sh/expand.c 320510 2017-06-30 21:32:48Z jilles $");
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);
108287751Sjillesstatic void strtodest(const char *, int, int, int);
109264168Sjillesstatic void varvalue(const char *, int, int, int);
110213811Sobrienstatic void recordregion(int, int, int);
111213811Sobrienstatic void removerecordregions(int);
112213811Sobrienstatic void ifsbreakup(char *, struct arglist *);
113287751Sjillesstatic void expandmeta(struct strlist *);
114213811Sobrienstatic void expmeta(char *, char *);
115213811Sobrienstatic void addfname(char *);
116213811Sobrienstatic struct strlist *expsort(struct strlist *);
117213811Sobrienstatic struct strlist *msort(struct strlist *, int);
118229220Sjillesstatic int patmatch(const char *, const char *, int);
119213811Sobrienstatic char *cvtnum(int, char *);
120221646Sjillesstatic int collate_range_cmp(wchar_t, wchar_t);
1211556Srgrimes
122213811Sobrienstatic int
123221646Sjillescollate_range_cmp(wchar_t c1, wchar_t c2)
12419281Sache{
125221646Sjilles	static wchar_t s1[2], s2[2];
12619281Sache
12719281Sache	s1[0] = c1;
12819281Sache	s2[0] = c2;
129221646Sjilles	return (wcscoll(s1, s2));
13019281Sache}
13119281Sache
132216384Sjillesstatic char *
133216384Sjillesstputs_quotes(const char *data, const char *syntax, char *p)
134216384Sjilles{
135216384Sjilles	while (*data) {
136216384Sjilles		CHECKSTRSPACE(2, p);
137216384Sjilles		if (syntax[(int)*data] == CCTL)
138216384Sjilles			USTPUTC(CTLESC, p);
139216384Sjilles		USTPUTC(*data++, p);
140216384Sjilles	}
141216384Sjilles	return (p);
142216384Sjilles}
143216384Sjilles#define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p)
1441556Srgrimes
1451556Srgrimes/*
146212243Sjilles * Perform expansions on an argument, placing the resulting list of arguments
147212243Sjilles * in arglist.  Parameter expansion, command substitution and arithmetic
148212243Sjilles * expansion are always performed; additional expansions can be requested
149212243Sjilles * via flag (EXP_*).
150212243Sjilles * The result is left in the stack string.
151218203Sjilles * When arglist is NULL, perform here document expansion.
152212243Sjilles *
153212243Sjilles * Caution: this function uses global state and is not reentrant.
154212243Sjilles * However, a new invocation after an interrupted invocation is safe
155212243Sjilles * and will reset the global state for the new call.
1561556Srgrimes */
1571556Srgrimesvoid
15890111Simpexpandarg(union node *arg, struct arglist *arglist, int flag)
15917987Speter{
1601556Srgrimes	struct strlist *sp;
1611556Srgrimes	char *p;
1621556Srgrimes
1631556Srgrimes	argbackq = arg->narg.backquote;
1641556Srgrimes	STARTSTACKSTR(expdest);
1651556Srgrimes	ifsfirst.next = NULL;
1661556Srgrimes	ifslastp = NULL;
1671556Srgrimes	argstr(arg->narg.text, flag);
1681556Srgrimes	if (arglist == NULL) {
169222907Sjilles		STACKSTRNUL(expdest);
1701556Srgrimes		return;			/* here document expanded */
1711556Srgrimes	}
1721556Srgrimes	STPUTC('\0', expdest);
1731556Srgrimes	p = grabstackstr(expdest);
1741556Srgrimes	exparg.lastp = &exparg.list;
1751556Srgrimes	if (flag & EXP_FULL) {
1761556Srgrimes		ifsbreakup(p, &exparg);
1771556Srgrimes		*exparg.lastp = NULL;
1781556Srgrimes		exparg.lastp = &exparg.list;
179287751Sjilles		expandmeta(exparg.list);
1801556Srgrimes	} else {
1811556Srgrimes		sp = (struct strlist *)stalloc(sizeof (struct strlist));
1821556Srgrimes		sp->text = p;
1831556Srgrimes		*exparg.lastp = sp;
1841556Srgrimes		exparg.lastp = &sp->next;
1851556Srgrimes	}
1861556Srgrimes	while (ifsfirst.next != NULL) {
1871556Srgrimes		struct ifsregion *ifsp;
1881556Srgrimes		INTOFF;
1891556Srgrimes		ifsp = ifsfirst.next->next;
1901556Srgrimes		ckfree(ifsfirst.next);
1911556Srgrimes		ifsfirst.next = ifsp;
1921556Srgrimes		INTON;
1931556Srgrimes	}
1941556Srgrimes	*exparg.lastp = NULL;
1951556Srgrimes	if (exparg.list) {
1961556Srgrimes		*arglist->lastp = exparg.list;
1971556Srgrimes		arglist->lastp = exparg.lastp;
1981556Srgrimes	}
1991556Srgrimes}
2001556Srgrimes
2011556Srgrimes
2021556Srgrimes
2031556Srgrimes/*
204212243Sjilles * Perform parameter expansion, command substitution and arithmetic
205212243Sjilles * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE.
206262951Sjmmv * Processing ends at a CTLENDVAR or CTLENDARI character as well as '\0'.
207212243Sjilles * This is used to expand word in ${var+word} etc.
208276365Sjilles * If EXP_FULL or EXP_CASE are set, keep and/or generate CTLESC
209212243Sjilles * characters to allow for further processing.
210212243Sjilles * If EXP_FULL is set, also preserve CTLQUOTEMARK characters.
2111556Srgrimes */
212264166Sjillesstatic char *
21390111Simpargstr(char *p, int flag)
21417987Speter{
21525233Ssteve	char c;
216276365Sjilles	int quotes = flag & (EXP_FULL | EXP_CASE);	/* do CTLESC */
2171556Srgrimes	int firsteq = 1;
218214512Sjilles	int split_lit;
219214512Sjilles	int lit_quoted;
2201556Srgrimes
221214512Sjilles	split_lit = flag & EXP_SPLIT_LIT;
222214512Sjilles	lit_quoted = flag & EXP_LIT_QUOTED;
223214512Sjilles	flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED);
2241556Srgrimes	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
2251556Srgrimes		p = exptilde(p, flag);
2261556Srgrimes	for (;;) {
227215783Sjilles		CHECKSTRSPACE(2, expdest);
2281556Srgrimes		switch (c = *p++) {
2291556Srgrimes		case '\0':
230264166Sjilles			return (p - 1);
231212243Sjilles		case CTLENDVAR:
232262951Sjmmv		case CTLENDARI:
233264166Sjilles			return (p);
23438887Stegge		case CTLQUOTEMARK:
235214512Sjilles			lit_quoted = 1;
23638887Stegge			/* "$@" syntax adherence hack */
23738887Stegge			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
23838887Stegge				break;
23939137Stegge			if ((flag & EXP_FULL) != 0)
240215783Sjilles				USTPUTC(c, expdest);
24138887Stegge			break;
242214512Sjilles		case CTLQUOTEEND:
243214512Sjilles			lit_quoted = 0;
244214512Sjilles			break;
2451556Srgrimes		case CTLESC:
2461556Srgrimes			if (quotes)
247215783Sjilles				USTPUTC(c, expdest);
2481556Srgrimes			c = *p++;
249215783Sjilles			USTPUTC(c, expdest);
250214512Sjilles			if (split_lit && !lit_quoted)
251214512Sjilles				recordregion(expdest - stackblock() -
252214512Sjilles				    (quotes ? 2 : 1),
253214512Sjilles				    expdest - stackblock(), 0);
2541556Srgrimes			break;
2551556Srgrimes		case CTLVAR:
2561556Srgrimes			p = evalvar(p, flag);
2571556Srgrimes			break;
2581556Srgrimes		case CTLBACKQ:
2591556Srgrimes		case CTLBACKQ|CTLQUOTE:
2601556Srgrimes			expbackq(argbackq->n, c & CTLQUOTE, flag);
2611556Srgrimes			argbackq = argbackq->next;
2621556Srgrimes			break;
263262951Sjmmv		case CTLARI:
264262951Sjmmv			p = expari(p);
2651556Srgrimes			break;
2661556Srgrimes		case ':':
2671556Srgrimes		case '=':
2681556Srgrimes			/*
2691556Srgrimes			 * sort of a hack - expand tildes in variable
2701556Srgrimes			 * assignments (after the first '=' and after ':'s).
2711556Srgrimes			 */
272215783Sjilles			USTPUTC(c, expdest);
273214512Sjilles			if (split_lit && !lit_quoted)
274214512Sjilles				recordregion(expdest - stackblock() - 1,
275214512Sjilles				    expdest - stackblock(), 0);
276214512Sjilles			if (flag & EXP_VARTILDE && *p == '~' &&
277214512Sjilles			    (c != '=' || firsteq)) {
278214512Sjilles				if (c == '=')
279214512Sjilles					firsteq = 0;
2801556Srgrimes				p = exptilde(p, flag);
2811556Srgrimes			}
2821556Srgrimes			break;
2831556Srgrimes		default:
284215783Sjilles			USTPUTC(c, expdest);
285214512Sjilles			if (split_lit && !lit_quoted)
286214512Sjilles				recordregion(expdest - stackblock() - 1,
287214512Sjilles				    expdest - stackblock(), 0);
2881556Srgrimes		}
2891556Srgrimes	}
2901556Srgrimes}
2911556Srgrimes
292212243Sjilles/*
293212243Sjilles * Perform tilde expansion, placing the result in the stack string and
294212243Sjilles * returning the next position in the input string to process.
295212243Sjilles */
296213811Sobrienstatic char *
29790111Simpexptilde(char *p, int flag)
29817987Speter{
2991556Srgrimes	char c, *startp = p;
3001556Srgrimes	struct passwd *pw;
3011556Srgrimes	char *home;
3021556Srgrimes
303287751Sjilles	for (;;) {
304287751Sjilles		c = *p;
3051556Srgrimes		switch(c) {
306200988Sjilles		case CTLESC: /* This means CTL* are always considered quoted. */
307200988Sjilles		case CTLVAR:
308200988Sjilles		case CTLBACKQ:
309200988Sjilles		case CTLBACKQ | CTLQUOTE:
310200988Sjilles		case CTLARI:
311200988Sjilles		case CTLENDARI:
31239137Stegge		case CTLQUOTEMARK:
31339137Stegge			return (startp);
3141556Srgrimes		case ':':
315287751Sjilles			if ((flag & EXP_VARTILDE) == 0)
316287751Sjilles				break;
317287751Sjilles			/* FALLTHROUGH */
318287751Sjilles		case '\0':
3191556Srgrimes		case '/':
320206150Sjilles		case CTLENDVAR:
321287751Sjilles			*p = '\0';
322287751Sjilles			if (*(startp+1) == '\0') {
323287751Sjilles				home = lookupvar("HOME");
324287751Sjilles			} else {
325287751Sjilles				pw = getpwnam(startp+1);
326287751Sjilles				home = pw != NULL ? pw->pw_dir : NULL;
327287751Sjilles			}
328287751Sjilles			*p = c;
329287751Sjilles			if (home == NULL || *home == '\0')
330287751Sjilles				return (startp);
331287751Sjilles			strtodest(home, flag, VSNORMAL, 1);
332287751Sjilles			return (p);
3331556Srgrimes		}
3341556Srgrimes		p++;
3351556Srgrimes	}
3361556Srgrimes}
3371556Srgrimes
3381556Srgrimes
339213811Sobrienstatic void
34090111Simpremoverecordregions(int endoff)
34138887Stegge{
34238887Stegge	if (ifslastp == NULL)
34338887Stegge		return;
34438887Stegge
34538887Stegge	if (ifsfirst.endoff > endoff) {
34638887Stegge		while (ifsfirst.next != NULL) {
34738887Stegge			struct ifsregion *ifsp;
34838887Stegge			INTOFF;
34938887Stegge			ifsp = ifsfirst.next->next;
35038887Stegge			ckfree(ifsfirst.next);
35138887Stegge			ifsfirst.next = ifsp;
35238887Stegge			INTON;
35338887Stegge		}
35438887Stegge		if (ifsfirst.begoff > endoff)
35538887Stegge			ifslastp = NULL;
35638887Stegge		else {
35738887Stegge			ifslastp = &ifsfirst;
35838887Stegge			ifsfirst.endoff = endoff;
35938887Stegge		}
36038887Stegge		return;
36138887Stegge	}
362155301Sschweikh
36338887Stegge	ifslastp = &ifsfirst;
36438887Stegge	while (ifslastp->next && ifslastp->next->begoff < endoff)
36538887Stegge		ifslastp=ifslastp->next;
36638887Stegge	while (ifslastp->next != NULL) {
36738887Stegge		struct ifsregion *ifsp;
36838887Stegge		INTOFF;
36938887Stegge		ifsp = ifslastp->next->next;
37038887Stegge		ckfree(ifslastp->next);
37138887Stegge		ifslastp->next = ifsp;
37238887Stegge		INTON;
37338887Stegge	}
37438887Stegge	if (ifslastp->endoff > endoff)
37538887Stegge		ifslastp->endoff = endoff;
37638887Stegge}
37738887Stegge
3781556Srgrimes/*
379262951Sjmmv * Expand arithmetic expression.
380262951Sjmmv * Note that flag is not required as digits never require CTLESC characters.
3811556Srgrimes */
382262951Sjmmvstatic char *
383262951Sjmmvexpari(char *p)
38417987Speter{
385262951Sjmmv	char *q, *start;
386178631Sstefanf	arith_t result;
38738887Stegge	int begoff;
38838887Stegge	int quoted;
389262951Sjmmv	int adj;
3901556Srgrimes
391262951Sjmmv	quoted = *p++ == '"';
392262951Sjmmv	begoff = expdest - stackblock();
393264166Sjilles	p = argstr(p, 0);
394262951Sjmmv	removerecordregions(begoff);
395262951Sjmmv	STPUTC('\0', expdest);
396262951Sjmmv	start = stackblock() + begoff;
39738887Stegge
398207206Sjilles	q = grabstackstr(expdest);
399262951Sjmmv	result = arith(start);
400207206Sjilles	ungrabstackstr(q, expdest);
401262951Sjmmv
402262951Sjmmv	start = stackblock() + begoff;
403262951Sjmmv	adj = start - expdest;
404262951Sjmmv	STADJUST(adj, expdest);
405262951Sjmmv
406262951Sjmmv	CHECKSTRSPACE((int)(DIGITS(result) + 1), expdest);
407262951Sjmmv	fmtstr(expdest, DIGITS(result), ARITH_FORMAT_STR, result);
408262951Sjmmv	adj = strlen(expdest);
409262951Sjmmv	STADJUST(adj, expdest);
410262951Sjmmv	if (!quoted)
411262951Sjmmv		recordregion(begoff, expdest - stackblock(), 0);
412262951Sjmmv	return p;
4131556Srgrimes}
4141556Srgrimes
4151556Srgrimes
4161556Srgrimes/*
417212243Sjilles * Perform command substitution.
4181556Srgrimes */
419213811Sobrienstatic void
42090111Simpexpbackq(union node *cmd, int quoted, int flag)
42117987Speter{
4221556Srgrimes	struct backcmd in;
4231556Srgrimes	int i;
4241556Srgrimes	char buf[128];
4251556Srgrimes	char *p;
4261556Srgrimes	char *dest = expdest;
4271556Srgrimes	struct ifsregion saveifs, *savelastp;
4281556Srgrimes	struct nodelist *saveargbackq;
4291556Srgrimes	char lastc;
4301556Srgrimes	int startloc = dest - stackblock();
4311556Srgrimes	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
432276365Sjilles	int quotes = flag & (EXP_FULL | EXP_CASE);
433248980Sjilles	size_t nnl;
4341556Srgrimes
4351556Srgrimes	INTOFF;
4361556Srgrimes	saveifs = ifsfirst;
4371556Srgrimes	savelastp = ifslastp;
4381556Srgrimes	saveargbackq = argbackq;
4391556Srgrimes	p = grabstackstr(dest);
4401556Srgrimes	evalbackcmd(cmd, &in);
4411556Srgrimes	ungrabstackstr(p, dest);
4421556Srgrimes
4431556Srgrimes	p = in.buf;
4441556Srgrimes	lastc = '\0';
445115424Sfenner	nnl = 0;
446115424Sfenner	/* Don't copy trailing newlines */
4471556Srgrimes	for (;;) {
4481556Srgrimes		if (--in.nleft < 0) {
4491556Srgrimes			if (in.fd < 0)
4501556Srgrimes				break;
4511556Srgrimes			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
4521556Srgrimes			TRACE(("expbackq: read returns %d\n", i));
4531556Srgrimes			if (i <= 0)
4541556Srgrimes				break;
4551556Srgrimes			p = buf;
4561556Srgrimes			in.nleft = i - 1;
4571556Srgrimes		}
4581556Srgrimes		lastc = *p++;
4591556Srgrimes		if (lastc != '\0') {
460115424Sfenner			if (lastc == '\n') {
461115424Sfenner				nnl++;
462115424Sfenner			} else {
463216706Sjilles				CHECKSTRSPACE(nnl + 2, dest);
464115424Sfenner				while (nnl > 0) {
465115424Sfenner					nnl--;
466216706Sjilles					USTPUTC('\n', dest);
467115424Sfenner				}
468216496Sjilles				if (quotes && syntax[(int)lastc] == CCTL)
469216706Sjilles					USTPUTC(CTLESC, dest);
470216706Sjilles				USTPUTC(lastc, dest);
471115424Sfenner			}
4721556Srgrimes		}
4731556Srgrimes	}
47417987Speter
4751556Srgrimes	if (in.fd >= 0)
4761556Srgrimes		close(in.fd);
4771556Srgrimes	if (in.buf)
4781556Srgrimes		ckfree(in.buf);
479316942Sjilles	if (in.jp) {
480316942Sjilles		p = grabstackstr(dest);
48145916Scracauer		exitstatus = waitforjob(in.jp, (int *)NULL);
482316942Sjilles		ungrabstackstr(p, dest);
483316942Sjilles	}
484213775Sjhb	TRACE(("expbackq: size=%td: \"%.*s\"\n",
485213775Sjhb		((dest - stackblock()) - startloc),
486213775Sjhb		(int)((dest - stackblock()) - startloc),
4871556Srgrimes		stackblock() + startloc));
488316942Sjilles	ifsfirst = saveifs;
489316942Sjilles	ifslastp = savelastp;
490316942Sjilles	if (quoted == 0)
491316942Sjilles		recordregion(startloc, dest - stackblock(), 0);
492316942Sjilles	argbackq = saveargbackq;
4931556Srgrimes	expdest = dest;
4941556Srgrimes	INTON;
4951556Srgrimes}
4961556Srgrimes
4971556Srgrimes
4981556Srgrimes
499287751Sjillesstatic void
500287751Sjillesrecordleft(const char *str, const char *loc, char *startp)
501287751Sjilles{
502287751Sjilles	int amount;
503287751Sjilles
504287751Sjilles	amount = ((str - 1) - (loc - startp)) - expdest;
505287751Sjilles	STADJUST(amount, expdest);
506287751Sjilles	while (loc != str - 1)
507287751Sjilles		*startp++ = *loc++;
508287751Sjilles}
509287751Sjilles
510213811Sobrienstatic int
51190111Simpsubevalvar(char *p, char *str, int strloc, int subtype, int startloc,
512214524Sjilles  int varflags, int quotes)
51317987Speter{
51417987Speter	char *startp;
51517987Speter	char *loc = NULL;
51645514Stegge	char *q;
51717987Speter	int c = 0;
51817987Speter	struct nodelist *saveargbackq = argbackq;
51920425Ssteve	int amount;
52020425Ssteve
521206150Sjilles	argstr(p, (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX ||
522206147Sjilles	    subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX ?
523206150Sjilles	    EXP_CASE : 0) | EXP_TILDE);
52417987Speter	STACKSTRNUL(expdest);
52517987Speter	argbackq = saveargbackq;
52617987Speter	startp = stackblock() + startloc;
52720425Ssteve	if (str == NULL)
52820425Ssteve	    str = stackblock() + strloc;
52917987Speter
53017987Speter	switch (subtype) {
53117987Speter	case VSASSIGN:
53217987Speter		setvar(str, startp, 0);
53320425Ssteve		amount = startp - expdest;
53420425Ssteve		STADJUST(amount, expdest);
53517987Speter		varflags &= ~VSNUL;
53617987Speter		return 1;
53717987Speter
53817987Speter	case VSQUESTION:
53917987Speter		if (*p != CTLENDVAR) {
540201366Sjilles			outfmt(out2, "%s\n", startp);
54117987Speter			error((char *)NULL);
54217987Speter		}
543112254Sru		error("%.*s: parameter %snot set", (int)(p - str - 1),
54417987Speter		      str, (varflags & VSNUL) ? "null or "
54517987Speter					      : nullstr);
54617987Speter		return 0;
54717987Speter
54817987Speter	case VSTRIMLEFT:
54925233Ssteve		for (loc = startp; loc < str; loc++) {
55017987Speter			c = *loc;
55117987Speter			*loc = '\0';
552214524Sjilles			if (patmatch(str, startp, quotes)) {
55317987Speter				*loc = c;
554287751Sjilles				recordleft(str, loc, startp);
555287751Sjilles				return 1;
55617987Speter			}
55717987Speter			*loc = c;
558214524Sjilles			if (quotes && *loc == CTLESC)
55945514Stegge				loc++;
56017987Speter		}
56117987Speter		return 0;
56217987Speter
56317987Speter	case VSTRIMLEFTMAX:
56445514Stegge		for (loc = str - 1; loc >= startp;) {
56517987Speter			c = *loc;
56617987Speter			*loc = '\0';
567214524Sjilles			if (patmatch(str, startp, quotes)) {
56817987Speter				*loc = c;
569287751Sjilles				recordleft(str, loc, startp);
570287751Sjilles				return 1;
57117987Speter			}
57217987Speter			*loc = c;
57345514Stegge			loc--;
574214524Sjilles			if (quotes && loc > startp && *(loc - 1) == CTLESC) {
57545514Stegge				for (q = startp; q < loc; q++)
57645514Stegge					if (*q == CTLESC)
57745514Stegge						q++;
57845514Stegge				if (q > loc)
57945514Stegge					loc--;
58045514Stegge			}
58117987Speter		}
58217987Speter		return 0;
58317987Speter
58417987Speter	case VSTRIMRIGHT:
58545514Stegge		for (loc = str - 1; loc >= startp;) {
586214524Sjilles			if (patmatch(str, loc, quotes)) {
58720425Ssteve				amount = loc - expdest;
58820425Ssteve				STADJUST(amount, expdest);
58917987Speter				return 1;
59017987Speter			}
59145514Stegge			loc--;
592214524Sjilles			if (quotes && loc > startp && *(loc - 1) == CTLESC) {
59345514Stegge				for (q = startp; q < loc; q++)
59445514Stegge					if (*q == CTLESC)
59545514Stegge						q++;
59645514Stegge				if (q > loc)
59745514Stegge					loc--;
59845514Stegge			}
59917987Speter		}
60017987Speter		return 0;
60117987Speter
60217987Speter	case VSTRIMRIGHTMAX:
60317987Speter		for (loc = startp; loc < str - 1; loc++) {
604214524Sjilles			if (patmatch(str, loc, quotes)) {
60520425Ssteve				amount = loc - expdest;
60620425Ssteve				STADJUST(amount, expdest);
60717987Speter				return 1;
60817987Speter			}
609214524Sjilles			if (quotes && *loc == CTLESC)
61045514Stegge				loc++;
61117987Speter		}
61217987Speter		return 0;
61317987Speter
61417987Speter
61517987Speter	default:
61617987Speter		abort();
61717987Speter	}
61817987Speter}
61917987Speter
62017987Speter
6211556Srgrimes/*
6221556Srgrimes * Expand a variable, and return a pointer to the next character in the
6231556Srgrimes * input string.
6241556Srgrimes */
6251556Srgrimes
626213811Sobrienstatic char *
62790111Simpevalvar(char *p, int flag)
62817987Speter{
6291556Srgrimes	int subtype;
6301556Srgrimes	int varflags;
6311556Srgrimes	char *var;
632264168Sjilles	const char *val;
63345644Stegge	int patloc;
6341556Srgrimes	int c;
6351556Srgrimes	int set;
6361556Srgrimes	int special;
6371556Srgrimes	int startloc;
63817987Speter	int varlen;
639221602Sjilles	int varlenb;
64017987Speter	int easy;
641276365Sjilles	int quotes = flag & (EXP_FULL | EXP_CASE);
642287751Sjilles	int record = 0;
6431556Srgrimes
644164081Sstefanf	varflags = (unsigned char)*p++;
6451556Srgrimes	subtype = varflags & VSTYPE;
6461556Srgrimes	var = p;
6471556Srgrimes	special = 0;
6481556Srgrimes	if (! is_name(*p))
6491556Srgrimes		special = 1;
6501556Srgrimes	p = strchr(p, '=') + 1;
6511556Srgrimesagain: /* jump here after setting a variable with ${var=text} */
652179022Sstefanf	if (varflags & VSLINENO) {
653179022Sstefanf		set = 1;
654262951Sjmmv		special = 1;
655262951Sjmmv		val = NULL;
656179022Sstefanf	} else if (special) {
65725233Ssteve		set = varisset(var, varflags & VSNUL);
6581556Srgrimes		val = NULL;
6591556Srgrimes	} else {
66060592Scracauer		val = bltinlookup(var, 1);
66117987Speter		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
6621556Srgrimes			val = NULL;
6631556Srgrimes			set = 0;
6641556Srgrimes		} else
6651556Srgrimes			set = 1;
6661556Srgrimes	}
66717987Speter	varlen = 0;
6681556Srgrimes	startloc = expdest - stackblock();
669198454Sjilles	if (!set && uflag && *var != '@' && *var != '*') {
67096939Stjr		switch (subtype) {
67196939Stjr		case VSNORMAL:
67296939Stjr		case VSTRIMLEFT:
67396939Stjr		case VSTRIMLEFTMAX:
67496939Stjr		case VSTRIMRIGHT:
67596939Stjr		case VSTRIMRIGHTMAX:
67696939Stjr		case VSLENGTH:
677112254Sru			error("%.*s: parameter not set", (int)(p - var - 1),
678112254Sru			    var);
67996939Stjr		}
68096939Stjr	}
6811556Srgrimes	if (set && subtype != VSPLUS) {
6821556Srgrimes		/* insert the value of the variable */
6831556Srgrimes		if (special) {
684262951Sjmmv			if (varflags & VSLINENO)
685262951Sjmmv				STPUTBIN(var, p - var - 1, expdest);
686262951Sjmmv			else
687262951Sjmmv				varvalue(var, varflags & VSQUOTE, subtype, flag);
68817987Speter			if (subtype == VSLENGTH) {
689221602Sjilles				varlenb = expdest - stackblock() - startloc;
690221602Sjilles				varlen = varlenb;
691221602Sjilles				if (localeisutf8) {
692221602Sjilles					val = stackblock() + startloc;
693221602Sjilles					for (;val != expdest; val++)
694221602Sjilles						if ((*val & 0xC0) == 0x80)
695221602Sjilles							varlen--;
696221602Sjilles				}
697221602Sjilles				STADJUST(-varlenb, expdest);
69817987Speter			}
6991556Srgrimes		} else {
70017987Speter			if (subtype == VSLENGTH) {
70117987Speter				for (;*val; val++)
702221602Sjilles					if (!localeisutf8 ||
703221602Sjilles					    (*val & 0xC0) != 0x80)
704221602Sjilles						varlen++;
7051556Srgrimes			}
706287751Sjilles			else
707287751Sjilles				strtodest(val, flag, subtype,
708287751Sjilles				    varflags & VSQUOTE);
7091556Srgrimes		}
7101556Srgrimes	}
71120425Ssteve
7121556Srgrimes	if (subtype == VSPLUS)
7131556Srgrimes		set = ! set;
71417987Speter
71520425Ssteve	easy = ((varflags & VSQUOTE) == 0 ||
71617987Speter		(*var == '@' && shellparam.nparam != 1));
71717987Speter
71817987Speter
71917987Speter	switch (subtype) {
72017987Speter	case VSLENGTH:
72117987Speter		expdest = cvtnum(varlen, expdest);
722287751Sjilles		record = 1;
723287751Sjilles		break;
72417987Speter
72517987Speter	case VSNORMAL:
726287751Sjilles		record = easy;
72717987Speter		break;
72817987Speter
72917987Speter	case VSPLUS:
73017987Speter	case VSMINUS:
73117987Speter		if (!set) {
732214512Sjilles			argstr(p, flag | (flag & EXP_FULL ? EXP_SPLIT_LIT : 0) |
733214512Sjilles			    (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0));
73417987Speter			break;
73517987Speter		}
736287751Sjilles		record = easy;
73717987Speter		break;
73817987Speter
73917987Speter	case VSTRIMLEFT:
74017987Speter	case VSTRIMLEFTMAX:
74117987Speter	case VSTRIMRIGHT:
74217987Speter	case VSTRIMRIGHTMAX:
743320510Sjilles		if (!set) {
744320510Sjilles			set = 1;
74517987Speter			break;
746320510Sjilles		}
74717987Speter		/*
74817987Speter		 * Terminate the string and start recording the pattern
74917987Speter		 * right after it
75017987Speter		 */
75117987Speter		STPUTC('\0', expdest);
75245644Stegge		patloc = expdest - stackblock();
75345644Stegge		if (subevalvar(p, NULL, patloc, subtype,
754214524Sjilles		    startloc, varflags, quotes) == 0) {
75545644Stegge			int amount = (expdest - stackblock() - patloc) + 1;
75625233Ssteve			STADJUST(-amount, expdest);
75725233Ssteve		}
75838887Stegge		/* Remove any recorded regions beyond start of variable */
75938887Stegge		removerecordregions(startloc);
760287751Sjilles		record = 1;
761287751Sjilles		break;
76217987Speter
76317987Speter	case VSASSIGN:
76417987Speter	case VSQUESTION:
76517987Speter		if (!set) {
766214524Sjilles			if (subevalvar(p, var, 0, subtype, startloc, varflags,
767214524Sjilles			    quotes)) {
76820425Ssteve				varflags &= ~VSNUL;
769155301Sschweikh				/*
770155301Sschweikh				 * Remove any recorded regions beyond
771155301Sschweikh				 * start of variable
77238887Stegge				 */
77338887Stegge				removerecordregions(startloc);
7741556Srgrimes				goto again;
77520425Ssteve			}
77617987Speter			break;
7771556Srgrimes		}
778287751Sjilles		record = easy;
77917987Speter		break;
78017987Speter
781164003Sstefanf	case VSERROR:
782164003Sstefanf		c = p - var - 1;
783164003Sstefanf		error("${%.*s%s}: Bad substitution", c, var,
784164003Sstefanf		    (c > 0 && *p != CTLENDVAR) ? "..." : "");
785164003Sstefanf
78617987Speter	default:
78717987Speter		abort();
7881556Srgrimes	}
78917987Speter
790287751Sjilles	if (record)
791287751Sjilles		recordregion(startloc, expdest - stackblock(),
792287751Sjilles		    varflags & VSQUOTE || (ifsset() && ifsval()[0] == '\0' &&
793287751Sjilles		    (*var == '@' || *var == '*')));
794287751Sjilles
7951556Srgrimes	if (subtype != VSNORMAL) {	/* skip to end of alternative */
7961556Srgrimes		int nesting = 1;
7971556Srgrimes		for (;;) {
7981556Srgrimes			if ((c = *p++) == CTLESC)
7991556Srgrimes				p++;
8001556Srgrimes			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
8011556Srgrimes				if (set)
8021556Srgrimes					argbackq = argbackq->next;
8031556Srgrimes			} else if (c == CTLVAR) {
8041556Srgrimes				if ((*p++ & VSTYPE) != VSNORMAL)
8051556Srgrimes					nesting++;
8061556Srgrimes			} else if (c == CTLENDVAR) {
8071556Srgrimes				if (--nesting == 0)
8081556Srgrimes					break;
8091556Srgrimes			}
8101556Srgrimes		}
8111556Srgrimes	}
8121556Srgrimes	return p;
8131556Srgrimes}
8141556Srgrimes
8151556Srgrimes
8161556Srgrimes
8171556Srgrimes/*
8181556Srgrimes * Test whether a specialized variable is set.
8191556Srgrimes */
8201556Srgrimes
821213811Sobrienstatic int
822264168Sjillesvarisset(const char *name, int nulok)
82325233Ssteve{
8241556Srgrimes
82525233Ssteve	if (*name == '!')
826209600Sjilles		return backgndpidset();
82725233Ssteve	else if (*name == '@' || *name == '*') {
8281556Srgrimes		if (*shellparam.p == NULL)
8291556Srgrimes			return 0;
83025233Ssteve
83125233Ssteve		if (nulok) {
83225233Ssteve			char **av;
83325233Ssteve
83425233Ssteve			for (av = shellparam.p; *av; av++)
83525233Ssteve				if (**av != '\0')
83625233Ssteve					return 1;
83725233Ssteve			return 0;
83825233Ssteve		}
83918202Speter	} else if (is_digit(*name)) {
84025233Ssteve		char *ap;
841275777Sjilles		long num;
84225233Ssteve
843275777Sjilles		errno = 0;
844275777Sjilles		num = strtol(name, NULL, 10);
845275777Sjilles		if (errno != 0 || num > shellparam.nparam)
84625233Ssteve			return 0;
84725233Ssteve
84825233Ssteve		if (num == 0)
84925233Ssteve			ap = arg0;
85025233Ssteve		else
85125233Ssteve			ap = shellparam.p[num - 1];
85225233Ssteve
85325233Ssteve		if (nulok && (ap == NULL || *ap == '\0'))
85425233Ssteve			return 0;
8551556Srgrimes	}
8561556Srgrimes	return 1;
8571556Srgrimes}
8581556Srgrimes
859216384Sjillesstatic void
860216384Sjillesstrtodest(const char *p, int flag, int subtype, int quoted)
861216384Sjilles{
862276365Sjilles	if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH)
863216384Sjilles		STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest);
864216384Sjilles	else
865216384Sjilles		STPUTS(p, expdest);
866216384Sjilles}
8671556Srgrimes
8681556Srgrimes/*
8691556Srgrimes * Add the value of a specialized variable to the stack string.
8701556Srgrimes */
8711556Srgrimes
872213811Sobrienstatic void
873264168Sjillesvarvalue(const char *name, int quoted, int subtype, int flag)
87417987Speter{
8751556Srgrimes	int num;
8761556Srgrimes	char *p;
8771556Srgrimes	int i;
8781556Srgrimes	char sep;
8791556Srgrimes	char **ap;
8801556Srgrimes
88118202Speter	switch (*name) {
8821556Srgrimes	case '$':
8831556Srgrimes		num = rootpid;
8841556Srgrimes		goto numvar;
8851556Srgrimes	case '?':
88617987Speter		num = oexitstatus;
8871556Srgrimes		goto numvar;
8881556Srgrimes	case '#':
8891556Srgrimes		num = shellparam.nparam;
8901556Srgrimes		goto numvar;
8911556Srgrimes	case '!':
892209600Sjilles		num = backgndpidval();
8931556Srgrimesnumvar:
89417987Speter		expdest = cvtnum(num, expdest);
8951556Srgrimes		break;
8961556Srgrimes	case '-':
8971556Srgrimes		for (i = 0 ; i < NOPTS ; i++) {
8981556Srgrimes			if (optlist[i].val)
8991556Srgrimes				STPUTC(optlist[i].letter, expdest);
9001556Srgrimes		}
9011556Srgrimes		break;
9021556Srgrimes	case '@':
903164081Sstefanf		if (flag & EXP_FULL && quoted) {
90438887Stegge			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
905216384Sjilles				strtodest(p, flag, subtype, quoted);
90638887Stegge				if (*ap)
90738887Stegge					STPUTC('\0', expdest);
90838887Stegge			}
90938887Stegge			break;
9101556Srgrimes		}
911102410Scharnier		/* FALLTHROUGH */
9121556Srgrimes	case '*':
913149825Srse		if (ifsset())
91438887Stegge			sep = ifsval()[0];
91538887Stegge		else
91638887Stegge			sep = ' ';
9171556Srgrimes		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
918216384Sjilles			strtodest(p, flag, subtype, quoted);
919222361Sjilles			if (!*ap)
920222361Sjilles				break;
921222361Sjilles			if (sep || (flag & EXP_FULL && !quoted && **ap != '\0'))
9221556Srgrimes				STPUTC(sep, expdest);
9231556Srgrimes		}
9241556Srgrimes		break;
9251556Srgrimes	case '0':
9261556Srgrimes		p = arg0;
927216384Sjilles		strtodest(p, flag, subtype, quoted);
9281556Srgrimes		break;
9291556Srgrimes	default:
93018202Speter		if (is_digit(*name)) {
93118202Speter			num = atoi(name);
93218202Speter			if (num > 0 && num <= shellparam.nparam) {
93318202Speter				p = shellparam.p[num - 1];
934216384Sjilles				strtodest(p, flag, subtype, quoted);
93518202Speter			}
9361556Srgrimes		}
9371556Srgrimes		break;
9381556Srgrimes	}
9391556Srgrimes}
9401556Srgrimes
9411556Srgrimes
9421556Srgrimes
9431556Srgrimes/*
944218909Sbrucec * Record the fact that we have to scan this region of the
9451556Srgrimes * string for IFS characters.
9461556Srgrimes */
9471556Srgrimes
948213811Sobrienstatic void
949194975Sjillesrecordregion(int start, int end, int inquotes)
95017987Speter{
95125233Ssteve	struct ifsregion *ifsp;
9521556Srgrimes
953264478Sjilles	INTOFF;
9541556Srgrimes	if (ifslastp == NULL) {
9551556Srgrimes		ifsp = &ifsfirst;
9561556Srgrimes	} else {
957194975Sjilles		if (ifslastp->endoff == start
958194975Sjilles		    && ifslastp->inquotes == inquotes) {
959194975Sjilles			/* extend previous area */
960194975Sjilles			ifslastp->endoff = end;
961264478Sjilles			INTON;
962194975Sjilles			return;
963194975Sjilles		}
9641556Srgrimes		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
9651556Srgrimes		ifslastp->next = ifsp;
9661556Srgrimes	}
9671556Srgrimes	ifslastp = ifsp;
9681556Srgrimes	ifslastp->next = NULL;
9691556Srgrimes	ifslastp->begoff = start;
9701556Srgrimes	ifslastp->endoff = end;
971194975Sjilles	ifslastp->inquotes = inquotes;
972264478Sjilles	INTON;
9731556Srgrimes}
9741556Srgrimes
9751556Srgrimes
9761556Srgrimes
9771556Srgrimes/*
9781556Srgrimes * Break the argument string into pieces based upon IFS and add the
9791556Srgrimes * strings to the argument list.  The regions of the string to be
9801556Srgrimes * searched for IFS characters have been stored by recordregion.
981212243Sjilles * CTLESC characters are preserved but have little effect in this pass
982212243Sjilles * other than escaping CTL* characters.  In particular, they do not escape
983212243Sjilles * IFS characters: that should be done with the ifsregion mechanism.
984212243Sjilles * CTLQUOTEMARK characters are used to preserve empty quoted strings.
985212243Sjilles * This pass treats them as a regular character, making the string non-empty.
986212243Sjilles * Later, they are removed along with the other CTL* characters.
9871556Srgrimes */
988213811Sobrienstatic void
98990111Simpifsbreakup(char *string, struct arglist *arglist)
99090111Simp{
9911556Srgrimes	struct ifsregion *ifsp;
9921556Srgrimes	struct strlist *sp;
9931556Srgrimes	char *start;
99425233Ssteve	char *p;
9951556Srgrimes	char *q;
996201053Sjilles	const char *ifs;
997194975Sjilles	const char *ifsspc;
998194975Sjilles	int had_param_ch = 0;
9991556Srgrimes
1000194975Sjilles	start = string;
100117987Speter
1002194975Sjilles	if (ifslastp == NULL) {
1003194975Sjilles		/* Return entire argument, IFS doesn't apply to any of it */
1004194975Sjilles		sp = (struct strlist *)stalloc(sizeof *sp);
1005194975Sjilles		sp->text = start;
1006194975Sjilles		*arglist->lastp = sp;
1007194975Sjilles		arglist->lastp = &sp->next;
1008194975Sjilles		return;
1009194975Sjilles	}
1010194975Sjilles
1011194975Sjilles	ifs = ifsset() ? ifsval() : " \t\n";
1012194975Sjilles
1013194975Sjilles	for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
1014194975Sjilles		p = string + ifsp->begoff;
1015194975Sjilles		while (p < string + ifsp->endoff) {
1016194975Sjilles			q = p;
1017194975Sjilles			if (*p == CTLESC)
1018194975Sjilles				p++;
1019194975Sjilles			if (ifsp->inquotes) {
1020194975Sjilles				/* Only NULs (should be from "$@") end args */
1021194977Sjilles				had_param_ch = 1;
1022194975Sjilles				if (*p != 0) {
10231556Srgrimes					p++;
1024194975Sjilles					continue;
1025194975Sjilles				}
1026194975Sjilles				ifsspc = NULL;
1027194975Sjilles			} else {
1028194975Sjilles				if (!strchr(ifs, *p)) {
1029194977Sjilles					had_param_ch = 1;
103038887Stegge					p++;
1031194975Sjilles					continue;
1032194975Sjilles				}
1033194975Sjilles				ifsspc = strchr(" \t\n", *p);
1034194975Sjilles
1035194975Sjilles				/* Ignore IFS whitespace at start */
1036194975Sjilles				if (q == start && ifsspc != NULL) {
1037194975Sjilles					p++;
10381556Srgrimes					start = p;
1039194975Sjilles					continue;
1040194975Sjilles				}
1041194977Sjilles				had_param_ch = 0;
10421556Srgrimes			}
1043194975Sjilles
1044194975Sjilles			/* Save this argument... */
1045194975Sjilles			*q = '\0';
10461556Srgrimes			sp = (struct strlist *)stalloc(sizeof *sp);
10471556Srgrimes			sp->text = start;
10481556Srgrimes			*arglist->lastp = sp;
10491556Srgrimes			arglist->lastp = &sp->next;
1050194975Sjilles			p++;
1051194975Sjilles
1052194975Sjilles			if (ifsspc != NULL) {
1053194975Sjilles				/* Ignore further trailing IFS whitespace */
1054194975Sjilles				for (; p < string + ifsp->endoff; p++) {
1055194975Sjilles					q = p;
1056194975Sjilles					if (*p == CTLESC)
1057194975Sjilles						p++;
1058194975Sjilles					if (strchr(ifs, *p) == NULL) {
1059194975Sjilles						p = q;
1060194975Sjilles						break;
1061194975Sjilles					}
1062194975Sjilles					if (strchr(" \t\n", *p) == NULL) {
1063194975Sjilles						p++;
1064194975Sjilles						break;
1065194975Sjilles					}
1066194975Sjilles				}
1067194975Sjilles			}
1068194975Sjilles			start = p;
10691556Srgrimes		}
1070194975Sjilles	}
1071194975Sjilles
1072194975Sjilles	/*
1073194975Sjilles	 * Save anything left as an argument.
1074194975Sjilles	 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
1075194975Sjilles	 * generating 2 arguments, the second of which is empty.
1076194975Sjilles	 * Some recent clarification of the Posix spec say that it
1077194975Sjilles	 * should only generate one....
1078194975Sjilles	 */
1079194975Sjilles	if (had_param_ch || *start != 0) {
10801556Srgrimes		sp = (struct strlist *)stalloc(sizeof *sp);
10811556Srgrimes		sp->text = start;
10821556Srgrimes		*arglist->lastp = sp;
10831556Srgrimes		arglist->lastp = &sp->next;
10841556Srgrimes	}
10851556Srgrimes}
10861556Srgrimes
10871556Srgrimes
1088213760Sobrienstatic char expdir[PATH_MAX];
1089212243Sjilles#define expdir_end (expdir + sizeof(expdir))
10901556Srgrimes
10911556Srgrimes/*
1092212243Sjilles * Perform pathname generation and remove control characters.
1093212243Sjilles * At this point, the only control characters should be CTLESC and CTLQUOTEMARK.
1094212243Sjilles * The results are stored in the list exparg.
10951556Srgrimes */
1096213811Sobrienstatic void
1097287751Sjillesexpandmeta(struct strlist *str)
109817987Speter{
10991556Srgrimes	char *p;
11001556Srgrimes	struct strlist **savelastp;
11011556Srgrimes	struct strlist *sp;
11021556Srgrimes	char c;
11031556Srgrimes
11041556Srgrimes	while (str) {
11051556Srgrimes		if (fflag)
11061556Srgrimes			goto nometa;
11071556Srgrimes		p = str->text;
11081556Srgrimes		for (;;) {			/* fast check for meta chars */
11091556Srgrimes			if ((c = *p++) == '\0')
11101556Srgrimes				goto nometa;
1111211646Sjilles			if (c == '*' || c == '?' || c == '[')
11121556Srgrimes				break;
11131556Srgrimes		}
11141556Srgrimes		savelastp = exparg.lastp;
11151556Srgrimes		INTOFF;
11161556Srgrimes		expmeta(expdir, str->text);
11171556Srgrimes		INTON;
11181556Srgrimes		if (exparg.lastp == savelastp) {
11198855Srgrimes			/*
11208855Srgrimes			 * no matches
11211556Srgrimes			 */
11221556Srgrimesnometa:
11231556Srgrimes			*exparg.lastp = str;
11241556Srgrimes			rmescapes(str->text);
11251556Srgrimes			exparg.lastp = &str->next;
11261556Srgrimes		} else {
11271556Srgrimes			*exparg.lastp = NULL;
11281556Srgrimes			*savelastp = sp = expsort(*savelastp);
11291556Srgrimes			while (sp->next != NULL)
11301556Srgrimes				sp = sp->next;
11311556Srgrimes			exparg.lastp = &sp->next;
11321556Srgrimes		}
11331556Srgrimes		str = str->next;
11341556Srgrimes	}
11351556Srgrimes}
11361556Srgrimes
11371556Srgrimes
11381556Srgrimes/*
11391556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion.
11401556Srgrimes */
11411556Srgrimes
1142213811Sobrienstatic void
114390111Simpexpmeta(char *enddir, char *name)
114490111Simp{
1145248980Sjilles	const char *p;
1146248980Sjilles	const char *q;
1147248980Sjilles	const char *start;
11481556Srgrimes	char *endname;
11491556Srgrimes	int metaflag;
11501556Srgrimes	struct stat statb;
11511556Srgrimes	DIR *dirp;
11521556Srgrimes	struct dirent *dp;
11531556Srgrimes	int atend;
11541556Srgrimes	int matchdot;
1155207944Sjilles	int esc;
1156228941Sjilles	int namlen;
11571556Srgrimes
11581556Srgrimes	metaflag = 0;
11591556Srgrimes	start = name;
1160207944Sjilles	for (p = name; esc = 0, *p; p += esc + 1) {
11611556Srgrimes		if (*p == '*' || *p == '?')
11621556Srgrimes			metaflag = 1;
11631556Srgrimes		else if (*p == '[') {
11641556Srgrimes			q = p + 1;
116526488Sache			if (*q == '!' || *q == '^')
11661556Srgrimes				q++;
11671556Srgrimes			for (;;) {
116838887Stegge				while (*q == CTLQUOTEMARK)
116938887Stegge					q++;
11701556Srgrimes				if (*q == CTLESC)
11711556Srgrimes					q++;
11721556Srgrimes				if (*q == '/' || *q == '\0')
11731556Srgrimes					break;
11741556Srgrimes				if (*++q == ']') {
11751556Srgrimes					metaflag = 1;
11761556Srgrimes					break;
11771556Srgrimes				}
11781556Srgrimes			}
11791556Srgrimes		} else if (*p == '\0')
11801556Srgrimes			break;
118138887Stegge		else if (*p == CTLQUOTEMARK)
118238887Stegge			continue;
1183207944Sjilles		else {
1184207944Sjilles			if (*p == CTLESC)
1185207944Sjilles				esc++;
1186207944Sjilles			if (p[esc] == '/') {
1187207944Sjilles				if (metaflag)
1188207944Sjilles					break;
1189207944Sjilles				start = p + esc + 1;
1190207944Sjilles			}
11911556Srgrimes		}
11921556Srgrimes	}
11931556Srgrimes	if (metaflag == 0) {	/* we've reached the end of the file name */
11941556Srgrimes		if (enddir != expdir)
11951556Srgrimes			metaflag++;
11961556Srgrimes		for (p = name ; ; p++) {
119738887Stegge			if (*p == CTLQUOTEMARK)
119838887Stegge				continue;
11991556Srgrimes			if (*p == CTLESC)
12001556Srgrimes				p++;
12011556Srgrimes			*enddir++ = *p;
12021556Srgrimes			if (*p == '\0')
12031556Srgrimes				break;
1204211155Sjilles			if (enddir == expdir_end)
1205211155Sjilles				return;
12061556Srgrimes		}
1207147812Sdelphij		if (metaflag == 0 || lstat(expdir, &statb) >= 0)
12081556Srgrimes			addfname(expdir);
12091556Srgrimes		return;
12101556Srgrimes	}
1211248980Sjilles	endname = name + (p - name);
12121556Srgrimes	if (start != name) {
12131556Srgrimes		p = name;
12141556Srgrimes		while (p < start) {
121538887Stegge			while (*p == CTLQUOTEMARK)
121638887Stegge				p++;
12171556Srgrimes			if (*p == CTLESC)
12181556Srgrimes				p++;
12191556Srgrimes			*enddir++ = *p++;
1220211155Sjilles			if (enddir == expdir_end)
1221211155Sjilles				return;
12221556Srgrimes		}
12231556Srgrimes	}
12241556Srgrimes	if (enddir == expdir) {
12251556Srgrimes		p = ".";
12261556Srgrimes	} else if (enddir == expdir + 1 && *expdir == '/') {
12271556Srgrimes		p = "/";
12281556Srgrimes	} else {
12291556Srgrimes		p = expdir;
12301556Srgrimes		enddir[-1] = '\0';
12311556Srgrimes	}
12321556Srgrimes	if ((dirp = opendir(p)) == NULL)
12331556Srgrimes		return;
12341556Srgrimes	if (enddir != expdir)
12351556Srgrimes		enddir[-1] = '/';
12361556Srgrimes	if (*endname == 0) {
12371556Srgrimes		atend = 1;
12381556Srgrimes	} else {
12391556Srgrimes		atend = 0;
1240207944Sjilles		*endname = '\0';
1241207944Sjilles		endname += esc + 1;
12421556Srgrimes	}
12431556Srgrimes	matchdot = 0;
124438887Stegge	p = start;
124538887Stegge	while (*p == CTLQUOTEMARK)
124638887Stegge		p++;
124738887Stegge	if (*p == CTLESC)
124838887Stegge		p++;
124938887Stegge	if (*p == '.')
12501556Srgrimes		matchdot++;
12511556Srgrimes	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
12521556Srgrimes		if (dp->d_name[0] == '.' && ! matchdot)
12531556Srgrimes			continue;
125445514Stegge		if (patmatch(start, dp->d_name, 0)) {
1255228941Sjilles			namlen = dp->d_namlen;
1256228941Sjilles			if (enddir + namlen + 1 > expdir_end)
1257211155Sjilles				continue;
1258228941Sjilles			memcpy(enddir, dp->d_name, namlen + 1);
1259211155Sjilles			if (atend)
12601556Srgrimes				addfname(expdir);
1261211155Sjilles			else {
1262228942Sjilles				if (dp->d_type != DT_UNKNOWN &&
1263228942Sjilles				    dp->d_type != DT_DIR &&
1264228942Sjilles				    dp->d_type != DT_LNK)
1265228942Sjilles					continue;
1266228941Sjilles				if (enddir + namlen + 2 > expdir_end)
126717987Speter					continue;
1268228941Sjilles				enddir[namlen] = '/';
1269228941Sjilles				enddir[namlen + 1] = '\0';
1270228941Sjilles				expmeta(enddir + namlen + 1, endname);
12711556Srgrimes			}
12721556Srgrimes		}
12731556Srgrimes	}
12741556Srgrimes	closedir(dirp);
12751556Srgrimes	if (! atend)
1276207944Sjilles		endname[-esc - 1] = esc ? CTLESC : '/';
12771556Srgrimes}
12781556Srgrimes
12791556Srgrimes
12801556Srgrimes/*
12811556Srgrimes * Add a file name to the list.
12821556Srgrimes */
12831556Srgrimes
1284213811Sobrienstatic void
128590111Simpaddfname(char *name)
128690111Simp{
12871556Srgrimes	char *p;
12881556Srgrimes	struct strlist *sp;
12891556Srgrimes
1290297749Sjilles	p = stsavestr(name);
12911556Srgrimes	sp = (struct strlist *)stalloc(sizeof *sp);
12921556Srgrimes	sp->text = p;
12931556Srgrimes	*exparg.lastp = sp;
12941556Srgrimes	exparg.lastp = &sp->next;
12951556Srgrimes}
12961556Srgrimes
12971556Srgrimes
12981556Srgrimes/*
12991556Srgrimes * Sort the results of file name expansion.  It calculates the number of
13001556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the
13011556Srgrimes * work.
13021556Srgrimes */
13031556Srgrimes
1304213811Sobrienstatic struct strlist *
130590111Simpexpsort(struct strlist *str)
130690111Simp{
13071556Srgrimes	int len;
13081556Srgrimes	struct strlist *sp;
13091556Srgrimes
13101556Srgrimes	len = 0;
13111556Srgrimes	for (sp = str ; sp ; sp = sp->next)
13121556Srgrimes		len++;
13131556Srgrimes	return msort(str, len);
13141556Srgrimes}
13151556Srgrimes
13161556Srgrimes
1317213811Sobrienstatic struct strlist *
131890111Simpmsort(struct strlist *list, int len)
131917987Speter{
132017987Speter	struct strlist *p, *q = NULL;
13211556Srgrimes	struct strlist **lpp;
13221556Srgrimes	int half;
13231556Srgrimes	int n;
13241556Srgrimes
13251556Srgrimes	if (len <= 1)
13261556Srgrimes		return list;
13278855Srgrimes	half = len >> 1;
13281556Srgrimes	p = list;
13291556Srgrimes	for (n = half ; --n >= 0 ; ) {
13301556Srgrimes		q = p;
13311556Srgrimes		p = p->next;
13321556Srgrimes	}
13331556Srgrimes	q->next = NULL;			/* terminate first half of list */
13341556Srgrimes	q = msort(list, half);		/* sort first half of list */
13351556Srgrimes	p = msort(p, len - half);		/* sort second half */
13361556Srgrimes	lpp = &list;
13371556Srgrimes	for (;;) {
13381556Srgrimes		if (strcmp(p->text, q->text) < 0) {
13391556Srgrimes			*lpp = p;
13401556Srgrimes			lpp = &p->next;
13411556Srgrimes			if ((p = *lpp) == NULL) {
13421556Srgrimes				*lpp = q;
13431556Srgrimes				break;
13441556Srgrimes			}
13451556Srgrimes		} else {
13461556Srgrimes			*lpp = q;
13471556Srgrimes			lpp = &q->next;
13481556Srgrimes			if ((q = *lpp) == NULL) {
13491556Srgrimes				*lpp = p;
13501556Srgrimes				break;
13511556Srgrimes			}
13521556Srgrimes		}
13531556Srgrimes	}
13541556Srgrimes	return list;
13551556Srgrimes}
13561556Srgrimes
13571556Srgrimes
13581556Srgrimes
1359221646Sjillesstatic wchar_t
1360221646Sjillesget_wc(const char **p)
1361221646Sjilles{
1362221646Sjilles	wchar_t c;
1363221646Sjilles	int chrlen;
1364221646Sjilles
1365221646Sjilles	chrlen = mbtowc(&c, *p, 4);
1366221646Sjilles	if (chrlen == 0)
1367221646Sjilles		return 0;
1368221646Sjilles	else if (chrlen == -1)
1369221646Sjilles		c = 0;
1370221646Sjilles	else
1371221646Sjilles		*p += chrlen;
1372221646Sjilles	return c;
1373221646Sjilles}
1374221646Sjilles
1375221646Sjilles
13761556Srgrimes/*
1377223120Sjilles * See if a character matches a character class, starting at the first colon
1378223120Sjilles * of "[:class:]".
1379223120Sjilles * If a valid character class is recognized, a pointer to the next character
1380223120Sjilles * after the final closing bracket is stored into *end, otherwise a null
1381223120Sjilles * pointer is stored into *end.
1382223120Sjilles */
1383223120Sjillesstatic int
1384223120Sjillesmatch_charclass(const char *p, wchar_t chr, const char **end)
1385223120Sjilles{
1386223120Sjilles	char name[20];
1387223120Sjilles	const char *nameend;
1388223120Sjilles	wctype_t cclass;
1389223120Sjilles
1390223120Sjilles	*end = NULL;
1391223120Sjilles	p++;
1392223120Sjilles	nameend = strstr(p, ":]");
1393248980Sjilles	if (nameend == NULL || (size_t)(nameend - p) >= sizeof(name) ||
1394248980Sjilles	    nameend == p)
1395223120Sjilles		return 0;
1396223120Sjilles	memcpy(name, p, nameend - p);
1397223120Sjilles	name[nameend - p] = '\0';
1398223120Sjilles	*end = nameend + 2;
1399223120Sjilles	cclass = wctype(name);
1400223120Sjilles	/* An unknown class matches nothing but is valid nevertheless. */
1401223120Sjilles	if (cclass == 0)
1402223120Sjilles		return 0;
1403223120Sjilles	return iswctype(chr, cclass);
1404223120Sjilles}
1405223120Sjilles
1406223120Sjilles
1407223120Sjilles/*
14081556Srgrimes * Returns true if the pattern matches the string.
14091556Srgrimes */
14101556Srgrimes
1411229220Sjillesstatic int
1412200956Sjillespatmatch(const char *pattern, const char *string, int squoted)
141390111Simp{
1414223120Sjilles	const char *p, *q, *end;
1415229201Sjilles	const char *bt_p, *bt_q;
141625233Ssteve	char c;
1417221646Sjilles	wchar_t wc, wc2;
14181556Srgrimes
14191556Srgrimes	p = pattern;
14201556Srgrimes	q = string;
1421229201Sjilles	bt_p = NULL;
1422229201Sjilles	bt_q = NULL;
14231556Srgrimes	for (;;) {
14241556Srgrimes		switch (c = *p++) {
14251556Srgrimes		case '\0':
1426229201Sjilles			if (*q != '\0')
1427229201Sjilles				goto backtrack;
1428229201Sjilles			return 1;
14291556Srgrimes		case CTLESC:
143045514Stegge			if (squoted && *q == CTLESC)
143145514Stegge				q++;
14321556Srgrimes			if (*q++ != *p++)
1433229201Sjilles				goto backtrack;
14341556Srgrimes			break;
143538887Stegge		case CTLQUOTEMARK:
143638887Stegge			continue;
14371556Srgrimes		case '?':
143845514Stegge			if (squoted && *q == CTLESC)
143945514Stegge				q++;
1440229201Sjilles			if (*q == '\0')
1441229201Sjilles				return 0;
1442229201Sjilles			if (localeisutf8) {
1443221646Sjilles				wc = get_wc(&q);
1444229201Sjilles				/*
1445229201Sjilles				 * A '?' does not match invalid UTF-8 but a
1446229201Sjilles				 * '*' does, so backtrack.
1447229201Sjilles				 */
1448229201Sjilles				if (wc == 0)
1449229201Sjilles					goto backtrack;
1450229201Sjilles			} else
1451223010Sjilles				wc = (unsigned char)*q++;
14521556Srgrimes			break;
14531556Srgrimes		case '*':
14541556Srgrimes			c = *p;
145538887Stegge			while (c == CTLQUOTEMARK || c == '*')
145638887Stegge				c = *++p;
1457229201Sjilles			/*
1458229201Sjilles			 * If the pattern ends here, we know the string
1459229201Sjilles			 * matches without needing to look at the rest of it.
1460229201Sjilles			 */
1461229201Sjilles			if (c == '\0')
1462229201Sjilles				return 1;
1463229201Sjilles			/*
1464229201Sjilles			 * First try the shortest match for the '*' that
1465229201Sjilles			 * could work. We can forget any earlier '*' since
1466229201Sjilles			 * there is no way having it match more characters
1467229201Sjilles			 * can help us, given that we are already here.
1468229201Sjilles			 */
1469229201Sjilles			bt_p = p;
1470229201Sjilles			bt_q = q;
1471229201Sjilles			break;
14721556Srgrimes		case '[': {
1473287752Sjilles			const char *savep, *saveq;
14741556Srgrimes			int invert, found;
1475221646Sjilles			wchar_t chr;
14761556Srgrimes
1477287752Sjilles			savep = p, saveq = q;
14781556Srgrimes			invert = 0;
147926488Sache			if (*p == '!' || *p == '^') {
14801556Srgrimes				invert++;
14811556Srgrimes				p++;
14821556Srgrimes			}
14831556Srgrimes			found = 0;
1484221646Sjilles			if (squoted && *q == CTLESC)
1485221646Sjilles				q++;
1486229201Sjilles			if (*q == '\0')
1487229201Sjilles				return 0;
1488229201Sjilles			if (localeisutf8) {
1489221646Sjilles				chr = get_wc(&q);
1490229201Sjilles				if (chr == 0)
1491229201Sjilles					goto backtrack;
1492229201Sjilles			} else
1493223010Sjilles				chr = (unsigned char)*q++;
14941556Srgrimes			c = *p++;
14951556Srgrimes			do {
1496287752Sjilles				if (c == '\0') {
1497287752Sjilles					p = savep, q = saveq;
1498287752Sjilles					c = '[';
1499287752Sjilles					goto dft;
1500287752Sjilles				}
150138887Stegge				if (c == CTLQUOTEMARK)
150238887Stegge					continue;
1503223120Sjilles				if (c == '[' && *p == ':') {
1504223120Sjilles					found |= match_charclass(p, chr, &end);
1505223120Sjilles					if (end != NULL)
1506223120Sjilles						p = end;
1507223120Sjilles				}
15081556Srgrimes				if (c == CTLESC)
15091556Srgrimes					c = *p++;
1510221646Sjilles				if (localeisutf8 && c & 0x80) {
1511221646Sjilles					p--;
1512221646Sjilles					wc = get_wc(&p);
1513221646Sjilles					if (wc == 0) /* bad utf-8 */
1514221646Sjilles						return 0;
1515221646Sjilles				} else
1516223010Sjilles					wc = (unsigned char)c;
15171556Srgrimes				if (*p == '-' && p[1] != ']') {
15181556Srgrimes					p++;
151938887Stegge					while (*p == CTLQUOTEMARK)
152038887Stegge						p++;
15211556Srgrimes					if (*p == CTLESC)
15221556Srgrimes						p++;
1523221646Sjilles					if (localeisutf8) {
1524221646Sjilles						wc2 = get_wc(&p);
1525221646Sjilles						if (wc2 == 0) /* bad utf-8 */
1526221646Sjilles							return 0;
1527221646Sjilles					} else
1528223010Sjilles						wc2 = (unsigned char)*p++;
1529221646Sjilles					if (   collate_range_cmp(chr, wc) >= 0
1530221646Sjilles					    && collate_range_cmp(chr, wc2) <= 0
153117525Sache					   )
15321556Srgrimes						found = 1;
15331556Srgrimes				} else {
1534221646Sjilles					if (chr == wc)
15351556Srgrimes						found = 1;
15361556Srgrimes				}
15371556Srgrimes			} while ((c = *p++) != ']');
15381556Srgrimes			if (found == invert)
1539229201Sjilles				goto backtrack;
15401556Srgrimes			break;
15411556Srgrimes		}
15421556Srgrimesdft:	        default:
154345514Stegge			if (squoted && *q == CTLESC)
154445514Stegge				q++;
1545229201Sjilles			if (*q == '\0')
15461556Srgrimes				return 0;
1547229201Sjilles			if (*q++ == c)
1548229201Sjilles				break;
1549229201Sjillesbacktrack:
1550229201Sjilles			/*
1551229201Sjilles			 * If we have a mismatch (other than hitting the end
1552229201Sjilles			 * of the string), go back to the last '*' seen and
1553229201Sjilles			 * have it match one additional character.
1554229201Sjilles			 */
1555229201Sjilles			if (bt_p == NULL)
1556229201Sjilles				return 0;
1557229201Sjilles			if (squoted && *bt_q == CTLESC)
1558229201Sjilles				bt_q++;
1559229201Sjilles			if (*bt_q == '\0')
1560229201Sjilles				return 0;
1561229201Sjilles			bt_q++;
1562229201Sjilles			p = bt_p;
1563229201Sjilles			q = bt_q;
15641556Srgrimes			break;
15651556Srgrimes		}
15661556Srgrimes	}
15671556Srgrimes}
15681556Srgrimes
15691556Srgrimes
15701556Srgrimes
15711556Srgrimes/*
1572212243Sjilles * Remove any CTLESC and CTLQUOTEMARK characters from a string.
15731556Srgrimes */
15741556Srgrimes
15751556Srgrimesvoid
157690111Simprmescapes(char *str)
157738887Stegge{
157825233Ssteve	char *p, *q;
15791556Srgrimes
15801556Srgrimes	p = str;
1581214512Sjilles	while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) {
15821556Srgrimes		if (*p++ == '\0')
15831556Srgrimes			return;
15841556Srgrimes	}
15851556Srgrimes	q = p;
15861556Srgrimes	while (*p) {
1587214512Sjilles		if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) {
158838887Stegge			p++;
158938887Stegge			continue;
159038887Stegge		}
15911556Srgrimes		if (*p == CTLESC)
15921556Srgrimes			p++;
15931556Srgrimes		*q++ = *p++;
15941556Srgrimes	}
15951556Srgrimes	*q = '\0';
15961556Srgrimes}
15971556Srgrimes
15981556Srgrimes
15991556Srgrimes
16001556Srgrimes/*
16011556Srgrimes * See if a pattern matches in a case statement.
16021556Srgrimes */
16031556Srgrimes
16041556Srgrimesint
1605200956Sjillescasematch(union node *pattern, const char *val)
160690111Simp{
16071556Srgrimes	struct stackmark smark;
16081556Srgrimes	int result;
16091556Srgrimes	char *p;
16101556Srgrimes
16111556Srgrimes	setstackmark(&smark);
16121556Srgrimes	argbackq = pattern->narg.backquote;
16131556Srgrimes	STARTSTACKSTR(expdest);
16141556Srgrimes	ifslastp = NULL;
16151556Srgrimes	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
16161556Srgrimes	STPUTC('\0', expdest);
16171556Srgrimes	p = grabstackstr(expdest);
161845514Stegge	result = patmatch(p, val, 0);
16191556Srgrimes	popstackmark(&smark);
16201556Srgrimes	return result;
16211556Srgrimes}
162217987Speter
162317987Speter/*
162417987Speter * Our own itoa().
162517987Speter */
162617987Speter
1627213811Sobrienstatic char *
162890111Simpcvtnum(int num, char *buf)
162990111Simp{
163017987Speter	char temp[32];
163117987Speter	int neg = num < 0;
163217987Speter	char *p = temp + 31;
163317987Speter
163417987Speter	temp[31] = '\0';
163517987Speter
163617987Speter	do {
163717987Speter		*--p = num % 10 + '0';
163817987Speter	} while ((num /= 10) != 0);
163917987Speter
164017987Speter	if (neg)
164117987Speter		*--p = '-';
164217987Speter
1643215783Sjilles	STPUTS(p, buf);
164417987Speter	return buf;
164517987Speter}
1646108286Stjr
1647108286Stjr/*
1648108286Stjr * Do most of the work for wordexp(3).
1649108286Stjr */
1650108286Stjr
1651108286Stjrint
1652108286Stjrwordexpcmd(int argc, char **argv)
1653108286Stjr{
1654108286Stjr	size_t len;
1655108286Stjr	int i;
1656108286Stjr
1657108286Stjr	out1fmt("%08x", argc - 1);
1658108286Stjr	for (i = 1, len = 0; i < argc; i++)
1659108286Stjr		len += strlen(argv[i]);
1660108286Stjr	out1fmt("%08x", (int)len);
1661215567Sjilles	for (i = 1; i < argc; i++)
1662215567Sjilles		outbin(argv[i], strlen(argv[i]) + 1, out1);
1663108286Stjr        return (0);
1664108286Stjr}
1665289938Sjilles
1666289938Sjilles/*
1667289938Sjilles * Do most of the work for wordexp(3), new version.
1668289938Sjilles */
1669289938Sjilles
1670289938Sjillesint
1671289938Sjillesfreebsd_wordexpcmd(int argc __unused, char **argv __unused)
1672289938Sjilles{
1673289938Sjilles	struct arglist arglist;
1674289938Sjilles	union node *args, *n;
1675289938Sjilles	struct strlist *sp;
1676289938Sjilles	size_t count, len;
1677289938Sjilles	int ch;
1678289938Sjilles	int protected = 0;
1679289938Sjilles	int fd = -1;
1680289938Sjilles
1681289938Sjilles	while ((ch = nextopt("f:p")) != '\0') {
1682289938Sjilles		switch (ch) {
1683289938Sjilles		case 'f':
1684289938Sjilles			fd = number(shoptarg);
1685289938Sjilles			break;
1686289938Sjilles		case 'p':
1687289938Sjilles			protected = 1;
1688289938Sjilles			break;
1689289938Sjilles		}
1690289938Sjilles	}
1691289938Sjilles	if (*argptr != NULL)
1692289938Sjilles		error("wrong number of arguments");
1693289938Sjilles	if (fd < 0)
1694289938Sjilles		error("missing fd");
1695289938Sjilles	INTOFF;
1696289938Sjilles	setinputfd(fd, 1);
1697289938Sjilles	INTON;
1698289938Sjilles	args = parsewordexp();
1699289938Sjilles	popfile(); /* will also close fd */
1700289938Sjilles	if (protected)
1701289938Sjilles		for (n = args; n != NULL; n = n->narg.next) {
1702289938Sjilles			if (n->narg.backquote != NULL) {
1703289938Sjilles				outcslow('C', out1);
1704289938Sjilles				error("command substitution disabled");
1705289938Sjilles			}
1706289938Sjilles		}
1707289938Sjilles	outcslow(' ', out1);
1708289938Sjilles	arglist.lastp = &arglist.list;
1709289938Sjilles	for (n = args; n != NULL; n = n->narg.next)
1710289938Sjilles		expandarg(n, &arglist, EXP_FULL | EXP_TILDE);
1711289938Sjilles	*arglist.lastp = NULL;
1712289938Sjilles	for (sp = arglist.list, count = len = 0; sp; sp = sp->next)
1713289938Sjilles		count++, len += strlen(sp->text);
1714289938Sjilles	out1fmt("%016zx %016zx", count, len);
1715289938Sjilles	for (sp = arglist.list; sp; sp = sp->next)
1716289938Sjilles		outbin(sp->text, strlen(sp->text) + 1, out1);
1717289938Sjilles	return (0);
1718289938Sjilles}
1719