xref: /netbsd-src/bin/sh/expand.c (revision d08d589de93595b37d5d2d504043fcd02699a97c)
1*d08d589dSchristos /*	$NetBSD: expand.c,v 1.136 2019/10/14 13:34:14 christos Exp $	*/
249f0ad86Scgd 
361f28255Scgd /*-
437ed7877Sjtc  * Copyright (c) 1991, 1993
537ed7877Sjtc  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * This code is derived from software contributed to Berkeley by
861f28255Scgd  * Kenneth Almquist.
961f28255Scgd  *
1061f28255Scgd  * Redistribution and use in source and binary forms, with or without
1161f28255Scgd  * modification, are permitted provided that the following conditions
1261f28255Scgd  * are met:
1361f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1561f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1761f28255Scgd  *    documentation and/or other materials provided with the distribution.
18b5b29542Sagc  * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd  *    may be used to endorse or promote products derived from this software
2061f28255Scgd  *    without specific prior written permission.
2161f28255Scgd  *
2261f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd  * SUCH DAMAGE.
3361f28255Scgd  */
3461f28255Scgd 
35cd799663Schristos #include <sys/cdefs.h>
3661f28255Scgd #ifndef lint
3749f0ad86Scgd #if 0
38fbac2e9dSchristos static char sccsid[] = "@(#)expand.c	8.5 (Berkeley) 5/15/95";
3949f0ad86Scgd #else
40*d08d589dSchristos __RCSID("$NetBSD: expand.c,v 1.136 2019/10/14 13:34:14 christos Exp $");
4149f0ad86Scgd #endif
4261f28255Scgd #endif /* not lint */
4361f28255Scgd 
4407bae7edSchristos #include <sys/types.h>
4507bae7edSchristos #include <sys/time.h>
4607bae7edSchristos #include <sys/stat.h>
4707bae7edSchristos #include <errno.h>
4807bae7edSchristos #include <dirent.h>
4907bae7edSchristos #include <unistd.h>
5007bae7edSchristos #include <pwd.h>
51a080d612Schristos #include <limits.h>
5207bae7edSchristos #include <stdlib.h>
531fbf0781Smycroft #include <stdio.h>
547969ec4dSroy #include <wctype.h>
558e4a570fSchristos #include <wchar.h>
5607bae7edSchristos 
5761f28255Scgd /*
5861f28255Scgd  * Routines to expand arguments to commands.  We have to deal with
5961f28255Scgd  * backquotes, shell variables, and file metacharacters.
6061f28255Scgd  */
6161f28255Scgd 
6261f28255Scgd #include "shell.h"
6361f28255Scgd #include "main.h"
6461f28255Scgd #include "nodes.h"
6561f28255Scgd #include "eval.h"
6661f28255Scgd #include "expand.h"
6761f28255Scgd #include "syntax.h"
68cc8e58edSkre #include "arithmetic.h"
6961f28255Scgd #include "parser.h"
7061f28255Scgd #include "jobs.h"
7161f28255Scgd #include "options.h"
724fc4fe2eSchristos #include "builtins.h"
7361f28255Scgd #include "var.h"
7461f28255Scgd #include "input.h"
7561f28255Scgd #include "output.h"
7661f28255Scgd #include "memalloc.h"
7761f28255Scgd #include "error.h"
7861f28255Scgd #include "mystring.h"
7907bae7edSchristos #include "show.h"
8061f28255Scgd 
8161f28255Scgd /*
8261f28255Scgd  * Structure specifying which parts of the string should be searched
8361f28255Scgd  * for IFS characters.
8461f28255Scgd  */
8561f28255Scgd 
8661f28255Scgd struct ifsregion {
8761f28255Scgd 	struct ifsregion *next;	/* next region in list */
8861f28255Scgd 	int begoff;		/* offset of start of region */
8961f28255Scgd 	int endoff;		/* offset of end of region */
90e4a2a056Sdsl 	int inquotes;		/* search for nul bytes only */
9161f28255Scgd };
9261f28255Scgd 
9361f28255Scgd 
9461f28255Scgd char *expdest;			/* output of current string */
9561f28255Scgd struct nodelist *argbackq;	/* list of back quote expressions */
9661f28255Scgd struct ifsregion ifsfirst;	/* first struct in list of ifs regions */
9761f28255Scgd struct ifsregion *ifslastp;	/* last struct in list */
9861f28255Scgd struct arglist exparg;		/* holds expanded arg list */
9961f28255Scgd 
100265b0617Skre static int empty_dollar_at;	/* have expanded "$@" to nothing */
101265b0617Skre 
102dd6b6414Skre STATIC const char *argstr(const char *, int);
103dd6b6414Skre STATIC const char *exptilde(const char *, int);
104c02b3bbdSchristos STATIC void expbackq(union node *, int, int);
10551791eabSkre STATIC const char *expari(const char *);
106dd6b6414Skre STATIC int subevalvar(const char *, const char *, int, int, int);
107dd6b6414Skre STATIC int subevalvar_trim(const char *, int, int, int, int, int);
108dd6b6414Skre STATIC const char *evalvar(const char *, int);
109dd6b6414Skre STATIC int varisset(const char *, int);
110dd6b6414Skre STATIC void varvalue(const char *, int, int, int);
111c02b3bbdSchristos STATIC void recordregion(int, int, int);
112c02b3bbdSchristos STATIC void removerecordregions(int);
113c02b3bbdSchristos STATIC void ifsbreakup(char *, struct arglist *);
114c02b3bbdSchristos STATIC void ifsfree(void);
115c02b3bbdSchristos STATIC void expandmeta(struct strlist *, int);
116c02b3bbdSchristos STATIC void expmeta(char *, char *);
117c02b3bbdSchristos STATIC void addfname(char *);
118c02b3bbdSchristos STATIC struct strlist *expsort(struct strlist *);
119c02b3bbdSchristos STATIC struct strlist *msort(struct strlist *, int);
1208e4a570fSchristos STATIC int patmatch(const char *, const char *, int);
121c02b3bbdSchristos STATIC char *cvtnum(int, char *);
1228e4a570fSchristos static int collate_range_cmp(wchar_t, wchar_t);
123dd6b6414Skre STATIC void add_args(struct strlist *);
124727a69dcSkre STATIC void rmescapes_nl(char *);
12533809804Schristos 
126701ac132Skre #ifdef	DEBUG
127701ac132Skre #define	NULLTERM_4_TRACE(p)	STACKSTRNUL(p)
128701ac132Skre #else
129701ac132Skre #define	NULLTERM_4_TRACE(p)	do { /* nothing */ } while (/*CONSTCOND*/0)
130701ac132Skre #endif
131701ac132Skre 
132c9f333adSkre #define	IS_BORING(_ch)						\
133c9f333adSkre 	((_ch) == CTLQUOTEMARK || (_ch) == CTLQUOTEEND || (_ch) == CTLNONL)
134c9f333adSkre #define	SKIP_BORING(p)						\
135c9f333adSkre 	do {							\
136c9f333adSkre 		char _ch;					\
137c9f333adSkre 								\
138c9f333adSkre 		while ((_ch = *(p)), IS_BORING(_ch))		\
139c9f333adSkre 			(p)++;					\
140c9f333adSkre 	} while (0)
141c9f333adSkre 
14261f28255Scgd /*
14361f28255Scgd  * Expand shell variables and backquotes inside a here document.
14461f28255Scgd  */
14561f28255Scgd 
14661f28255Scgd void
147c02b3bbdSchristos expandhere(union node *arg, int fd)
14861f28255Scgd {
1491d1484aaSchristos 
15061f28255Scgd 	herefd = fd;
1519f61b804Splunky 	expandarg(arg, NULL, 0);
15261f28255Scgd 	xwrite(fd, stackblock(), expdest - stackblock());
15361f28255Scgd }
15461f28255Scgd 
15561f28255Scgd 
1568e4a570fSchristos static int
1578e4a570fSchristos collate_range_cmp(wchar_t c1, wchar_t c2)
1588e4a570fSchristos {
1598e4a570fSchristos 	wchar_t s1[2], s2[2];
1608e4a570fSchristos 
1618e4a570fSchristos 	s1[0] = c1;
1628e4a570fSchristos 	s1[1] = L'\0';
1638e4a570fSchristos 	s2[0] = c2;
1648e4a570fSchristos 	s2[1] = L'\0';
1658e4a570fSchristos 	return (wcscoll(s1, s2));
1668e4a570fSchristos }
1678e4a570fSchristos 
16861f28255Scgd /*
16961f28255Scgd  * Perform variable substitution and command substitution on an argument,
17037ed7877Sjtc  * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
17161f28255Scgd  * perform splitting and file name expansion.  When arglist is NULL, perform
17261f28255Scgd  * here document expansion.
17361f28255Scgd  */
17461f28255Scgd 
17561f28255Scgd void
176c02b3bbdSchristos expandarg(union node *arg, struct arglist *arglist, int flag)
17761f28255Scgd {
17861f28255Scgd 	struct strlist *sp;
17961f28255Scgd 	char *p;
18061f28255Scgd 
18158810bf0Skre 	CTRACE(DBG_EXPAND, ("expandarg(fl=%#x)\n", flag));
182dd6b6414Skre 	if (fflag)		/* no filename expandsion */
183dd6b6414Skre 		flag &= ~EXP_GLOB;
184dd6b6414Skre 
185265b0617Skre 	empty_dollar_at = 0;
18661f28255Scgd 	argbackq = arg->narg.backquote;
18761f28255Scgd 	STARTSTACKSTR(expdest);
18861f28255Scgd 	ifsfirst.next = NULL;
18961f28255Scgd 	ifslastp = NULL;
190727a69dcSkre 	line_number = arg->narg.lineno;
19169c48e32Skre 	argstr(arg->narg.text, flag);
19237ed7877Sjtc 	if (arglist == NULL) {
1931fca9bbfSkre 		STACKSTRNUL(expdest);
19458810bf0Skre 		CTRACE(DBG_EXPAND, ("expandarg: no arglist, done (%d) \"%s\"\n",
19558810bf0Skre 		    expdest - stackblock(), stackblock()));
19661f28255Scgd 		return;			/* here document expanded */
19737ed7877Sjtc 	}
19861f28255Scgd 	STPUTC('\0', expdest);
19958810bf0Skre 	CTRACE(DBG_EXPAND, ("expandarg: arglist got (%d) \"%s\"\n",
20058810bf0Skre 		    expdest - stackblock() - 1, stackblock()));
20161f28255Scgd 	p = grabstackstr(expdest);
20261f28255Scgd 	exparg.lastp = &exparg.list;
20337ed7877Sjtc 	/*
20437ed7877Sjtc 	 * TODO - EXP_REDIR
20537ed7877Sjtc 	 */
206dd6b6414Skre 	if (flag & EXP_SPLIT) {
20761f28255Scgd 		ifsbreakup(p, &exparg);
20861f28255Scgd 		*exparg.lastp = NULL;
20961f28255Scgd 		exparg.lastp = &exparg.list;
210dd6b6414Skre 		if (flag & EXP_GLOB)
21137ed7877Sjtc 			expandmeta(exparg.list, flag);
212dd6b6414Skre 		else
213dd6b6414Skre 			add_args(exparg.list);
214256d645dSkre #if 0
215256d645dSkre 	} else if (flag & EXP_REDIR) {
216256d645dSkre 		/* if EXP_REDIR ever happens, it happens here */
217256d645dSkre 		/* for now just (below) remove escapes, and leave it alone */
218256d645dSkre #endif
21961f28255Scgd 	} else {
220256d645dSkre 		rmescapes(p);	/* we might have escaped CTL bytes to remove */
22178204bbfSchristos 		sp = stalloc(sizeof(*sp));
22261f28255Scgd 		sp->text = p;
22361f28255Scgd 		*exparg.lastp = sp;
22461f28255Scgd 		exparg.lastp = &sp->next;
22561f28255Scgd 	}
226f7c8df6dSchristos 	ifsfree();
22761f28255Scgd 	*exparg.lastp = NULL;
22861f28255Scgd 	if (exparg.list) {
22961f28255Scgd 		*arglist->lastp = exparg.list;
23061f28255Scgd 		arglist->lastp = exparg.lastp;
23161f28255Scgd 	}
23261f28255Scgd }
23361f28255Scgd 
23461f28255Scgd 
23561f28255Scgd 
23661f28255Scgd /*
237c6cbc16dSdsl  * Perform variable and command substitution.
238dd6b6414Skre  * If EXP_GLOB is set, output CTLESC characters to allow for further processing.
239dd6b6414Skre  * If EXP_SPLIT is set, remember location of result for later,
240c6cbc16dSdsl  * Otherwise treat $@ like $* since no splitting will be performed.
24161f28255Scgd  */
24261f28255Scgd 
243dd6b6414Skre STATIC const char *
244dd6b6414Skre argstr(const char *p, int flag)
24561f28255Scgd {
24648250187Stls 	char c;
247df073671Skre 	const int quotes = flag & EXP_QNEEDED;		/* do CTLESC */
24837ed7877Sjtc 	int firsteq = 1;
249265b0617Skre 	int had_dol_at = 0;
250265b0617Skre 	int startoff;
2515166671bSlukem 	const char *ifs = NULL;
252c6cbc16dSdsl 	int ifs_split = EXP_IFS_SPLIT;
253c6cbc16dSdsl 
254c6cbc16dSdsl 	if (flag & EXP_IFS_SPLIT)
255ca12a0b8Schristos 		ifs = ifsval();
25661f28255Scgd 
257ea89b130Skre 	CTRACE(DBG_EXPAND, ("argstr(\"%s\", %#x) quotes=%#x\n", p,flag,quotes));
258dd6b6414Skre 
259265b0617Skre 	startoff = expdest - stackblock();
26037ed7877Sjtc 	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
26137ed7877Sjtc 		p = exptilde(p, flag);
26261f28255Scgd 	for (;;) {
26361f28255Scgd 		switch (c = *p++) {
26461f28255Scgd 		case '\0':
265701ac132Skre 			NULLTERM_4_TRACE(expdest);
266dd6b6414Skre 			VTRACE(DBG_EXPAND, ("argstr returning at \"\" "
267ee3b307fSkre 			   "added \"%s\" to expdest\n", stackblock()));
268dd6b6414Skre 			return p - 1;
269e4a2a056Sdsl 		case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */
270dd6b6414Skre 		case CTLENDARI: /* end of a $(( )) string */
271*d08d589dSchristos 			if (had_dol_at && *p == CTLQUOTEEND)
272265b0617Skre 				p++;
273701ac132Skre 			NULLTERM_4_TRACE(expdest);
274dd6b6414Skre 			VTRACE(DBG_EXPAND, ("argstr returning at \"%.6s\"..."
275ee3b307fSkre 			    " after %2.2X; added \"%s\" to expdest\n",
276ee3b307fSkre 			    p, (c & 0xff), stackblock()));
277dd6b6414Skre 			return p;
2781fbf0781Smycroft 		case CTLQUOTEMARK:
2791fbf0781Smycroft 			/* "$@" syntax adherence hack */
280dec2ca90Schristos 			if (p[0] == CTLVAR && p[1] & VSQUOTE &&
281265b0617Skre 			    p[2] == '@' && p[3] == '=') {
282265b0617Skre 				had_dol_at = 1;
2831fbf0781Smycroft 				break;
284265b0617Skre 			}
285265b0617Skre 			had_dol_at = 0;
286265b0617Skre 			empty_dollar_at = 0;
287dd6b6414Skre 			if ((flag & EXP_SPLIT) != 0)
2881fbf0781Smycroft 				STPUTC(c, expdest);
289c6cbc16dSdsl 			ifs_split = 0;
290c6cbc16dSdsl 			break;
291727a69dcSkre 		case CTLNONL:
292727a69dcSkre 			if (flag & EXP_NL)
293727a69dcSkre 				STPUTC(c, expdest);
294727a69dcSkre 			line_number++;
295727a69dcSkre 			break;
2965f92382cSkre 		case CTLCNL:
2975f92382cSkre 			STPUTC('\n', expdest);	/* no line_number++ */
2985f92382cSkre 			break;
299c6cbc16dSdsl 		case CTLQUOTEEND:
300265b0617Skre 			if (empty_dollar_at &&
301265b0617Skre 			    expdest - stackblock() > startoff &&
302265b0617Skre 			    expdest[-1] == CTLQUOTEMARK)
303265b0617Skre 				expdest--;
304265b0617Skre 			else if (!had_dol_at && (flag & EXP_SPLIT) != 0)
305c9f333adSkre 				STPUTC(c, expdest);
306c6cbc16dSdsl 			ifs_split = EXP_IFS_SPLIT;
307265b0617Skre 			had_dol_at = 0;
3081fbf0781Smycroft 			break;
30961f28255Scgd 		case CTLESC:
310256d645dSkre 			if (quotes || ISCTL(*p))
31161f28255Scgd 				STPUTC(c, expdest);
31261f28255Scgd 			c = *p++;
31361f28255Scgd 			STPUTC(c, expdest);
314ee3b307fSkre 			if (c == '\n')		/* should not happen, but ... */
315ee3b307fSkre 				line_number++;
31661f28255Scgd 			break;
317ee3b307fSkre 		case CTLVAR: {
318ee3b307fSkre #ifdef DEBUG
319ee3b307fSkre 			unsigned int pos = expdest - stackblock();
320c9f333adSkre 			NULLTERM_4_TRACE(expdest);
321ee3b307fSkre #endif
322c6cbc16dSdsl 			p = evalvar(p, (flag & ~EXP_IFS_SPLIT) | (flag & ifs_split));
323701ac132Skre 			NULLTERM_4_TRACE(expdest);
324dd6b6414Skre 			VTRACE(DBG_EXPAND, ("argstr evalvar "
325c9f333adSkre 			   "added %zd \"%s\" to expdest\n",
326c9f333adSkre 			   (size_t)(expdest - (stackblock() + pos)),
327ee3b307fSkre 			   stackblock() + pos));
32861f28255Scgd 			break;
329ee3b307fSkre 		}
33061f28255Scgd 		case CTLBACKQ:
331ee3b307fSkre 		case CTLBACKQ|CTLQUOTE: {
332ee3b307fSkre #ifdef DEBUG
333ee3b307fSkre 			unsigned int pos = expdest - stackblock();
334ee3b307fSkre #endif
33537ed7877Sjtc 			expbackq(argbackq->n, c & CTLQUOTE, flag);
33661f28255Scgd 			argbackq = argbackq->next;
337701ac132Skre 			NULLTERM_4_TRACE(expdest);
338ee3b307fSkre 			VTRACE(DBG_EXPAND, ("argstr expbackq added \"%s\" "
339ee3b307fSkre 			   "to expdest\n", stackblock() + pos));
34061f28255Scgd 			break;
341ee3b307fSkre 		}
342ee3b307fSkre 		case CTLARI: {
343ee3b307fSkre #ifdef DEBUG
344ee3b307fSkre 			unsigned int pos = expdest - stackblock();
345ee3b307fSkre #endif
34651791eabSkre 			p = expari(p);
347701ac132Skre 			NULLTERM_4_TRACE(expdest);
348dd6b6414Skre 			VTRACE(DBG_EXPAND, ("argstr expari "
349ee3b307fSkre 			   "+ \"%s\" to expdest p=\"%.5s...\"\n",
350ee3b307fSkre 			   stackblock() + pos, p));
35137ed7877Sjtc 			break;
352ee3b307fSkre 		}
35337ed7877Sjtc 		case ':':
35437ed7877Sjtc 		case '=':
35537ed7877Sjtc 			/*
35637ed7877Sjtc 			 * sort of a hack - expand tildes in variable
35737ed7877Sjtc 			 * assignments (after the first '=' and after ':'s).
35837ed7877Sjtc 			 */
35937ed7877Sjtc 			STPUTC(c, expdest);
36037ed7877Sjtc 			if (flag & EXP_VARTILDE && *p == '~') {
36137ed7877Sjtc 				if (c == '=') {
36237ed7877Sjtc 					if (firsteq)
36337ed7877Sjtc 						firsteq = 0;
36437ed7877Sjtc 					else
36537ed7877Sjtc 						break;
36637ed7877Sjtc 				}
36737ed7877Sjtc 				p = exptilde(p, flag);
36837ed7877Sjtc 			}
36937ed7877Sjtc 			break;
37061f28255Scgd 		default:
371727a69dcSkre 			if (c == '\n')
372727a69dcSkre 				line_number++;
37361f28255Scgd 			STPUTC(c, expdest);
3747d60739aSdsl 			if (flag & ifs_split && strchr(ifs, c) != NULL) {
375c6cbc16dSdsl 				/* We need to get the output split here... */
376c6cbc16dSdsl 				recordregion(expdest - stackblock() - 1,
377c6cbc16dSdsl 						expdest - stackblock(), 0);
378c6cbc16dSdsl 			}
379e4a2a056Sdsl 			break;
38061f28255Scgd 		}
38161f28255Scgd 	}
38261f28255Scgd }
38361f28255Scgd 
384dd6b6414Skre STATIC const char *
385dd6b6414Skre exptilde(const char *p, int flag)
38637ed7877Sjtc {
387dd6b6414Skre 	char c;
388dd6b6414Skre 	const char *startp = p;
38937ed7877Sjtc 	struct passwd *pw;
3905dd823abSmycroft 	const char *home;
391df073671Skre 	const int quotes = flag & EXP_QNEEDED;
392dd6b6414Skre 	char *user;
393ee3b307fSkre 	struct stackmark smark;
394ee3b307fSkre #ifdef DEBUG
395ee3b307fSkre 	unsigned int offs = expdest - stackblock();
396ee3b307fSkre #endif
39737ed7877Sjtc 
398ee3b307fSkre 	setstackmark(&smark);
399bd208c69Skre 	(void) grabstackstr(expdest);
400ee3b307fSkre 	user = stackblock();		/* we will just borrow top of stack */
401ee3b307fSkre 
402aa681addSkre 	while ((c = *++p) != '\0') {
40337ed7877Sjtc 		switch(c) {
404ee3b307fSkre 		case CTLESC:		/* any of these occurring */
405ee3b307fSkre 		case CTLVAR:		/* means ~ expansion */
406ee3b307fSkre 		case CTLBACKQ:		/* does not happen at all */
407dd6b6414Skre 		case CTLBACKQ | CTLQUOTE:
408ee3b307fSkre 		case CTLARI:		/* just leave original unchanged */
409dd6b6414Skre 		case CTLENDARI:
4101fbf0781Smycroft 		case CTLQUOTEMARK:
411ee3b307fSkre 		case '\n':
412ee3b307fSkre 			popstackmark(&smark);
4131fbf0781Smycroft 			return (startp);
414727a69dcSkre 		case CTLNONL:
415ee3b307fSkre 			continue;
41637ed7877Sjtc 		case ':':
417bd208c69Skre 			if (!posix || flag & EXP_VARTILDE)
41837ed7877Sjtc 				goto done;
41937ed7877Sjtc 			break;
420bd208c69Skre 		case CTLENDVAR:
42137ed7877Sjtc 		case '/':
42237ed7877Sjtc 			goto done;
42337ed7877Sjtc 		}
424dd6b6414Skre 		STPUTC(c, user);
42537ed7877Sjtc 	}
42637ed7877Sjtc  done:
427dd6b6414Skre 	STACKSTRNUL(user);
428ee3b307fSkre 	user = stackblock();		/* to start of collected username */
429ea89b130Skre 
430ee3b307fSkre 	CTRACE(DBG_EXPAND, ("exptilde, found \"~%s\"", user));
431ee3b307fSkre 	if (*user == '\0') {
432dd6b6414Skre 		home = lookupvar("HOME");
433ee3b307fSkre 		/*
434ee3b307fSkre 		 * if HOME is unset, results are unspecified...
435ee3b307fSkre 		 * we used to just leave the ~ unchanged, but
436ee3b307fSkre 		 * (some) other shells do ... and this seems more useful.
437ee3b307fSkre 		 */
438ee3b307fSkre 		if (home == NULL && (pw = getpwuid(getuid())) != NULL)
43937ed7877Sjtc 			home = pw->pw_dir;
440ee3b307fSkre 	} else if ((pw = getpwnam(user)) == NULL) {
441ee3b307fSkre 		/*
442ee3b307fSkre 		 * If user does not exist, results are undefined.
443ee3b307fSkre 		 * so we can abort() here if we want, but let's not!
444ee3b307fSkre 		 */
445ee3b307fSkre 		home = NULL;
446ee3b307fSkre 	} else
447ee3b307fSkre 		home = pw->pw_dir;
448dd6b6414Skre 
449ee3b307fSkre 	VTRACE(DBG_EXPAND, (" ->\"%s\"", home ? home : "<<NULL>>"));
450ee3b307fSkre 	popstackmark(&smark);	/* now expdest is valid again */
451ee3b307fSkre 
452ee3b307fSkre 	/*
453ee3b307fSkre 	 * Posix XCU 2.6.1: The value of $HOME (for ~) or the initial
454ee3b307fSkre 	 *		working directory from getpwnam() for ~user
455ee3b307fSkre 	 * Nothing there about "except if a null string".  So do what it wants.
456ee3b307fSkre 	 */
457ee3b307fSkre 	if (home == NULL /* || *home == '\0' */) {
458ee3b307fSkre 		CTRACE(DBG_EXPAND, (": returning unused \"%s\"\n", startp));
459dd6b6414Skre 		return startp;
460ee3b307fSkre 	} while ((c = *home++) != '\0') {
461256d645dSkre 		if ((quotes && NEEDESC(c)) || ISCTL(c))
46237ed7877Sjtc 			STPUTC(CTLESC, expdest);
46337ed7877Sjtc 		STPUTC(c, expdest);
46437ed7877Sjtc 	}
465ee3b307fSkre 	CTRACE(DBG_EXPAND, (": added %d \"%.*s\" returning \"%s\"\n",
466ee3b307fSkre 	      expdest - stackblock() - offs, expdest - stackblock() - offs,
467ee3b307fSkre 	      stackblock() + offs, p));
468ea89b130Skre 
46937ed7877Sjtc 	return (p);
47037ed7877Sjtc }
47137ed7877Sjtc 
47237ed7877Sjtc 
4731fbf0781Smycroft STATIC void
474c02b3bbdSchristos removerecordregions(int endoff)
4751fbf0781Smycroft {
47658810bf0Skre 
47758810bf0Skre 	VTRACE(DBG_EXPAND, ("removerecordregions(%d):", endoff));
47858810bf0Skre 	if (ifslastp == NULL) {
47958810bf0Skre 		VTRACE(DBG_EXPAND, (" none\n", endoff));
4801fbf0781Smycroft 		return;
48158810bf0Skre 	}
4821fbf0781Smycroft 
4831fbf0781Smycroft 	if (ifsfirst.endoff > endoff) {
48458810bf0Skre 		VTRACE(DBG_EXPAND, (" first(%d)", ifsfirst.endoff));
4851fbf0781Smycroft 		while (ifsfirst.next != NULL) {
4861fbf0781Smycroft 			struct ifsregion *ifsp;
4871fbf0781Smycroft 			INTOFF;
4881fbf0781Smycroft 			ifsp = ifsfirst.next->next;
4891fbf0781Smycroft 			ckfree(ifsfirst.next);
4901fbf0781Smycroft 			ifsfirst.next = ifsp;
4911fbf0781Smycroft 			INTON;
4921fbf0781Smycroft 		}
4931fbf0781Smycroft 		if (ifsfirst.begoff > endoff)
4941fbf0781Smycroft 			ifslastp = NULL;
4951fbf0781Smycroft 		else {
49658810bf0Skre 			VTRACE(DBG_EXPAND,("->(%d,%d)",ifsfirst.begoff,endoff));
4971fbf0781Smycroft 			ifslastp = &ifsfirst;
4981fbf0781Smycroft 			ifsfirst.endoff = endoff;
4991fbf0781Smycroft 		}
50058810bf0Skre 		VTRACE(DBG_EXPAND, ("\n"));
5011fbf0781Smycroft 		return;
5021fbf0781Smycroft 	}
5031fbf0781Smycroft 
5041fbf0781Smycroft 	ifslastp = &ifsfirst;
5051fbf0781Smycroft 	while (ifslastp->next && ifslastp->next->begoff < endoff)
5061fbf0781Smycroft 		ifslastp=ifslastp->next;
50758810bf0Skre 	VTRACE(DBG_EXPAND, (" found(%d,%d)", ifslastp->begoff,ifslastp->endoff));
5081fbf0781Smycroft 	while (ifslastp->next != NULL) {
5091fbf0781Smycroft 		struct ifsregion *ifsp;
5101fbf0781Smycroft 		INTOFF;
5111fbf0781Smycroft 		ifsp = ifslastp->next->next;
5121fbf0781Smycroft 		ckfree(ifslastp->next);
5131fbf0781Smycroft 		ifslastp->next = ifsp;
5141fbf0781Smycroft 		INTON;
5151fbf0781Smycroft 	}
5161fbf0781Smycroft 	if (ifslastp->endoff > endoff)
5171fbf0781Smycroft 		ifslastp->endoff = endoff;
51858810bf0Skre 	VTRACE(DBG_EXPAND, ("->(%d,%d)", ifslastp->begoff,ifslastp->endoff));
5191fbf0781Smycroft }
5201fbf0781Smycroft 
5211fbf0781Smycroft 
52237ed7877Sjtc /*
523ee3b307fSkre  * Expand arithmetic expression.
524ee3b307fSkre  *
525ee3b307fSkre  * In this incarnation, we start at the beginning (yes, "Let's start at the
526ee3b307fSkre  * very beginning.  A very good place to start.") and collect the expression
527ee3b307fSkre  * until the end - which means expanding anything contained within.
528ee3b307fSkre  *
529ee3b307fSkre  * Fortunately, argstr() just happens to do that for us...
53037ed7877Sjtc  */
531dd6b6414Skre STATIC const char *
53251791eabSkre expari(const char *p)
53337ed7877Sjtc {
534dd6b6414Skre 	char *q, *start;
53591ce988bSapb 	intmax_t result;
53691ce988bSapb 	int adjustment;
5371fbf0781Smycroft 	int begoff;
5381fbf0781Smycroft 	int quoted;
539ee3b307fSkre 	struct stackmark smark;
54037ed7877Sjtc 
5411fbf0781Smycroft 	/*	ifsfree(); */
542633ceb6dSchristos 
54337ed7877Sjtc 	/*
544cc8e58edSkre 	 * SPACE_NEEDED is enough for all possible digits (rounded up)
545cc8e58edSkre 	 * plus possible "-", and the terminating '\0', hence, plus 2
546cc8e58edSkre 	 *
547cc8e58edSkre 	 * The calculation produces the number of bytes needed to
548cc8e58edSkre 	 * represent the biggest possible value, in octal.  We only
549cc8e58edSkre 	 * generate decimal, which takes (often) less digits (never more)
550cc8e58edSkre 	 * so this is safe, if occasionally slightly wasteful.
551cc8e58edSkre 	 */
552dd6b6414Skre #define SPACE_NEEDED ((int)((sizeof(intmax_t) * CHAR_BIT + 2) / 3 + 2))
553ee3b307fSkre 
554dd6b6414Skre 	quoted = *p++ == '"';
555dd6b6414Skre 	begoff = expdest - stackblock();
556ee3b307fSkre 	VTRACE(DBG_EXPAND, ("expari%s: \"%s\" begoff %d\n",
557ee3b307fSkre 	    quoted ? "(quoted)" : "", p, begoff));
558ee3b307fSkre 
559727a69dcSkre 	p = argstr(p, EXP_NL);			/* expand $(( )) string */
560dd6b6414Skre 	STPUTC('\0', expdest);
561dd6b6414Skre 	start = stackblock() + begoff;
562ee3b307fSkre 
563ee3b307fSkre 	removerecordregions(begoff);		/* nothing there is kept */
564727a69dcSkre 	rmescapes_nl(start);		/* convert CRTNONL back into \n's */
565ee3b307fSkre 
566ee3b307fSkre 	setstackmark(&smark);
567ee3b307fSkre 	q = grabstackstr(expdest);	/* keep the expression while eval'ing */
568727a69dcSkre 	result = arith(start, line_number);
569ee3b307fSkre 	popstackmark(&smark);		/* return the stack to before grab */
570633ceb6dSchristos 
571ee3b307fSkre 	start = stackblock() + begoff;		/* block may have moved */
572dd6b6414Skre 	adjustment = expdest - start;
573ee3b307fSkre 	STADJUST(-adjustment, expdest);		/* remove the argstr() result */
574dd6b6414Skre 
575ee3b307fSkre 	CHECKSTRSPACE(SPACE_NEEDED, expdest);	/* nb: stack block might move */
576dd6b6414Skre 	fmtstr(expdest, SPACE_NEEDED, "%"PRIdMAX, result);
577dd6b6414Skre 
578ee3b307fSkre 	for (q = expdest; *q++ != '\0'; )	/* find end of what we added */
57937ed7877Sjtc 		;
580633ceb6dSchristos 
581dd6b6414Skre 	if (quoted == 0)			/* allow weird splitting */
58258810bf0Skre 		recordregion(begoff, begoff + q - 1 - expdest, 0);
583d3573c91Skre 	adjustment = q - expdest - 1;
584ee3b307fSkre 	STADJUST(adjustment, expdest);		/* move expdest to end */
585f72bc19eSkre 	VTRACE(DBG_EXPAND, ("expari: adding %d \"%s\", returning \"%.5s...\"\n",
586f72bc19eSkre 	    adjustment, stackblock() + begoff, p));
587dd6b6414Skre 
588dd6b6414Skre 	return p;
58937ed7877Sjtc }
59037ed7877Sjtc 
59161f28255Scgd 
59261f28255Scgd /*
593ee3b307fSkre  * Expand stuff in backwards quotes (these days, any command substitution).
59461f28255Scgd  */
59561f28255Scgd 
59661f28255Scgd STATIC void
597c02b3bbdSchristos expbackq(union node *cmd, int quoted, int flag)
59861f28255Scgd {
59961f28255Scgd 	struct backcmd in;
60061f28255Scgd 	int i;
60161f28255Scgd 	char buf[128];
60261f28255Scgd 	char *p;
603ee3b307fSkre 	char *dest = expdest;	/* expdest may be reused by eval, use an alt */
60461f28255Scgd 	struct ifsregion saveifs, *savelastp;
60561f28255Scgd 	struct nodelist *saveargbackq;
60661f28255Scgd 	char lastc;
60761f28255Scgd 	int startloc = dest - stackblock();
60861f28255Scgd 	int saveherefd;
609df073671Skre 	const int quotes = flag & EXP_QNEEDED;
61069a4e2eeSchristos 	int nnl;
611ee3b307fSkre 	struct stackmark smark;
61261f28255Scgd 
613ee3b307fSkre 	VTRACE(DBG_EXPAND, ("expbackq( ..., q=%d flag=%#x) have %d\n",
614ee3b307fSkre 	    quoted, flag, startloc));
61561f28255Scgd 	INTOFF;
61661f28255Scgd 	saveifs = ifsfirst;
61761f28255Scgd 	savelastp = ifslastp;
61861f28255Scgd 	saveargbackq = argbackq;
61961f28255Scgd 	saveherefd = herefd;
62061f28255Scgd 	herefd = -1;
621ee3b307fSkre 
622ee3b307fSkre 	setstackmark(&smark);	/* preserve the stack */
623ee3b307fSkre 	p = grabstackstr(dest);	/* save what we have there currently */
624ee3b307fSkre 	evalbackcmd(cmd, &in);	/* evaluate the $( ) tree (using stack) */
625ee3b307fSkre 	popstackmark(&smark);	/* and return stack to when we entered */
626ee3b307fSkre 
62761f28255Scgd 	ifsfirst = saveifs;
62861f28255Scgd 	ifslastp = savelastp;
62961f28255Scgd 	argbackq = saveargbackq;
63061f28255Scgd 	herefd = saveherefd;
63161f28255Scgd 
632ee3b307fSkre 	p = in.buf;		/* now extract the results */
633ee3b307fSkre 	nnl = 0;		/* dropping trailing \n's */
63461f28255Scgd 	for (;;) {
63561f28255Scgd 		if (--in.nleft < 0) {
63661f28255Scgd 			if (in.fd < 0)
63761f28255Scgd 				break;
6381fca9bbfSkre 			INTON;
6393eb04a36Sjoerg 			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR)
6403eb04a36Sjoerg 				continue;
6411fca9bbfSkre 			INTOFF;
642ea89b130Skre 			VTRACE(DBG_EXPAND, ("expbackq: read returns %d\n", i));
64361f28255Scgd 			if (i <= 0)
64461f28255Scgd 				break;
64561f28255Scgd 			p = buf;
64661f28255Scgd 			in.nleft = i - 1;
64761f28255Scgd 		}
64861f28255Scgd 		lastc = *p++;
64961f28255Scgd 		if (lastc != '\0') {
650ee3b307fSkre 			if (lastc == '\n')	/* don't save \n yet */
651ee3b307fSkre 				nnl++;		/* it might be trailing */
65269a4e2eeSchristos 			else {
653ee3b307fSkre 				/*
654ee3b307fSkre 				 * We have something other than \n
655ee3b307fSkre 				 *
656ee3b307fSkre 				 * Before saving it, we need to insert
657ee3b307fSkre 				 * any \n's that we have just skipped.
658ee3b307fSkre 				 */
659ee3b307fSkre 
660ee3b307fSkre 				/* XXX
661ee3b307fSkre 				 * this hack is just because our
662ee3b307fSkre 				 * CHECKSTRSPACE() is lazy, and only
663ee3b307fSkre 				 * ever grows the stack once, even
664ee3b307fSkre 				 * if that does not allocate the space
665ee3b307fSkre 				 * we requested.  ie: safe for small
666ee3b307fSkre 				 * requests, but not large ones.
667ee3b307fSkre 				 * FIXME someday...
668ee3b307fSkre 				 */
669ee3b307fSkre 				if (nnl < 20) {
67069a4e2eeSchristos 					CHECKSTRSPACE(nnl + 2, dest);
67169a4e2eeSchristos 					while (nnl > 0) {
67269a4e2eeSchristos 						nnl--;
67369a4e2eeSchristos 						USTPUTC('\n', dest);
67469a4e2eeSchristos 					}
675ee3b307fSkre 				} else {
676ee3b307fSkre 					/* The slower, safer, way */
677ee3b307fSkre 					while (nnl > 0) {
678ee3b307fSkre 						nnl--;
679ee3b307fSkre 						STPUTC('\n', dest);
680ee3b307fSkre 					}
681ee3b307fSkre 					CHECKSTRSPACE(2, dest);
682ee3b307fSkre 				}
683256d645dSkre 				if ((quotes && quoted && NEEDESC(lastc)) ||
684256d645dSkre 				    ISCTL(lastc))
68569a4e2eeSchristos 					USTPUTC(CTLESC, dest);
68669a4e2eeSchristos 				USTPUTC(lastc, dest);
68761f28255Scgd 			}
68861f28255Scgd 		}
68969a4e2eeSchristos 	}
6906ab19545Schristos 
69161f28255Scgd 	if (in.fd >= 0)
69261f28255Scgd 		close(in.fd);
69361f28255Scgd 	if (in.buf)
69461f28255Scgd 		ckfree(in.buf);
69561f28255Scgd 	if (in.jp)
696c02b3bbdSchristos 		back_exitstatus = waitforjob(in.jp);
69761f28255Scgd 	if (quoted == 0)
69861f28255Scgd 		recordregion(startloc, dest - stackblock(), 0);
699ea89b130Skre 	CTRACE(DBG_EXPAND, ("evalbackq: size=%d: \"%.*s\"\n",
7007fb5a8c6Sdholland 		(int)((dest - stackblock()) - startloc),
7017fb5a8c6Sdholland 		(int)((dest - stackblock()) - startloc),
70261f28255Scgd 		stackblock() + startloc));
703ee3b307fSkre 
704ee3b307fSkre 	expdest = dest;		/* all done, expdest is all ours again */
70561f28255Scgd 	INTON;
70661f28255Scgd }
70761f28255Scgd 
70861f28255Scgd 
709ad8d5369Schristos STATIC int
710dd6b6414Skre subevalvar(const char *p, const char *str, int subtype, int startloc,
711dd6b6414Skre     int varflags)
712ad8d5369Schristos {
713ad8d5369Schristos 	char *startp;
714ad8d5369Schristos 	int saveherefd = herefd;
715ad8d5369Schristos 	struct nodelist *saveargbackq = argbackq;
716dd6b6414Skre 	int amount;
717d3aec206Schristos 
718ad8d5369Schristos 	herefd = -1;
719ee3b307fSkre 	VTRACE(DBG_EXPAND, ("subevalvar(%d) \"%.20s\" ${%.*s} sloc=%d vf=%x\n",
720ee3b307fSkre 	    subtype, p, p-str, str, startloc, varflags));
721bd208c69Skre 	argstr(p, subtype == VSASSIGN ? EXP_VARTILDE : EXP_TILDE);
722ad8d5369Schristos 	STACKSTRNUL(expdest);
723ad8d5369Schristos 	herefd = saveherefd;
724ad8d5369Schristos 	argbackq = saveargbackq;
725ad8d5369Schristos 	startp = stackblock() + startloc;
726ad8d5369Schristos 
727ad8d5369Schristos 	switch (subtype) {
728ad8d5369Schristos 	case VSASSIGN:
729ad8d5369Schristos 		setvar(str, startp, 0);
730ee3b307fSkre 		amount = startp - expdest;	/* remove what argstr added */
731d3aec206Schristos 		STADJUST(amount, expdest);
732ee3b307fSkre 		varflags &= ~VSNUL;	/*XXX Huh?   What's that achieve? */
733ee3b307fSkre 		return 1;			/* go back and eval var again */
734ad8d5369Schristos 
735ad8d5369Schristos 	case VSQUESTION:
736ad8d5369Schristos 		if (*p != CTLENDVAR) {
737ad8d5369Schristos 			outfmt(&errout, "%s\n", startp);
7389f61b804Splunky 			error(NULL);
739ad8d5369Schristos 		}
7407fb5a8c6Sdholland 		error("%.*s: parameter %snot set",
7417fb5a8c6Sdholland 		      (int)(p - str - 1),
742ad8d5369Schristos 		      str, (varflags & VSNUL) ? "null or "
743ad8d5369Schristos 					      : nullstr);
744ee9e50eaSmycroft 		/* NOTREACHED */
745ad8d5369Schristos 
746dd6b6414Skre 	default:
747dd6b6414Skre 		abort();
748dd6b6414Skre 	}
749dd6b6414Skre }
750dd6b6414Skre 
751dd6b6414Skre STATIC int
752dd6b6414Skre subevalvar_trim(const char *p, int strloc, int subtype, int startloc,
753dd6b6414Skre     int varflags, int quotes)
754dd6b6414Skre {
755dd6b6414Skre 	char *startp;
756dd6b6414Skre 	char *str;
757dd6b6414Skre 	char *loc = NULL;
758dd6b6414Skre 	char *q;
759dd6b6414Skre 	int c = 0;
760dd6b6414Skre 	int saveherefd = herefd;
761dd6b6414Skre 	struct nodelist *saveargbackq = argbackq;
762dd6b6414Skre 	int amount;
763dd6b6414Skre 
764dd6b6414Skre 	herefd = -1;
765dd6b6414Skre 	switch (subtype) {
766dd6b6414Skre 	case VSTRIMLEFT:
767dd6b6414Skre 	case VSTRIMLEFTMAX:
768dd6b6414Skre 	case VSTRIMRIGHT:
769dd6b6414Skre 	case VSTRIMRIGHTMAX:
770dd6b6414Skre 		break;
771dd6b6414Skre 	default:
772dd6b6414Skre 		abort();
773dd6b6414Skre 		break;
774dd6b6414Skre 	}
775ea89b130Skre 
776ea89b130Skre 	VTRACE(DBG_EXPAND,
777ea89b130Skre 	("subevalvar_trim(\"%.9s\", STR@%d, SUBT=%d, start@%d, vf=%x, q=%x)\n",
778ea89b130Skre 		p, strloc, subtype, startloc, varflags, quotes));
779ea89b130Skre 
780dd6b6414Skre 	argstr(p, (varflags & (VSQUOTE|VSPATQ)) == VSQUOTE ? 0 : EXP_CASE);
781dd6b6414Skre 	STACKSTRNUL(expdest);
782dd6b6414Skre 	herefd = saveherefd;
783dd6b6414Skre 	argbackq = saveargbackq;
784dd6b6414Skre 	startp = stackblock() + startloc;
785dd6b6414Skre 	str = stackblock() + strloc;
786dd6b6414Skre 
787dd6b6414Skre 	switch (subtype) {
788dd6b6414Skre 
789ad8d5369Schristos 	case VSTRIMLEFT:
790633ceb6dSchristos 		for (loc = startp; loc < str; loc++) {
791ad8d5369Schristos 			c = *loc;
792ad8d5369Schristos 			*loc = '\0';
79319d6b839Schristos 			if (patmatch(str, startp, quotes))
794ad8d5369Schristos 				goto recordleft;
795ad8d5369Schristos 			*loc = c;
79619d6b839Schristos 			if (quotes && *loc == CTLESC)
79728607542Schristos 				loc++;
798ad8d5369Schristos 		}
799ad8d5369Schristos 		return 0;
800ad8d5369Schristos 
801ad8d5369Schristos 	case VSTRIMLEFTMAX:
80228607542Schristos 		for (loc = str - 1; loc >= startp;) {
803ad8d5369Schristos 			c = *loc;
804ad8d5369Schristos 			*loc = '\0';
80519d6b839Schristos 			if (patmatch(str, startp, quotes))
806ad8d5369Schristos 				goto recordleft;
807ad8d5369Schristos 			*loc = c;
80828607542Schristos 			loc--;
80919d6b839Schristos 			if (quotes && loc > startp &&
81028607542Schristos 			    *(loc - 1) == CTLESC) {
81128607542Schristos 				for (q = startp; q < loc; q++)
81228607542Schristos 					if (*q == CTLESC)
81328607542Schristos 						q++;
81428607542Schristos 				if (q > loc)
81528607542Schristos 					loc--;
81628607542Schristos 			}
817ad8d5369Schristos 		}
818ad8d5369Schristos 		return 0;
819ad8d5369Schristos 
820ad8d5369Schristos 	case VSTRIMRIGHT:
82128607542Schristos 		for (loc = str - 1; loc >= startp;) {
82219d6b839Schristos 			if (patmatch(str, loc, quotes))
82304784d87Schristos 				goto recordright;
82428607542Schristos 			loc--;
82519d6b839Schristos 			if (quotes && loc > startp &&
82628607542Schristos 			    *(loc - 1) == CTLESC) {
82728607542Schristos 				for (q = startp; q < loc; q++)
82828607542Schristos 					if (*q == CTLESC)
82928607542Schristos 						q++;
83028607542Schristos 				if (q > loc)
83128607542Schristos 					loc--;
83228607542Schristos 			}
83328607542Schristos 		}
834ad8d5369Schristos 		return 0;
835ad8d5369Schristos 
836ad8d5369Schristos 	case VSTRIMRIGHTMAX:
83728607542Schristos 		for (loc = startp; loc < str - 1; loc++) {
83819d6b839Schristos 			if (patmatch(str, loc, quotes))
83904784d87Schristos 				goto recordright;
84019d6b839Schristos 			if (quotes && *loc == CTLESC)
84128607542Schristos 				loc++;
84228607542Schristos 		}
843ad8d5369Schristos 		return 0;
844ad8d5369Schristos 
845ad8d5369Schristos 	default:
846ad8d5369Schristos 		abort();
847ad8d5369Schristos 	}
848ad8d5369Schristos 
849ad8d5369Schristos  recordleft:
85004784d87Schristos 	*loc = c;
851d3aec206Schristos 	amount = ((str - 1) - (loc - startp)) - expdest;
852d3aec206Schristos 	STADJUST(amount, expdest);
853ad8d5369Schristos 	while (loc != str - 1)
854ad8d5369Schristos 		*startp++ = *loc++;
855ad8d5369Schristos 	return 1;
85604784d87Schristos 
85704784d87Schristos  recordright:
85804784d87Schristos 	amount = loc - expdest;
85904784d87Schristos 	STADJUST(amount, expdest);
86004784d87Schristos 	STPUTC('\0', expdest);
86104784d87Schristos 	STADJUST(-1, expdest);
86204784d87Schristos 	return 1;
863ad8d5369Schristos }
864ad8d5369Schristos 
865ad8d5369Schristos 
86661f28255Scgd /*
86761f28255Scgd  * Expand a variable, and return a pointer to the next character in the
86861f28255Scgd  * input string.
86961f28255Scgd  */
87061f28255Scgd 
871dd6b6414Skre STATIC const char *
872dd6b6414Skre evalvar(const char *p, int flag)
87361f28255Scgd {
87461f28255Scgd 	int subtype;
87537ed7877Sjtc 	int varflags;
876dd6b6414Skre 	const char *var;
87761f28255Scgd 	char *val;
878e97b0193She 	int patloc;
87961f28255Scgd 	int c;
88061f28255Scgd 	int set;
88161f28255Scgd 	int special;
88261f28255Scgd 	int startloc;
883ad8d5369Schristos 	int varlen;
884e4a2a056Sdsl 	int apply_ifs;
885df073671Skre 	const int quotes = flag & EXP_QNEEDED;
88661f28255Scgd 
8874dcb41aeSchristos 	varflags = (unsigned char)*p++;
88837ed7877Sjtc 	subtype = varflags & VSTYPE;
88961f28255Scgd 	var = p;
89030651d6bSjdolecek 	special = !is_name(*p);
89161f28255Scgd 	p = strchr(p, '=') + 1;
892e4a2a056Sdsl 
893ea89b130Skre 	CTRACE(DBG_EXPAND,
894ea89b130Skre 	    ("evalvar \"%.*s\", flag=%#X quotes=%#X vf=%#X subtype=%X\n",
895ea89b130Skre 	    p - var - 1, var, flag, quotes, varflags, subtype));
896ea89b130Skre 
89761f28255Scgd  again: /* jump here after setting a variable with ${var=text} */
898018a6f78Schristos 	if (varflags & VSLINENO) {
899727a69dcSkre 		if (line_num.flags & VUNSET) {
900727a69dcSkre 			set = 0;
901727a69dcSkre 			val = NULL;
902727a69dcSkre 		} else {
903018a6f78Schristos 			set = 1;
904dd6b6414Skre 			special = p - var;
905dd6b6414Skre 			val = NULL;
906727a69dcSkre 		}
907018a6f78Schristos 	} else if (special) {
908633ceb6dSchristos 		set = varisset(var, varflags & VSNUL);
90961f28255Scgd 		val = NULL;
910265b0617Skre 		if (!set && *var == '@')
911265b0617Skre 			empty_dollar_at = 1;
91261f28255Scgd 	} else {
91361f28255Scgd 		val = lookupvar(var);
91407bae7edSchristos 		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
91561f28255Scgd 			val = NULL;
91661f28255Scgd 			set = 0;
91761f28255Scgd 		} else
91861f28255Scgd 			set = 1;
91961f28255Scgd 	}
920e4a2a056Sdsl 
921ad8d5369Schristos 	varlen = 0;
92261f28255Scgd 	startloc = expdest - stackblock();
923e4a2a056Sdsl 
92483d9b545Sast 	if (!set && uflag && *var != '@' && *var != '*') {
925eac95517Schristos 		switch (subtype) {
926eac95517Schristos 		case VSNORMAL:
927eac95517Schristos 		case VSTRIMLEFT:
928eac95517Schristos 		case VSTRIMLEFTMAX:
929eac95517Schristos 		case VSTRIMRIGHT:
930eac95517Schristos 		case VSTRIMRIGHTMAX:
931eac95517Schristos 		case VSLENGTH:
9327fb5a8c6Sdholland 			error("%.*s: parameter not set",
9337fb5a8c6Sdholland 			    (int)(p - var - 1), var);
934eac95517Schristos 			/* NOTREACHED */
935eac95517Schristos 		}
936e4a2a056Sdsl 	}
937e4a2a056Sdsl 
938265b0617Skre #if 0		/* no longer need this $@ evil ... */
939c9f333adSkre 	if (!set && subtype != VSPLUS && special && *var == '@')
940c9f333adSkre 		if (startloc > 0 && expdest[-1] == CTLQUOTEMARK)
941c9f333adSkre 			expdest--, startloc--;
942265b0617Skre #endif
943c9f333adSkre 
94461f28255Scgd 	if (set && subtype != VSPLUS) {
94561f28255Scgd 		/* insert the value of the variable */
94661f28255Scgd 		if (special) {
947dd6b6414Skre 			if (varflags & VSLINENO) {
948dd6b6414Skre 				/*
949ee3b307fSkre 				 * The LINENO hack (expansion part)
950dd6b6414Skre 				 */
951dd6b6414Skre 				while (--special > 0) {
952dd6b6414Skre /*						not needed, it is a number...
953df073671Skre 					if (quotes && NEEDESC(*var))
954dd6b6414Skre 						STPUTC(CTLESC, expdest);
955dd6b6414Skre */
956dd6b6414Skre 					STPUTC(*var++, expdest);
957dd6b6414Skre 				}
958dd6b6414Skre 			} else
959b17e1b5cSdsl 				varvalue(var, varflags&VSQUOTE, subtype, flag);
960ad8d5369Schristos 			if (subtype == VSLENGTH) {
961a0fa692dSchristos 				varlen = expdest - stackblock() - startloc;
962a0fa692dSchristos 				STADJUST(-varlen, expdest);
963ad8d5369Schristos 			}
96461f28255Scgd 		} else {
96561f28255Scgd 
966ad8d5369Schristos 			if (subtype == VSLENGTH) {
967ad8d5369Schristos 				for (; *val; val++)
968ad8d5369Schristos 					varlen++;
969df073671Skre 			} else if (quotes && varflags & VSQUOTE) {
97058e34de6Skre 				/*
97158e34de6Skre 				 * If we are going to look for magic in the
97258e34de6Skre 				 * value (quotes is set) and the expansion
97358e34de6Skre 				 * occurs inside "" (VSQUOTE) then any char
97458e34de6Skre 				 * that has any potential special meaning
97558e34de6Skre 				 * needs to have that meaning suppressed,
97658e34de6Skre 				 * so supply a CTLESC prefix for it.
97758e34de6Skre 				 */
978df073671Skre 				for (; (c = *val) != '\0'; val++) {
979df073671Skre 					if (NEEDESC(c))
98061f28255Scgd 						STPUTC(CTLESC, expdest);
981df073671Skre 					STPUTC(c, expdest);
98261f28255Scgd 				}
983df073671Skre 			} else {
98458e34de6Skre 				/*
98558e34de6Skre 				 * We are going to rmescapes() later,
98658e34de6Skre 				 * so make sure that any data char that
98758e34de6Skre 				 * might be mistaken for one of our CTLxxx
98858e34de6Skre 				 * magic chars is protected ... always.
98958e34de6Skre 				 */
99058e34de6Skre 				for (; (c = *val) != '\0'; val++) {
991256d645dSkre 					if (ISCTL(c))
99258e34de6Skre 						STPUTC(CTLESC, expdest);
99358e34de6Skre 					STPUTC(c, expdest);
99458e34de6Skre 				}
99561f28255Scgd 			}
99661f28255Scgd 		}
997ad8d5369Schristos 	}
998ad8d5369Schristos 
999ad8d5369Schristos 
1000c9f333adSkre 	if (varflags & VSQUOTE) {
10017d60739aSdsl 		if  (*var == '@' && shellparam.nparam != 1)
10027d60739aSdsl 		    apply_ifs = 1;
10037d60739aSdsl 		else {
10047d60739aSdsl 		    /*
10057d60739aSdsl 		     * Mark so that we don't apply IFS if we recurse through
10067d60739aSdsl 		     * here expanding $bar from "${foo-$bar}".
10077d60739aSdsl 		     */
10087d60739aSdsl 		    flag |= EXP_IN_QUOTES;
10097d60739aSdsl 		    apply_ifs = 0;
10107d60739aSdsl 		}
1011c9f333adSkre 	} else if (flag & EXP_IN_QUOTES) {
1012c9f333adSkre 		apply_ifs = 0;
10137d60739aSdsl 	} else
10147d60739aSdsl 		apply_ifs = 1;
1015ad8d5369Schristos 
1016ad8d5369Schristos 	switch (subtype) {
1017ad8d5369Schristos 	case VSLENGTH:
1018ad8d5369Schristos 		expdest = cvtnum(varlen, expdest);
1019e4a2a056Sdsl 		break;
1020ad8d5369Schristos 
1021ad8d5369Schristos 	case VSNORMAL:
1022ad8d5369Schristos 		break;
1023ad8d5369Schristos 
1024ad8d5369Schristos 	case VSPLUS:
1025e4a2a056Sdsl 		set = !set;
1026e4a2a056Sdsl 		/* FALLTHROUGH */
1027ad8d5369Schristos 	case VSMINUS:
1028cf199667Schristos 		if (!set) {
1029c6cbc16dSdsl 			argstr(p, flag | (apply_ifs ? EXP_IFS_SPLIT : 0));
1030e4a2a056Sdsl 			/*
1031e4a2a056Sdsl 			 * ${x-a b c} doesn't get split, but removing the
10325c056f67Swiz 			 * 'apply_ifs = 0' apparently breaks ${1+"$@"}..
1033e4a2a056Sdsl 			 * ${x-'a b' c} should generate 2 args.
1034e4a2a056Sdsl 			 */
1035dec2ca90Schristos 			if (*p != CTLENDVAR)
1036e4a2a056Sdsl 			/* We should have marked stuff already */
1037e4a2a056Sdsl 				apply_ifs = 0;
1038cf199667Schristos 		}
1039ad8d5369Schristos 		break;
1040ad8d5369Schristos 
1041ad8d5369Schristos 	case VSTRIMLEFT:
1042ad8d5369Schristos 	case VSTRIMLEFTMAX:
1043ad8d5369Schristos 	case VSTRIMRIGHT:
1044ad8d5369Schristos 	case VSTRIMRIGHTMAX:
1045df029fccSkre 		if (!set) {
1046df029fccSkre 			set = 1;  /* allow argbackq to be advanced if needed */
1047ad8d5369Schristos 			break;
1048df029fccSkre 		}
1049ad8d5369Schristos 		/*
1050ad8d5369Schristos 		 * Terminate the string and start recording the pattern
1051ad8d5369Schristos 		 * right after it
1052ad8d5369Schristos 		 */
1053ad8d5369Schristos 		STPUTC('\0', expdest);
1054e97b0193She 		patloc = expdest - stackblock();
1055dd6b6414Skre 		if (subevalvar_trim(p, patloc, subtype, startloc, varflags,
1056dd6b6414Skre 		    quotes) == 0) {
1057e97b0193She 			int amount = (expdest - stackblock() - patloc) + 1;
1058633ceb6dSchristos 			STADJUST(-amount, expdest);
1059633ceb6dSchristos 		}
10601fbf0781Smycroft 		/* Remove any recorded regions beyond start of variable */
10611fbf0781Smycroft 		removerecordregions(startloc);
1062e4a2a056Sdsl 		apply_ifs = 1;
1063e4a2a056Sdsl 		break;
1064ad8d5369Schristos 
1065ad8d5369Schristos 	case VSASSIGN:
1066ad8d5369Schristos 	case VSQUESTION:
1067e4a2a056Sdsl 		if (set)
1068e4a2a056Sdsl 			break;
1069dd6b6414Skre 		if (subevalvar(p, var, subtype, startloc, varflags)) {
1070dd6b6414Skre 			/* if subevalvar() returns, it always returns 1 */
1071dd6b6414Skre 
107229cad877Schristos 			varflags &= ~VSNUL;
10731fbf0781Smycroft 			/*
10741fbf0781Smycroft 			 * Remove any recorded regions beyond
10751fbf0781Smycroft 			 * start of variable
10761fbf0781Smycroft 			 */
10771fbf0781Smycroft 			removerecordregions(startloc);
107861f28255Scgd 			goto again;
107929cad877Schristos 		}
1080ee3b307fSkre 		apply_ifs = 0;		/* never executed */
1081ad8d5369Schristos 		break;
1082ad8d5369Schristos 
1083ad8d5369Schristos 	default:
1084ad8d5369Schristos 		abort();
108561f28255Scgd 	}
1086ad8d5369Schristos 
1087e4a2a056Sdsl 	if (apply_ifs)
1088e4a2a056Sdsl 		recordregion(startloc, expdest - stackblock(),
1089e4a2a056Sdsl 			     varflags & VSQUOTE);
1090e4a2a056Sdsl 
109161f28255Scgd 	if (subtype != VSNORMAL) {	/* skip to end of alternative */
109261f28255Scgd 		int nesting = 1;
109361f28255Scgd 		for (;;) {
109461f28255Scgd 			if ((c = *p++) == CTLESC)
109561f28255Scgd 				p++;
1096727a69dcSkre 			else if (c == CTLNONL)
1097727a69dcSkre 				;
109861f28255Scgd 			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
1099c823b55cSmycroft 				if (set)
110061f28255Scgd 					argbackq = argbackq->next;
110161f28255Scgd 			} else if (c == CTLVAR) {
110261f28255Scgd 				if ((*p++ & VSTYPE) != VSNORMAL)
110361f28255Scgd 					nesting++;
110461f28255Scgd 			} else if (c == CTLENDVAR) {
110561f28255Scgd 				if (--nesting == 0)
110661f28255Scgd 					break;
110761f28255Scgd 			}
110861f28255Scgd 		}
110961f28255Scgd 	}
111061f28255Scgd 	return p;
111161f28255Scgd }
111261f28255Scgd 
111361f28255Scgd 
111461f28255Scgd 
111561f28255Scgd /*
1116ee3b307fSkre  * Test whether a special parameter is set.
111761f28255Scgd  */
111861f28255Scgd 
111961f28255Scgd STATIC int
1120dd6b6414Skre varisset(const char *name, int nulok)
112161f28255Scgd {
1122633ceb6dSchristos 	if (*name == '!')
1123633ceb6dSchristos 		return backgndpid != -1;
1124633ceb6dSchristos 	else if (*name == '@' || *name == '*') {
112561f28255Scgd 		if (*shellparam.p == NULL)
112661f28255Scgd 			return 0;
1127633ceb6dSchristos 
1128633ceb6dSchristos 		if (nulok) {
11294a521d35Schristos 			char **av;
11304a521d35Schristos 
11314a521d35Schristos 			for (av = shellparam.p; *av; av++)
11324a521d35Schristos 				if (**av != '\0')
1133633ceb6dSchristos 					return 1;
1134633ceb6dSchristos 			return 0;
1135633ceb6dSchristos 		}
11364b99c4d7Schristos 	} else if (is_digit(*name)) {
11374a521d35Schristos 		char *ap;
1138ee3b307fSkre 		long num;
1139ee3b307fSkre 
1140ee3b307fSkre 		/*
1141ee3b307fSkre 		 * handle overflow sensibly (the *ap tests should never fail)
1142ee3b307fSkre 		 */
1143ee3b307fSkre 		errno = 0;
1144ee3b307fSkre 		num = strtol(name, &ap, 10);
1145ee3b307fSkre 		if (errno != 0 || (*ap != '\0' && *ap != '='))
1146ee3b307fSkre 			return 0;
1147633ceb6dSchristos 
11484a521d35Schristos 		if (num == 0)
11494a521d35Schristos 			ap = arg0;
1150dd6b6414Skre 		else if (num > shellparam.nparam)
1151dd6b6414Skre 			return 0;
11524a521d35Schristos 		else
11534a521d35Schristos 			ap = shellparam.p[num - 1];
11544a521d35Schristos 
11554a521d35Schristos 		if (nulok && (ap == NULL || *ap == '\0'))
115661f28255Scgd 			return 0;
115761f28255Scgd 	}
115861f28255Scgd 	return 1;
115961f28255Scgd }
116061f28255Scgd 
116161f28255Scgd 
116261f28255Scgd 
116361f28255Scgd /*
116461f28255Scgd  * Add the value of a specialized variable to the stack string.
116561f28255Scgd  */
116661f28255Scgd 
116761f28255Scgd STATIC void
1168dd6b6414Skre varvalue(const char *name, int quoted, int subtype, int flag)
116961f28255Scgd {
117061f28255Scgd 	int num;
117161f28255Scgd 	char *p;
117261f28255Scgd 	int i;
1173c83568a7Skre 	int sep;
117461f28255Scgd 	char **ap;
1175c9f333adSkre #ifdef DEBUG
1176c9f333adSkre 	char *start = expdest;
1177c9f333adSkre #endif
1178c9f333adSkre 
1179c9f333adSkre 	VTRACE(DBG_EXPAND, ("varvalue(%c%s, sub=%d, fl=%#x)", *name,
1180c9f333adSkre 	    quoted ? ", quoted" : "", subtype, flag));
118161f28255Scgd 
1182dd6b6414Skre 	if (subtype == VSLENGTH)	/* no magic required ... */
1183dd6b6414Skre 		flag &= ~EXP_FULL;
1184dd6b6414Skre 
118537ed7877Sjtc #define STRTODEST(p) \
118637ed7877Sjtc 	do {\
1187df073671Skre 		if ((flag & EXP_QNEEDED) && quoted) { \
118837ed7877Sjtc 			while (*p) { \
1189df073671Skre 				if (NEEDESC(*p)) \
119037ed7877Sjtc 					STPUTC(CTLESC, expdest); \
119137ed7877Sjtc 				STPUTC(*p++, expdest); \
119237ed7877Sjtc 			} \
119337ed7877Sjtc 		} else \
1194256d645dSkre 			while (*p) { \
1195256d645dSkre 				if (ISCTL(*p)) \
1196256d645dSkre 					STPUTC(CTLESC, expdest); \
119737ed7877Sjtc 				STPUTC(*p++, expdest); \
1198256d645dSkre 			} \
119937ed7877Sjtc 	} while (0)
120037ed7877Sjtc 
120137ed7877Sjtc 
12024b99c4d7Schristos 	switch (*name) {
120361f28255Scgd 	case '$':
120461f28255Scgd 		num = rootpid;
1205dd6b6414Skre 		break;
120661f28255Scgd 	case '?':
1207c02b3bbdSchristos 		num = exitstatus;
1208dd6b6414Skre 		break;
120961f28255Scgd 	case '#':
121061f28255Scgd 		num = shellparam.nparam;
1211dd6b6414Skre 		break;
121261f28255Scgd 	case '!':
121361f28255Scgd 		num = backgndpid;
121461f28255Scgd 		break;
121561f28255Scgd 	case '-':
1216f359a311Skre 		for (i = 0; i < option_flags; i++) {
1217f359a311Skre 			if (optlist[optorder[i]].val)
1218f359a311Skre 				STPUTC(optlist[optorder[i]].letter, expdest);
121961f28255Scgd 		}
1220c9f333adSkre 		VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start));
1221dd6b6414Skre 		return;
122261f28255Scgd 	case '@':
1223dd6b6414Skre 		if (flag & EXP_SPLIT && quoted) {
1224c9f333adSkre 			VTRACE(DBG_EXPAND, (": $@ split (%d)\n",
1225c9f333adSkre 			    shellparam.nparam));
1226265b0617Skre #if 0
1227c9f333adSkre 		/* GROSS HACK */
1228c9f333adSkre 			if (shellparam.nparam == 0 &&
1229c9f333adSkre 				expdest[-1] == CTLQUOTEMARK)
1230c9f333adSkre 					expdest--;
1231c9f333adSkre 		/* KCAH SSORG */
1232265b0617Skre #endif
1233265b0617Skre 			if (shellparam.nparam == 0)
1234265b0617Skre 				empty_dollar_at = 1;
1235265b0617Skre 
123661f28255Scgd 			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
1237265b0617Skre 				if (*p == '\0') {
1238265b0617Skre 					/* retain an explicit null string */
1239265b0617Skre 					STPUTC(CTLQUOTEMARK, expdest);
1240265b0617Skre 					STPUTC(CTLQUOTEEND, expdest);
1241265b0617Skre 				} else
124237ed7877Sjtc 					STRTODEST(p);
1243413b7762Sdsl 				if (*ap)
1244413b7762Sdsl 					/* A NUL separates args inside "" */
12451fbf0781Smycroft 					STPUTC('\0', expdest);
12461fbf0781Smycroft 			}
1247dd6b6414Skre 			return;
12481fbf0781Smycroft 		}
12491fbf0781Smycroft 		/* fall through */
12501fbf0781Smycroft 	case '*':
12511fbf0781Smycroft 		sep = ifsval()[0];
12521fbf0781Smycroft 		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
12531fbf0781Smycroft 			STRTODEST(p);
1254bda1f28dSkre 			if (!*ap)
1255bda1f28dSkre 				break;
1256c83568a7Skre 			if (sep) {
1257df073671Skre 				if (quoted && (flag & EXP_QNEEDED) &&
1258df073671Skre 				    NEEDESC(sep))
1259c83568a7Skre 					STPUTC(CTLESC, expdest);
126061f28255Scgd 				STPUTC(sep, expdest);
1261c83568a7Skre 			} else
1262c83568a7Skre 			    if ((flag & (EXP_SPLIT|EXP_IN_QUOTES)) == EXP_SPLIT
1263bda1f28dSkre 			      && !quoted && **ap != '\0')
1264bda1f28dSkre 				STPUTC('\0', expdest);
126561f28255Scgd 		}
1266c9f333adSkre 		VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start));
1267dd6b6414Skre 		return;
126861f28255Scgd 	default:
12694b99c4d7Schristos 		if (is_digit(*name)) {
1270dd6b6414Skre 			long lnum;
1271dd6b6414Skre 
1272dd6b6414Skre 			errno = 0;
1273dd6b6414Skre 			lnum = strtol(name, &p, 10);
1274dd6b6414Skre 			if (errno != 0 || (*p != '\0' && *p != '='))
1275dd6b6414Skre 				return;
1276dd6b6414Skre 
1277dd6b6414Skre 			if (lnum == 0)
1278dd6b6414Skre 				p = arg0;
1279dd6b6414Skre 			else if (lnum > 0 && lnum <= shellparam.nparam)
1280dd6b6414Skre 				p = shellparam.p[lnum - 1];
1281dd6b6414Skre 			else
1282dd6b6414Skre 				return;
128337ed7877Sjtc 			STRTODEST(p);
128461f28255Scgd 		}
1285c9f333adSkre 		VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start));
1286dd6b6414Skre 		return;
12874b99c4d7Schristos 	}
1288dd6b6414Skre 	/*
1289dd6b6414Skre 	 * only the specials with an int value arrive here
1290dd6b6414Skre 	 */
1291c9f333adSkre 	VTRACE(DBG_EXPAND, ("(%d)", num));
1292dd6b6414Skre 	expdest = cvtnum(num, expdest);
1293c9f333adSkre 	VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start));
129461f28255Scgd }
129561f28255Scgd 
129661f28255Scgd 
129761f28255Scgd 
129861f28255Scgd /*
12991594850fSsoren  * Record the fact that we have to scan this region of the
130061f28255Scgd  * string for IFS characters.
130161f28255Scgd  */
130261f28255Scgd 
130361f28255Scgd STATIC void
1304e4a2a056Sdsl recordregion(int start, int end, int inquotes)
13055dad1439Scgd {
130648250187Stls 	struct ifsregion *ifsp;
130761f28255Scgd 
130858810bf0Skre 	VTRACE(DBG_EXPAND, ("recordregion(%d,%d,%d)\n", start, end, inquotes));
130961f28255Scgd 	if (ifslastp == NULL) {
131061f28255Scgd 		ifsp = &ifsfirst;
131161f28255Scgd 	} else {
13122078d2c0Sdsl 		if (ifslastp->endoff == start
13132078d2c0Sdsl 		    && ifslastp->inquotes == inquotes) {
1314c6cbc16dSdsl 			/* extend previous area */
1315c6cbc16dSdsl 			ifslastp->endoff = end;
1316c6cbc16dSdsl 			return;
1317c6cbc16dSdsl 		}
131861f28255Scgd 		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
131961f28255Scgd 		ifslastp->next = ifsp;
132061f28255Scgd 	}
132161f28255Scgd 	ifslastp = ifsp;
132261f28255Scgd 	ifslastp->next = NULL;
132361f28255Scgd 	ifslastp->begoff = start;
132461f28255Scgd 	ifslastp->endoff = end;
1325e4a2a056Sdsl 	ifslastp->inquotes = inquotes;
132661f28255Scgd }
132761f28255Scgd 
132861f28255Scgd 
132961f28255Scgd 
133061f28255Scgd /*
133161f28255Scgd  * Break the argument string into pieces based upon IFS and add the
133261f28255Scgd  * strings to the argument list.  The regions of the string to be
133361f28255Scgd  * searched for IFS characters have been stored by recordregion.
133461f28255Scgd  */
133561f28255Scgd STATIC void
1336c02b3bbdSchristos ifsbreakup(char *string, struct arglist *arglist)
133761f28255Scgd {
133861f28255Scgd 	struct ifsregion *ifsp;
133961f28255Scgd 	struct strlist *sp;
134061f28255Scgd 	char *start;
134148250187Stls 	char *p;
134261f28255Scgd 	char *q;
13433d424690Schristos 	const char *ifs;
1344e4a2a056Sdsl 	const char *ifsspc;
1345413b7762Sdsl 	int had_param_ch = 0;
134661f28255Scgd 
134761f28255Scgd 	start = string;
1348e4a2a056Sdsl 
1349ea89b130Skre 	VTRACE(DBG_EXPAND, ("ifsbreakup(\"%s\")", string)); /* misses \0's */
1350e4a2a056Sdsl 	if (ifslastp == NULL) {
1351e4a2a056Sdsl 		/* Return entire argument, IFS doesn't apply to any of it */
1352ea89b130Skre 		VTRACE(DBG_EXPAND, ("no regions\n", string));
135378204bbfSchristos 		sp = stalloc(sizeof(*sp));
1354e4a2a056Sdsl 		sp->text = start;
1355e4a2a056Sdsl 		*arglist->lastp = sp;
1356e4a2a056Sdsl 		arglist->lastp = &sp->next;
1357e4a2a056Sdsl 		return;
1358e4a2a056Sdsl 	}
1359e4a2a056Sdsl 
1360ca12a0b8Schristos 	ifs = ifsval();
1361e4a2a056Sdsl 
1362e4a2a056Sdsl 	for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
136361f28255Scgd 		p = string + ifsp->begoff;
1364ea89b130Skre 		VTRACE(DBG_EXPAND, (" !%.*s!(%d)", ifsp->endoff-ifsp->begoff,
1365ea89b130Skre 		    p, ifsp->endoff-ifsp->begoff));
136661f28255Scgd 		while (p < string + ifsp->endoff) {
1367413b7762Sdsl 			had_param_ch = 1;
136861f28255Scgd 			q = p;
1369c9f333adSkre 			if (IS_BORING(*p)) {
1370727a69dcSkre 				p++;
1371727a69dcSkre 				continue;
1372727a69dcSkre 			}
137361f28255Scgd 			if (*p == CTLESC)
137461f28255Scgd 				p++;
1375413b7762Sdsl 			if (ifsp->inquotes) {
1376413b7762Sdsl 				/* Only NULs (should be from "$@") end args */
1377e4a2a056Sdsl 				if (*p != 0) {
1378e4a2a056Sdsl 					p++;
1379e4a2a056Sdsl 					continue;
1380e4a2a056Sdsl 				}
1381413b7762Sdsl 				ifsspc = NULL;
1382ea89b130Skre 				VTRACE(DBG_EXPAND, (" \\0 nxt:\"%s\" ", p));
1383e4a2a056Sdsl 			} else {
1384e4a2a056Sdsl 				if (!strchr(ifs, *p)) {
1385e4a2a056Sdsl 					p++;
1386e4a2a056Sdsl 					continue;
1387e4a2a056Sdsl 				}
1388c58b829dSdsl 				had_param_ch = 0;
1389e4a2a056Sdsl 				ifsspc = strchr(" \t\n", *p);
1390e4a2a056Sdsl 
13911fbf0781Smycroft 				/* Ignore IFS whitespace at start */
1392e4a2a056Sdsl 				if (q == start && ifsspc != NULL) {
13931fbf0781Smycroft 					p++;
13941fbf0781Smycroft 					start = p;
13951fbf0781Smycroft 					continue;
13961fbf0781Smycroft 				}
1397e4a2a056Sdsl 			}
1398e4a2a056Sdsl 
1399e4a2a056Sdsl 			/* Save this argument... */
140061f28255Scgd 			*q = '\0';
1401ea89b130Skre 			VTRACE(DBG_EXPAND, ("<%s>", start));
140278204bbfSchristos 			sp = stalloc(sizeof(*sp));
140361f28255Scgd 			sp->text = start;
140461f28255Scgd 			*arglist->lastp = sp;
140561f28255Scgd 			arglist->lastp = &sp->next;
14061fbf0781Smycroft 			p++;
1407e4a2a056Sdsl 
1408328c4365Sdsl 			if (ifsspc != NULL) {
1409328c4365Sdsl 				/* Ignore further trailing IFS whitespace */
1410e4a2a056Sdsl 				for (; p < string + ifsp->endoff; p++) {
141161f28255Scgd 					q = p;
1412727a69dcSkre 					if (*p == CTLNONL)
1413727a69dcSkre 						continue;
141461f28255Scgd 					if (*p == CTLESC)
141561f28255Scgd 						p++;
14161fbf0781Smycroft 					if (strchr(ifs, *p) == NULL) {
14171fbf0781Smycroft 						p = q;
14181fbf0781Smycroft 						break;
1419e4a2a056Sdsl 					}
1420e4a2a056Sdsl 					if (strchr(" \t\n", *p) == NULL) {
1421328c4365Sdsl 						p++;
142261f28255Scgd 						break;
142361f28255Scgd 					}
142461f28255Scgd 				}
142561f28255Scgd 			}
142661f28255Scgd 			start = p;
142761f28255Scgd 		}
142861f28255Scgd 	}
1429e4a2a056Sdsl 
1430265b0617Skre /*
1431c9f333adSkre 	while (*start == CTLQUOTEEND)
1432c9f333adSkre 		start++;
1433265b0617Skre */
1434c9f333adSkre 
1435328c4365Sdsl 	/*
1436328c4365Sdsl 	 * Save anything left as an argument.
1437328c4365Sdsl 	 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
1438328c4365Sdsl 	 * generating 2 arguments, the second of which is empty.
1439328c4365Sdsl 	 * Some recent clarification of the Posix spec say that it
1440328c4365Sdsl 	 * should only generate one....
1441328c4365Sdsl 	 */
1442413b7762Sdsl 	if (had_param_ch || *start != 0) {
1443ea89b130Skre 		VTRACE(DBG_EXPAND, (" T<%s>", start));
144478204bbfSchristos 		sp = stalloc(sizeof(*sp));
144561f28255Scgd 		sp->text = start;
144661f28255Scgd 		*arglist->lastp = sp;
144761f28255Scgd 		arglist->lastp = &sp->next;
144861f28255Scgd 	}
1449ea89b130Skre 	VTRACE(DBG_EXPAND, ("\n"));
145061f28255Scgd }
145161f28255Scgd 
1452f7c8df6dSchristos STATIC void
1453c02b3bbdSchristos ifsfree(void)
1454f7c8df6dSchristos {
1455f7c8df6dSchristos 	while (ifsfirst.next != NULL) {
1456f7c8df6dSchristos 		struct ifsregion *ifsp;
1457f7c8df6dSchristos 		INTOFF;
1458f7c8df6dSchristos 		ifsp = ifsfirst.next->next;
1459f7c8df6dSchristos 		ckfree(ifsfirst.next);
1460f7c8df6dSchristos 		ifsfirst.next = ifsp;
1461f7c8df6dSchristos 		INTON;
1462f7c8df6dSchristos 	}
1463f7c8df6dSchristos 	ifslastp = NULL;
1464f7c8df6dSchristos 	ifsfirst.next = NULL;
1465f7c8df6dSchristos }
1466f7c8df6dSchristos 
146761f28255Scgd 
146861f28255Scgd 
146961f28255Scgd /*
147061f28255Scgd  * Expand shell metacharacters.  At this point, the only control characters
147161f28255Scgd  * should be escapes.  The results are stored in the list exparg.
147261f28255Scgd  */
147361f28255Scgd 
147461f28255Scgd char *expdir;
147561f28255Scgd 
147661f28255Scgd 
147761f28255Scgd STATIC void
1478c02b3bbdSchristos expandmeta(struct strlist *str, int flag)
147961f28255Scgd {
148061f28255Scgd 	char *p;
148161f28255Scgd 	struct strlist **savelastp;
148261f28255Scgd 	struct strlist *sp;
148361f28255Scgd 	char c;
148437ed7877Sjtc 	/* TODO - EXP_REDIR */
148561f28255Scgd 
148661f28255Scgd 	while (str) {
148761f28255Scgd 		p = str->text;
148861f28255Scgd 		for (;;) {			/* fast check for meta chars */
148961f28255Scgd 			if ((c = *p++) == '\0')
149061f28255Scgd 				goto nometa;
1491c9f333adSkre 			if (c == '*' || c == '?' || c == '[' /* || c == '!' */)
149261f28255Scgd 				break;
149361f28255Scgd 		}
149461f28255Scgd 		savelastp = exparg.lastp;
149561f28255Scgd 		INTOFF;
149637ed7877Sjtc 		if (expdir == NULL) {
149737ed7877Sjtc 			int i = strlen(str->text);
149837ed7877Sjtc 			expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
149937ed7877Sjtc 		}
150037ed7877Sjtc 
150161f28255Scgd 		expmeta(expdir, str->text);
150261f28255Scgd 		ckfree(expdir);
150361f28255Scgd 		expdir = NULL;
150461f28255Scgd 		INTON;
150561f28255Scgd 		if (exparg.lastp == savelastp) {
150637ed7877Sjtc 			/*
150737ed7877Sjtc 			 * no matches
150837ed7877Sjtc 			 */
150961f28255Scgd  nometa:
151061f28255Scgd 			*exparg.lastp = str;
151161f28255Scgd 			rmescapes(str->text);
151261f28255Scgd 			exparg.lastp = &str->next;
151361f28255Scgd 		} else {
151461f28255Scgd 			*exparg.lastp = NULL;
151561f28255Scgd 			*savelastp = sp = expsort(*savelastp);
151661f28255Scgd 			while (sp->next != NULL)
151761f28255Scgd 				sp = sp->next;
151861f28255Scgd 			exparg.lastp = &sp->next;
151961f28255Scgd 		}
152061f28255Scgd 		str = str->next;
152161f28255Scgd 	}
152261f28255Scgd }
152361f28255Scgd 
1524dd6b6414Skre STATIC void
1525dd6b6414Skre add_args(struct strlist *str)
1526dd6b6414Skre {
1527dd6b6414Skre 	while (str) {
1528dd6b6414Skre 		*exparg.lastp = str;
1529dd6b6414Skre 		rmescapes(str->text);
1530dd6b6414Skre 		exparg.lastp = &str->next;
1531dd6b6414Skre 		str = str->next;
1532dd6b6414Skre 	}
1533dd6b6414Skre }
1534dd6b6414Skre 
153561f28255Scgd 
153661f28255Scgd /*
153761f28255Scgd  * Do metacharacter (i.e. *, ?, [...]) expansion.
153861f28255Scgd  */
153961f28255Scgd 
154061f28255Scgd STATIC void
1541c02b3bbdSchristos expmeta(char *enddir, char *name)
154261f28255Scgd {
154348250187Stls 	char *p;
15443d424690Schristos 	const char *cp;
154561f28255Scgd 	char *q;
154661f28255Scgd 	char *start;
154761f28255Scgd 	char *endname;
154861f28255Scgd 	int metaflag;
154961f28255Scgd 	struct stat statb;
155061f28255Scgd 	DIR *dirp;
155161f28255Scgd 	struct dirent *dp;
155261f28255Scgd 	int atend;
155361f28255Scgd 	int matchdot;
155461f28255Scgd 
1555bcacfd9aSkre 	CTRACE(DBG_EXPAND|DBG_MATCH, ("expmeta(\"%s\")\n", name));
155661f28255Scgd 	metaflag = 0;
155761f28255Scgd 	start = name;
155861f28255Scgd 	for (p = name ; ; p++) {
155961f28255Scgd 		if (*p == '*' || *p == '?')
156061f28255Scgd 			metaflag = 1;
156161f28255Scgd 		else if (*p == '[') {
156261f28255Scgd 			q = p + 1;
156314482abcSkre 			if (*q == '!' || *q == '^')
156461f28255Scgd 				q++;
156561f28255Scgd 			for (;;) {
1566c9f333adSkre 				while (IS_BORING(*q))
15671fbf0781Smycroft 					q++;
156814482abcSkre 				if (*q == ']') {
156914482abcSkre 					q++;
157014482abcSkre 					metaflag = 1;
157114482abcSkre 					break;
157214482abcSkre 				}
157314482abcSkre 				if (*q == '[' && q[1] == ':') {
157414482abcSkre 					/*
157514482abcSkre 					 * character class, look for :] ending
157614482abcSkre 					 * also stop on ']' (end bracket expr)
157714482abcSkre 					 * or '\0' or '/' (end pattern)
157814482abcSkre 					 */
157914482abcSkre 					while (*++q != '\0' && *q != ']' &&
158014482abcSkre 					    *q != '/') {
158114482abcSkre 						if (*q == CTLESC) {
158214482abcSkre 							if (*++q == '\0')
158314482abcSkre 								break;
158414482abcSkre 							if (*q == '/')
158514482abcSkre 								break;
158614482abcSkre 						} else if (*q == ':' &&
158714482abcSkre 						    q[1] == ']')
158814482abcSkre 							break;
158914482abcSkre 					}
159014482abcSkre 					if (*q == ':') {
159114482abcSkre 						/*
159214482abcSkre 						 * stopped at ':]'
159314482abcSkre 						 * still in [...]
159414482abcSkre 						 * skip ":]" and continue;
159514482abcSkre 						 */
159614482abcSkre 						q += 2;
159714482abcSkre 						continue;
159814482abcSkre 					}
159914482abcSkre 
160014482abcSkre 					/* done at end of pattern, not [...] */
160114482abcSkre 					if (*q == '\0' || *q == '/')
160214482abcSkre 						break;
160314482abcSkre 
160414482abcSkre 					/* found the ']', we have a [...] */
160514482abcSkre 					metaflag = 1;
160614482abcSkre 					q++;	/* skip ']' */
160714482abcSkre 					break;
160814482abcSkre 				}
160961f28255Scgd 				if (*q == CTLESC)
161061f28255Scgd 					q++;
161114482abcSkre 				/* end of pattern cannot be escaped */
161261f28255Scgd 				if (*q == '/' || *q == '\0')
161361f28255Scgd 					break;
161414482abcSkre 				q++;
161561f28255Scgd 			}
161661f28255Scgd 		} else if (*p == '\0')
161761f28255Scgd 			break;
1618c9f333adSkre 		else if (IS_BORING(*p))
16191fbf0781Smycroft 			continue;
162061f28255Scgd 		else if (*p == CTLESC)
162161f28255Scgd 			p++;
162261f28255Scgd 		if (*p == '/') {
162361f28255Scgd 			if (metaflag)
162461f28255Scgd 				break;
162561f28255Scgd 			start = p + 1;
162661f28255Scgd 		}
162761f28255Scgd 	}
162861f28255Scgd 	if (metaflag == 0) {	/* we've reached the end of the file name */
162961f28255Scgd 		if (enddir != expdir)
163061f28255Scgd 			metaflag++;
163161f28255Scgd 		for (p = name ; ; p++) {
1632c9f333adSkre 			if (IS_BORING(*p))
16331fbf0781Smycroft 				continue;
163461f28255Scgd 			if (*p == CTLESC)
163561f28255Scgd 				p++;
163661f28255Scgd 			*enddir++ = *p;
163761f28255Scgd 			if (*p == '\0')
163861f28255Scgd 				break;
163961f28255Scgd 		}
164059771e30Smycroft 		if (metaflag == 0 || lstat(expdir, &statb) >= 0)
164161f28255Scgd 			addfname(expdir);
164261f28255Scgd 		return;
164361f28255Scgd 	}
164461f28255Scgd 	endname = p;
164561f28255Scgd 	if (start != name) {
164661f28255Scgd 		p = name;
164761f28255Scgd 		while (p < start) {
1648c9f333adSkre 			while (IS_BORING(*p))
16491fbf0781Smycroft 				p++;
165061f28255Scgd 			if (*p == CTLESC)
165161f28255Scgd 				p++;
165261f28255Scgd 			*enddir++ = *p++;
165361f28255Scgd 		}
165461f28255Scgd 	}
165561f28255Scgd 	if (enddir == expdir) {
16563d424690Schristos 		cp = ".";
165761f28255Scgd 	} else if (enddir == expdir + 1 && *expdir == '/') {
16583d424690Schristos 		cp = "/";
165961f28255Scgd 	} else {
16603d424690Schristos 		cp = expdir;
166161f28255Scgd 		enddir[-1] = '\0';
166261f28255Scgd 	}
16633d424690Schristos 	if ((dirp = opendir(cp)) == NULL)
166461f28255Scgd 		return;
166561f28255Scgd 	if (enddir != expdir)
166661f28255Scgd 		enddir[-1] = '/';
166761f28255Scgd 	if (*endname == 0) {
166861f28255Scgd 		atend = 1;
166961f28255Scgd 	} else {
167061f28255Scgd 		atend = 0;
167161f28255Scgd 		*endname++ = '\0';
167261f28255Scgd 	}
167361f28255Scgd 	matchdot = 0;
16741fbf0781Smycroft 	p = start;
1675c9f333adSkre 	while (IS_BORING(*p))
16761fbf0781Smycroft 		p++;
16771fbf0781Smycroft 	if (*p == CTLESC)
16781fbf0781Smycroft 		p++;
16791fbf0781Smycroft 	if (*p == '.')
168061f28255Scgd 		matchdot++;
168161f28255Scgd 	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
168261f28255Scgd 		if (dp->d_name[0] == '.' && ! matchdot)
168361f28255Scgd 			continue;
168428607542Schristos 		if (patmatch(start, dp->d_name, 0)) {
168561f28255Scgd 			if (atend) {
168661f28255Scgd 				scopy(dp->d_name, enddir);
168761f28255Scgd 				addfname(expdir);
168861f28255Scgd 			} else {
16893d424690Schristos 				for (p = enddir, cp = dp->d_name;
16903d424690Schristos 				     (*p++ = *cp++) != '\0';)
169107bae7edSchristos 					continue;
169261f28255Scgd 				p[-1] = '/';
169361f28255Scgd 				expmeta(p, endname);
169461f28255Scgd 			}
169561f28255Scgd 		}
169661f28255Scgd 	}
169761f28255Scgd 	closedir(dirp);
169861f28255Scgd 	if (! atend)
169961f28255Scgd 		endname[-1] = '/';
170061f28255Scgd }
170161f28255Scgd 
170261f28255Scgd 
170361f28255Scgd /*
170461f28255Scgd  * Add a file name to the list.
170561f28255Scgd  */
170661f28255Scgd 
170761f28255Scgd STATIC void
1708c02b3bbdSchristos addfname(char *name)
170961f28255Scgd {
171061f28255Scgd 	char *p;
171161f28255Scgd 	struct strlist *sp;
171261f28255Scgd 
171361f28255Scgd 	p = stalloc(strlen(name) + 1);
171461f28255Scgd 	scopy(name, p);
171578204bbfSchristos 	sp = stalloc(sizeof(*sp));
171661f28255Scgd 	sp->text = p;
171761f28255Scgd 	*exparg.lastp = sp;
171861f28255Scgd 	exparg.lastp = &sp->next;
171961f28255Scgd }
172061f28255Scgd 
172161f28255Scgd 
172261f28255Scgd /*
172361f28255Scgd  * Sort the results of file name expansion.  It calculates the number of
172461f28255Scgd  * strings to sort and then calls msort (short for merge sort) to do the
172561f28255Scgd  * work.
172661f28255Scgd  */
172761f28255Scgd 
172861f28255Scgd STATIC struct strlist *
1729c02b3bbdSchristos expsort(struct strlist *str)
173061f28255Scgd {
173161f28255Scgd 	int len;
173261f28255Scgd 	struct strlist *sp;
173361f28255Scgd 
173461f28255Scgd 	len = 0;
173561f28255Scgd 	for (sp = str ; sp ; sp = sp->next)
173661f28255Scgd 		len++;
173761f28255Scgd 	return msort(str, len);
173861f28255Scgd }
173961f28255Scgd 
174061f28255Scgd 
174161f28255Scgd STATIC struct strlist *
1742c02b3bbdSchristos msort(struct strlist *list, int len)
174361f28255Scgd {
17444b99c4d7Schristos 	struct strlist *p, *q = NULL;
174561f28255Scgd 	struct strlist **lpp;
174661f28255Scgd 	int half;
174761f28255Scgd 	int n;
174861f28255Scgd 
174961f28255Scgd 	if (len <= 1)
175061f28255Scgd 		return list;
175161f28255Scgd 	half = len >> 1;
175261f28255Scgd 	p = list;
175361f28255Scgd 	for (n = half ; --n >= 0 ; ) {
175461f28255Scgd 		q = p;
175561f28255Scgd 		p = p->next;
175661f28255Scgd 	}
175761f28255Scgd 	q->next = NULL;			/* terminate first half of list */
175861f28255Scgd 	q = msort(list, half);		/* sort first half of list */
175961f28255Scgd 	p = msort(p, len - half);		/* sort second half */
176061f28255Scgd 	lpp = &list;
176161f28255Scgd 	for (;;) {
176261f28255Scgd 		if (strcmp(p->text, q->text) < 0) {
176361f28255Scgd 			*lpp = p;
176461f28255Scgd 			lpp = &p->next;
176561f28255Scgd 			if ((p = *lpp) == NULL) {
176661f28255Scgd 				*lpp = q;
176761f28255Scgd 				break;
176861f28255Scgd 			}
176961f28255Scgd 		} else {
177061f28255Scgd 			*lpp = q;
177161f28255Scgd 			lpp = &q->next;
177261f28255Scgd 			if ((q = *lpp) == NULL) {
177361f28255Scgd 				*lpp = p;
177461f28255Scgd 				break;
177561f28255Scgd 			}
177661f28255Scgd 		}
177761f28255Scgd 	}
177861f28255Scgd 	return list;
177961f28255Scgd }
178061f28255Scgd 
178161f28255Scgd 
17827969ec4dSroy /*
17837969ec4dSroy  * See if a character matches a character class, starting at the first colon
17847969ec4dSroy  * of "[:class:]".
17857969ec4dSroy  * If a valid character class is recognized, a pointer to the next character
17867969ec4dSroy  * after the final closing bracket is stored into *end, otherwise a null
17877969ec4dSroy  * pointer is stored into *end.
17887969ec4dSroy  */
17897969ec4dSroy static int
17908e4a570fSchristos match_charclass(const char *p, wchar_t chr, const char **end)
17917969ec4dSroy {
17927969ec4dSroy 	char name[20];
17937969ec4dSroy 	char *nameend;
17947969ec4dSroy 	wctype_t cclass;
17957dca2b7eSkre 	char *q;
17967969ec4dSroy 
17977969ec4dSroy 	*end = NULL;
17987969ec4dSroy 	p++;
17997dca2b7eSkre 	q = &name[0];
18007969ec4dSroy 	nameend = strstr(p, ":]");
1801b81009ceSkre 	if (nameend == NULL || nameend == p)	/* not a valid class */
18027969ec4dSroy 		return 0;
1803b81009ceSkre 
18047dca2b7eSkre 	if (*p == CTLESC) {
18057dca2b7eSkre 		if (*++p == CTLESC)
1806b81009ceSkre 			return 0;
18077dca2b7eSkre 		if (p == nameend)
18087dca2b7eSkre 			return 0;
18097dca2b7eSkre 	}
18107dca2b7eSkre 	if (!is_alpha(*p))
18117dca2b7eSkre 		return 0;
18127dca2b7eSkre 	while (p < nameend) {
18137dca2b7eSkre 		if (*p == CTLESC) {
18147dca2b7eSkre 			p++;
18157dca2b7eSkre 			if (p == nameend)
18167dca2b7eSkre 				return 0;
18177dca2b7eSkre 		}
18187dca2b7eSkre 		if (!is_in_name(*p))	/* '_' is a local extension */
18197dca2b7eSkre 			return 0;
18207dca2b7eSkre 		if (q < &name[sizeof name])
18217dca2b7eSkre 			*q++ = *p++;
18227dca2b7eSkre 		else
18237dca2b7eSkre 			p++;
18247dca2b7eSkre 	}
1825b81009ceSkre 
1826b81009ceSkre 	*end = nameend + 2;		/* committed to it being a char class */
18277dca2b7eSkre 
18287dca2b7eSkre 	if (q < &name[sizeof name])	/* a usable name found */
18297dca2b7eSkre 		*q++ = '\0';
18307dca2b7eSkre 	else				/* too long, valid, but no match */
18317dca2b7eSkre 		return 0;
18327dca2b7eSkre 
18337969ec4dSroy 	cclass = wctype(name);
18347969ec4dSroy 	/* An unknown class matches nothing but is valid nevertheless. */
18357969ec4dSroy 	if (cclass == 0)
18367969ec4dSroy 		return 0;
18377969ec4dSroy 	return iswctype(chr, cclass);
18387969ec4dSroy }
18397969ec4dSroy 
18407969ec4dSroy 
184161f28255Scgd /*
184261f28255Scgd  * Returns true if the pattern matches the string.
184361f28255Scgd  */
184461f28255Scgd 
184561f28255Scgd STATIC int
18468e4a570fSchristos patmatch(const char *pattern, const char *string, int squoted)
184761f28255Scgd {
18488e4a570fSchristos 	const char *p, *q, *end;
18498e4a570fSchristos 	const char *bt_p, *bt_q;
185048250187Stls 	char c;
18518e4a570fSchristos 	wchar_t wc, wc2;
185261f28255Scgd 
1853bcacfd9aSkre 	VTRACE(DBG_MATCH, ("patmatch(P=\"%s\", W=\"%s\"%s): ",
1854bcacfd9aSkre 	    pattern, string, squoted ? ", SQ" : ""));
185561f28255Scgd 	p = pattern;
185661f28255Scgd 	q = string;
18578e4a570fSchristos 	bt_p = NULL;
18588e4a570fSchristos 	bt_q = NULL;
185961f28255Scgd 	for (;;) {
186061f28255Scgd 		switch (c = *p++) {
186161f28255Scgd 		case '\0':
186214482abcSkre 			if (squoted && *q == CTLESC) {
186314482abcSkre 				if (q[1] == '\0')
186414482abcSkre 					q++;
186514482abcSkre 			}
18668e4a570fSchristos 			if (*q != '\0')
18678e4a570fSchristos 				goto backtrack;
1868bcacfd9aSkre 			VTRACE(DBG_MATCH, ("match\n"));
18698e4a570fSchristos 			return 1;
187061f28255Scgd 		case CTLESC:
187128607542Schristos 			if (squoted && *q == CTLESC)
187228607542Schristos 				q++;
187314482abcSkre 			if (*p == '\0' && *q == '\0') {
187414482abcSkre 				VTRACE(DBG_MATCH, ("match-\\\n"));
187514482abcSkre 				return 1;
187614482abcSkre 			}
187714482abcSkre 			if (*q++ != *p++)
187814482abcSkre 				goto backtrack;
187914482abcSkre 			break;
188014482abcSkre 		case '\\':
188114482abcSkre 			if (squoted && *q == CTLESC)
188214482abcSkre 				q++;
188361f28255Scgd 			if (*q++ != *p++)
18848e4a570fSchristos 				goto backtrack;
188561f28255Scgd 			break;
18861fbf0781Smycroft 		case CTLQUOTEMARK:
1887c9f333adSkre 		case CTLQUOTEEND:
1888727a69dcSkre 		case CTLNONL:
18891fbf0781Smycroft 			continue;
189061f28255Scgd 		case '?':
189128607542Schristos 			if (squoted && *q == CTLESC)
189228607542Schristos 				q++;
1893bcacfd9aSkre 			if (*q++ == '\0') {
1894bcacfd9aSkre 				VTRACE(DBG_MATCH, ("?fail\n"));
189561f28255Scgd 				return 0;
1896bcacfd9aSkre 			}
189761f28255Scgd 			break;
189861f28255Scgd 		case '*':
189961f28255Scgd 			c = *p;
19001fbf0781Smycroft 			while (c == CTLQUOTEMARK || c == '*')
19011fbf0781Smycroft 				c = *++p;
1902c9f333adSkre 			if (c != CTLESC && !IS_BORING(c) &&
19031fbf0781Smycroft 			    c != '?' && c != '*' && c != '[') {
190461f28255Scgd 				while (*q != c) {
190528607542Schristos 					if (squoted && *q == CTLESC &&
190628607542Schristos 					    q[1] == c)
190728607542Schristos 						break;
1908bcacfd9aSkre 					if (*q == '\0') {
1909bcacfd9aSkre 						VTRACE(DBG_MATCH, ("*fail\n"));
191061f28255Scgd 						return 0;
1911bcacfd9aSkre 					}
191228607542Schristos 					if (squoted && *q == CTLESC)
191328607542Schristos 						q++;
191461f28255Scgd 					q++;
191561f28255Scgd 				}
191661f28255Scgd 			}
191714482abcSkre 			if (c == CTLESC && p[1] == '\0') {
191814482abcSkre 				VTRACE(DBG_MATCH, ("match+\\\n"));
191914482abcSkre 				return 1;
192014482abcSkre 			}
19218e4a570fSchristos 			/*
19228e4a570fSchristos 			 * First try the shortest match for the '*' that
19238e4a570fSchristos 			 * could work. We can forget any earlier '*' since
19248e4a570fSchristos 			 * there is no way having it match more characters
19258e4a570fSchristos 			 * can help us, given that we are already here.
19268e4a570fSchristos 			 */
19278e4a570fSchristos 			bt_p = p;
19288e4a570fSchristos 			bt_q = q;
19298e4a570fSchristos 			break;
193061f28255Scgd 		case '[': {
19318e4a570fSchristos 			const char *savep, *saveq, *endp;
193261f28255Scgd 			int invert, found;
19338e4a570fSchristos 			unsigned char chr;
193461f28255Scgd 
1935c83568a7Skre 			/*
1936c83568a7Skre 			 * First quick check to see if there is a
1937c83568a7Skre 			 * possible matching ']' - if not, then this
1938c83568a7Skre 			 * is not a char class, and the '[' is just
1939c83568a7Skre 			 * a literal '['.
1940c83568a7Skre 			 *
1941c83568a7Skre 			 * This check will not detect all non classes, but
1942c83568a7Skre 			 * that's OK - It just means that we execute the
1943c83568a7Skre 			 * harder code sometimes when it it cannot succeed.
1944c83568a7Skre 			 */
194561f28255Scgd 			endp = p;
1946c83568a7Skre 			if (*endp == '!' || *endp == '^')
194761f28255Scgd 				endp++;
194861f28255Scgd 			for (;;) {
1949c9f333adSkre 				while (IS_BORING(*endp))
19501fbf0781Smycroft 					endp++;
195161f28255Scgd 				if (*endp == '\0')
195261f28255Scgd 					goto dft;	/* no matching ] */
1953c9f333adSkre 				if (*endp++ == ']')
195461f28255Scgd 					break;
195561f28255Scgd 			}
1956c83568a7Skre 			/* end shortcut */
1957c83568a7Skre 
19588e4a570fSchristos 			savep = p, saveq = q;
19598e4a570fSchristos 			invert = 0;
19608e4a570fSchristos 			if (*p == '!' || *p == '^') {
196161f28255Scgd 				invert++;
196261f28255Scgd 				p++;
196361f28255Scgd 			}
196461f28255Scgd 			found = 0;
1965bcacfd9aSkre 			if (*q == '\0') {
1966bcacfd9aSkre 				VTRACE(DBG_MATCH, ("[]fail\n"));
1967d22a5147Smycroft 				return 0;
1968bcacfd9aSkre 			}
196914482abcSkre 			if (squoted && *q == CTLESC)
197014482abcSkre 				q++;
19718e4a570fSchristos 			chr = (unsigned char)*q++;
197261f28255Scgd 			c = *p++;
197361f28255Scgd 			do {
1974c9f333adSkre 				if (IS_BORING(c))
19751fbf0781Smycroft 					continue;
19768e4a570fSchristos 				if (c == '\0') {
19778e4a570fSchristos 					p = savep, q = saveq;
19788e4a570fSchristos 					c = '[';
19798e4a570fSchristos 					goto dft;
19808e4a570fSchristos 				}
19817969ec4dSroy 				if (c == '[' && *p == ':') {
19827969ec4dSroy 					found |= match_charclass(p, chr, &end);
1983829cc62aSkre 					if (end != NULL) {
19847969ec4dSroy 						p = end;
1985829cc62aSkre 						continue;
1986829cc62aSkre 					}
19877969ec4dSroy 				}
1988c83568a7Skre 				if (c == CTLESC || c == '\\')
198961f28255Scgd 					c = *p++;
19908e4a570fSchristos 				wc = (unsigned char)c;
199161f28255Scgd 				if (*p == '-' && p[1] != ']') {
199261f28255Scgd 					p++;
1993c83568a7Skre 					if (*p == CTLESC || *p == '\\')
199461f28255Scgd 						p++;
19958e4a570fSchristos 					wc2 = (unsigned char)*p++;
19968e4a570fSchristos 					if (   collate_range_cmp(chr, wc) >= 0
19978e4a570fSchristos 					    && collate_range_cmp(chr, wc2) <= 0
19988e4a570fSchristos 					   )
199961f28255Scgd 						found = 1;
200061f28255Scgd 				} else {
20018e4a570fSchristos 					if (chr == wc)
200261f28255Scgd 						found = 1;
200361f28255Scgd 				}
200461f28255Scgd 			} while ((c = *p++) != ']');
200561f28255Scgd 			if (found == invert)
20068e4a570fSchristos 				goto backtrack;
200761f28255Scgd 			break;
200861f28255Scgd 		}
200961f28255Scgd   dft:		default:
201028607542Schristos 			if (squoted && *q == CTLESC)
201128607542Schristos 				q++;
20128e4a570fSchristos 			if (*q++ == c)
20138e4a570fSchristos 				break;
20148e4a570fSchristos   backtrack:
20158e4a570fSchristos 			/*
20168e4a570fSchristos 			 * If we have a mismatch (other than hitting the end
20178e4a570fSchristos 			 * of the string), go back to the last '*' seen and
20188e4a570fSchristos 			 * have it match one additional character.
20198e4a570fSchristos 			 */
2020bcacfd9aSkre 			if (bt_p == NULL) {
2021bcacfd9aSkre 				VTRACE(DBG_MATCH, ("BTP fail\n"));
202261f28255Scgd 				return 0;
2023bcacfd9aSkre 			}
2024bcacfd9aSkre 			if (*bt_q == '\0') {
2025bcacfd9aSkre 				VTRACE(DBG_MATCH, ("BTQ fail\n"));
20268e4a570fSchristos 				return 0;
2027bcacfd9aSkre 			}
20288e4a570fSchristos 			bt_q++;
20298e4a570fSchristos 			p = bt_p;
20308e4a570fSchristos 			q = bt_q;
203161f28255Scgd 			break;
203261f28255Scgd 		}
203361f28255Scgd 	}
203461f28255Scgd }
203561f28255Scgd 
203661f28255Scgd 
203761f28255Scgd 
203861f28255Scgd /*
2039727a69dcSkre  * Remove any CTLESC or CTLNONL characters from a string.
204061f28255Scgd  */
204161f28255Scgd 
204261f28255Scgd void
2043c02b3bbdSchristos rmescapes(char *str)
204461f28255Scgd {
204548250187Stls 	char *p, *q;
204661f28255Scgd 
204761f28255Scgd 	p = str;
2048256d645dSkre 	while (!ISCTL(*p)) {
204961f28255Scgd 		if (*p++ == '\0')
205061f28255Scgd 			return;
205161f28255Scgd 	}
205261f28255Scgd 	q = p;
205361f28255Scgd 	while (*p) {
2054c9f333adSkre 		if (IS_BORING(*p)) {
20551fbf0781Smycroft 			p++;
20561fbf0781Smycroft 			continue;
20571fbf0781Smycroft 		}
20585f92382cSkre 		if (*p == CTLCNL) {
20595f92382cSkre 			p++;
20605f92382cSkre 			*q++ = '\n';
20615f92382cSkre 			continue;
20625f92382cSkre 		}
206361f28255Scgd 		if (*p == CTLESC)
206461f28255Scgd 			p++;
206558e34de6Skre #ifdef DEBUG
2066256d645dSkre 		else if (ISCTL(*p))
206758e34de6Skre 			abort();
206858e34de6Skre #endif
206961f28255Scgd 		*q++ = *p++;
207061f28255Scgd 	}
207161f28255Scgd 	*q = '\0';
207261f28255Scgd }
207361f28255Scgd 
2074727a69dcSkre /*
2075727a69dcSkre  * and a special version for dealing with expressions to be parsed
2076727a69dcSkre  * by the arithmetic evaluator.   That needs to be able to count \n's
2077727a69dcSkre  * even ones that were \newline elided \n's, so we have to put the
2078727a69dcSkre  * latter back into the string - just being careful to put them only
2079727a69dcSkre  * at a place where white space can reasonably occur in the string
2080727a69dcSkre  * -- then the \n we insert will just be white space, and ignored
2081727a69dcSkre  * for all purposes except line counting.
2082727a69dcSkre  */
2083727a69dcSkre 
2084727a69dcSkre void
2085727a69dcSkre rmescapes_nl(char *str)
2086727a69dcSkre {
2087727a69dcSkre 	char *p, *q;
2088727a69dcSkre 	int nls = 0, holdnl = 0, holdlast;
2089727a69dcSkre 
2090727a69dcSkre 	p = str;
2091256d645dSkre 	while (!ISCTL(*p)) {
2092727a69dcSkre 		if (*p++ == '\0')
2093727a69dcSkre 			return;
2094727a69dcSkre 	}
2095727a69dcSkre 	if (p > str)	/* must reprocess char before stopper (if any) */
2096727a69dcSkre 		--p;	/* so we do not place a \n badly */
2097727a69dcSkre 	q = p;
2098727a69dcSkre 	while (*p) {
2099c9f333adSkre 		if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) {
2100727a69dcSkre 			p++;
2101727a69dcSkre 			continue;
2102727a69dcSkre 		}
2103727a69dcSkre 		if (*p == CTLNONL) {
2104727a69dcSkre 			p++;
2105727a69dcSkre 			nls++;
2106727a69dcSkre 			continue;
2107727a69dcSkre 		}
21085f92382cSkre 		if (*p == CTLCNL) {
21095f92382cSkre 			p++;
21105f92382cSkre 			*q++ = '\n';
21115f92382cSkre 			continue;
21125f92382cSkre 		}
2113727a69dcSkre 		if (*p == CTLESC)
2114727a69dcSkre 			p++;
211558e34de6Skre #ifdef DEBUG
2116256d645dSkre 		else if (ISCTL(*p))
211758e34de6Skre 			abort();
211858e34de6Skre #endif
2119727a69dcSkre 
2120727a69dcSkre 		holdlast = holdnl;
2121727a69dcSkre 		holdnl = is_in_name(*p);	/* letters, digits, _ */
2122727a69dcSkre 		if (q == str || is_space(q[-1]) || (*p != '=' && q[-1] != *p)) {
2123727a69dcSkre 			if (nls > 0 && holdnl != holdlast) {
2124727a69dcSkre 				while (nls > 0)
2125727a69dcSkre 					*q++ = '\n', nls--;
2126727a69dcSkre 			}
2127727a69dcSkre 		}
2128727a69dcSkre 		*q++ = *p++;
2129727a69dcSkre 	}
2130727a69dcSkre 	while (--nls >= 0)
2131727a69dcSkre 		*q++ = '\n';
2132727a69dcSkre 	*q = '\0';
2133727a69dcSkre }
2134727a69dcSkre 
213561f28255Scgd 
213661f28255Scgd 
213761f28255Scgd /*
213861f28255Scgd  * See if a pattern matches in a case statement.
213961f28255Scgd  */
214061f28255Scgd 
214161f28255Scgd int
2142c02b3bbdSchristos casematch(union node *pattern, char *val)
214361f28255Scgd {
214461f28255Scgd 	struct stackmark smark;
214561f28255Scgd 	int result;
214661f28255Scgd 	char *p;
214761f28255Scgd 
2148bcacfd9aSkre 	CTRACE(DBG_MATCH, ("casematch(P=\"%s\", W=\"%s\")\n",
2149bcacfd9aSkre 	    pattern->narg.text, val));
215061f28255Scgd 	setstackmark(&smark);
215161f28255Scgd 	argbackq = pattern->narg.backquote;
215261f28255Scgd 	STARTSTACKSTR(expdest);
215361f28255Scgd 	ifslastp = NULL;
21541fbf0781Smycroft 	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
215561f28255Scgd 	STPUTC('\0', expdest);
215661f28255Scgd 	p = grabstackstr(expdest);
215728607542Schristos 	result = patmatch(p, val, 0);
215861f28255Scgd 	popstackmark(&smark);
215961f28255Scgd 	return result;
216061f28255Scgd }
2161ad8d5369Schristos 
2162ad8d5369Schristos /*
2163dd6b6414Skre  * Our own itoa().   Assumes result buffer is on the stack
2164ad8d5369Schristos  */
2165ad8d5369Schristos 
2166ad8d5369Schristos STATIC char *
2167c02b3bbdSchristos cvtnum(int num, char *buf)
2168ad8d5369Schristos {
2169ad8d5369Schristos 	char temp[32];
2170ad8d5369Schristos 	int neg = num < 0;
2171dd6b6414Skre 	char *p = temp + sizeof temp - 1;
2172ad8d5369Schristos 
2173dd6b6414Skre 	if (neg)
2174dd6b6414Skre 		num = -num;
2175ad8d5369Schristos 
2176dd6b6414Skre 	*p = '\0';
2177ad8d5369Schristos 	do {
2178ad8d5369Schristos 		*--p = num % 10 + '0';
2179dd6b6414Skre 	} while ((num /= 10) != 0 && p > temp + 1);
2180ad8d5369Schristos 
2181ad8d5369Schristos 	if (neg)
2182ad8d5369Schristos 		*--p = '-';
2183ad8d5369Schristos 
2184ad8d5369Schristos 	while (*p)
2185ad8d5369Schristos 		STPUTC(*p++, buf);
2186ad8d5369Schristos 	return buf;
2187ad8d5369Schristos }
2188cf788c31Sseb 
2189cf788c31Sseb /*
2190cf788c31Sseb  * Do most of the work for wordexp(3).
2191cf788c31Sseb  */
2192cf788c31Sseb 
2193cf788c31Sseb int
2194cf788c31Sseb wordexpcmd(int argc, char **argv)
2195cf788c31Sseb {
2196cf788c31Sseb 	size_t len;
2197cf788c31Sseb 	int i;
2198cf788c31Sseb 
2199cf788c31Sseb 	out1fmt("%d", argc - 1);
2200cf788c31Sseb 	out1c('\0');
2201cf788c31Sseb 	for (i = 1, len = 0; i < argc; i++)
2202cf788c31Sseb 		len += strlen(argv[i]);
220349ee47d0Stsutsui 	out1fmt("%zu", len);
2204cf788c31Sseb 	out1c('\0');
2205cf788c31Sseb 	for (i = 1; i < argc; i++) {
2206cf788c31Sseb 		out1str(argv[i]);
2207cf788c31Sseb 		out1c('\0');
2208cf788c31Sseb 	}
2209cf788c31Sseb 	return (0);
2210cf788c31Sseb }
2211