xref: /netbsd-src/bin/sh/expand.c (revision a7e8b4a5c84baeacaa93eecdd02a7533285bb4d4)
1*a7e8b4a5Skre /*	$NetBSD: expand.c,v 1.146 2024/10/21 15:57:45 kre 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*a7e8b4a5Skre __RCSID("$NetBSD: expand.c,v 1.146 2024/10/21 15:57:45 kre 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
1294cb87529Srillig #define	NULLTERM_4_TRACE(p)	do { /* nothing */ } while (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 
14616d85571Skre char *
14716d85571Skre expandhere(union node *arg)
14861f28255Scgd {
149c69ada4cSkre 	int len;
1501d1484aaSchristos 
15116d85571Skre 	VTRACE(DBG_EXPAND|DBG_REDIR, ("expandhere(%p)\n", arg));
1529f61b804Splunky 	expandarg(arg, NULL, 0);
153c69ada4cSkre 	len = rmescapes(stackblock());
15416d85571Skre 	VTRACE(DBG_EXPAND|DBG_REDIR, ("expandhere() -> %d\n", len));
15516d85571Skre 	return stalloc(len + 1);	/* include the \0 */
15661f28255Scgd }
15761f28255Scgd 
15861f28255Scgd 
1598e4a570fSchristos static int
1608e4a570fSchristos collate_range_cmp(wchar_t c1, wchar_t c2)
1618e4a570fSchristos {
1628e4a570fSchristos 	wchar_t s1[2], s2[2];
1638e4a570fSchristos 
1648e4a570fSchristos 	s1[0] = c1;
1658e4a570fSchristos 	s1[1] = L'\0';
1668e4a570fSchristos 	s2[0] = c2;
1678e4a570fSchristos 	s2[1] = L'\0';
1688e4a570fSchristos 	return (wcscoll(s1, s2));
1698e4a570fSchristos }
1708e4a570fSchristos 
17161f28255Scgd /*
17261f28255Scgd  * Perform variable substitution and command substitution on an argument,
17337ed7877Sjtc  * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
17461f28255Scgd  * perform splitting and file name expansion.  When arglist is NULL, perform
17561f28255Scgd  * here document expansion.
17661f28255Scgd  */
17761f28255Scgd 
17861f28255Scgd void
179c02b3bbdSchristos expandarg(union node *arg, struct arglist *arglist, int flag)
18061f28255Scgd {
18161f28255Scgd 	struct strlist *sp;
18261f28255Scgd 	char *p;
18361f28255Scgd 
18458810bf0Skre 	CTRACE(DBG_EXPAND, ("expandarg(fl=%#x)\n", flag));
185dd6b6414Skre 	if (fflag)		/* no filename expandsion */
186dd6b6414Skre 		flag &= ~EXP_GLOB;
187dd6b6414Skre 
188265b0617Skre 	empty_dollar_at = 0;
18961f28255Scgd 	argbackq = arg->narg.backquote;
19061f28255Scgd 	STARTSTACKSTR(expdest);
19161f28255Scgd 	ifsfirst.next = NULL;
19261f28255Scgd 	ifslastp = NULL;
193727a69dcSkre 	line_number = arg->narg.lineno;
19469c48e32Skre 	argstr(arg->narg.text, flag);
19537ed7877Sjtc 	if (arglist == NULL) {
1961fca9bbfSkre 		STACKSTRNUL(expdest);
197b8bee70dSkre 		CTRACE(DBG_EXPAND,
198b8bee70dSkre 		    ("expandarg: no arglist, done[%d] (len %d) \"%s\"\n",
199b8bee70dSkre 		    back_exitstatus, expdest - stackblock(), stackblock()));
20061f28255Scgd 		return;			/* here document expanded */
20137ed7877Sjtc 	}
20261f28255Scgd 	STPUTC('\0', expdest);
20358810bf0Skre 	CTRACE(DBG_EXPAND, ("expandarg: arglist got (%d) \"%s\"\n",
20458810bf0Skre 		    expdest - stackblock() - 1, stackblock()));
20561f28255Scgd 	p = grabstackstr(expdest);
20661f28255Scgd 	exparg.lastp = &exparg.list;
20737ed7877Sjtc 	/*
20837ed7877Sjtc 	 * TODO - EXP_REDIR
20937ed7877Sjtc 	 */
210dd6b6414Skre 	if (flag & EXP_SPLIT) {
21161f28255Scgd 		ifsbreakup(p, &exparg);
21261f28255Scgd 		*exparg.lastp = NULL;
21361f28255Scgd 		exparg.lastp = &exparg.list;
214dd6b6414Skre 		if (flag & EXP_GLOB)
21537ed7877Sjtc 			expandmeta(exparg.list, flag);
216dd6b6414Skre 		else
217dd6b6414Skre 			add_args(exparg.list);
218256d645dSkre #if 0
219256d645dSkre 	} else if (flag & EXP_REDIR) {
220256d645dSkre 		/* if EXP_REDIR ever happens, it happens here */
221256d645dSkre 		/* for now just (below) remove escapes, and leave it alone */
222256d645dSkre #endif
22361f28255Scgd 	} else {
224256d645dSkre 		rmescapes(p);	/* we might have escaped CTL bytes to remove */
22578204bbfSchristos 		sp = stalloc(sizeof(*sp));
22661f28255Scgd 		sp->text = p;
22761f28255Scgd 		*exparg.lastp = sp;
22861f28255Scgd 		exparg.lastp = &sp->next;
22961f28255Scgd 	}
230f7c8df6dSchristos 	ifsfree();
23161f28255Scgd 	*exparg.lastp = NULL;
23261f28255Scgd 	if (exparg.list) {
23361f28255Scgd 		*arglist->lastp = exparg.list;
23461f28255Scgd 		arglist->lastp = exparg.lastp;
23561f28255Scgd 	}
23661f28255Scgd }
23761f28255Scgd 
23861f28255Scgd 
23961f28255Scgd 
24061f28255Scgd /*
241c6cbc16dSdsl  * Perform variable and command substitution.
242dd6b6414Skre  * If EXP_GLOB is set, output CTLESC characters to allow for further processing.
243dd6b6414Skre  * If EXP_SPLIT is set, remember location of result for later,
244c6cbc16dSdsl  * Otherwise treat $@ like $* since no splitting will be performed.
24561f28255Scgd  */
24661f28255Scgd 
247dd6b6414Skre STATIC const char *
248dd6b6414Skre argstr(const char *p, int flag)
24961f28255Scgd {
25048250187Stls 	char c;
251df073671Skre 	const int quotes = flag & EXP_QNEEDED;		/* do CTLESC */
25237ed7877Sjtc 	int firsteq = 1;
253265b0617Skre 	int had_dol_at = 0;
254265b0617Skre 	int startoff;
2555166671bSlukem 	const char *ifs = NULL;
256c6cbc16dSdsl 	int ifs_split = EXP_IFS_SPLIT;
257c6cbc16dSdsl 
258c6cbc16dSdsl 	if (flag & EXP_IFS_SPLIT)
259ca12a0b8Schristos 		ifs = ifsval();
26061f28255Scgd 
261ea89b130Skre 	CTRACE(DBG_EXPAND, ("argstr(\"%s\", %#x) quotes=%#x\n", p,flag,quotes));
262dd6b6414Skre 
263265b0617Skre 	startoff = expdest - stackblock();
26437ed7877Sjtc 	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
26537ed7877Sjtc 		p = exptilde(p, flag);
26661f28255Scgd 	for (;;) {
26761f28255Scgd 		switch (c = *p++) {
26861f28255Scgd 		case '\0':
269701ac132Skre 			NULLTERM_4_TRACE(expdest);
270dd6b6414Skre 			VTRACE(DBG_EXPAND, ("argstr returning at \"\" "
271ee3b307fSkre 			   "added \"%s\" to expdest\n", stackblock()));
272dd6b6414Skre 			return p - 1;
273e4a2a056Sdsl 		case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */
274dd6b6414Skre 		case CTLENDARI: /* end of a $(( )) string */
275d08d589dSchristos 			if (had_dol_at && *p == CTLQUOTEEND)
276265b0617Skre 				p++;
277701ac132Skre 			NULLTERM_4_TRACE(expdest);
278dd6b6414Skre 			VTRACE(DBG_EXPAND, ("argstr returning at \"%.6s\"..."
279ee3b307fSkre 			    " after %2.2X; added \"%s\" to expdest\n",
280ee3b307fSkre 			    p, (c & 0xff), stackblock()));
281dd6b6414Skre 			return p;
2821fbf0781Smycroft 		case CTLQUOTEMARK:
2831fbf0781Smycroft 			/* "$@" syntax adherence hack */
284dec2ca90Schristos 			if (p[0] == CTLVAR && p[1] & VSQUOTE &&
285265b0617Skre 			    p[2] == '@' && p[3] == '=') {
286265b0617Skre 				had_dol_at = 1;
2871fbf0781Smycroft 				break;
288265b0617Skre 			}
289265b0617Skre 			had_dol_at = 0;
290265b0617Skre 			empty_dollar_at = 0;
291dd6b6414Skre 			if ((flag & EXP_SPLIT) != 0)
2921fbf0781Smycroft 				STPUTC(c, expdest);
293c6cbc16dSdsl 			ifs_split = 0;
294c6cbc16dSdsl 			break;
295727a69dcSkre 		case CTLNONL:
296727a69dcSkre 			if (flag & EXP_NL)
297727a69dcSkre 				STPUTC(c, expdest);
298727a69dcSkre 			line_number++;
299727a69dcSkre 			break;
3005f92382cSkre 		case CTLCNL:
3015f92382cSkre 			STPUTC('\n', expdest);	/* no line_number++ */
3025f92382cSkre 			break;
303c6cbc16dSdsl 		case CTLQUOTEEND:
304265b0617Skre 			if (empty_dollar_at &&
305265b0617Skre 			    expdest - stackblock() > startoff &&
306265b0617Skre 			    expdest[-1] == CTLQUOTEMARK)
307265b0617Skre 				expdest--;
308265b0617Skre 			else if (!had_dol_at && (flag & EXP_SPLIT) != 0)
309c9f333adSkre 				STPUTC(c, expdest);
310c6cbc16dSdsl 			ifs_split = EXP_IFS_SPLIT;
311265b0617Skre 			had_dol_at = 0;
3121fbf0781Smycroft 			break;
31361f28255Scgd 		case CTLESC:
314b95d46c2Skre 			if (quotes || ISCTL(*p))
31561f28255Scgd 				STPUTC(c, expdest);
31661f28255Scgd 			c = *p++;
31761f28255Scgd 			STPUTC(c, expdest);
318ee3b307fSkre 			if (c == '\n')		/* should not happen, but ... */
319ee3b307fSkre 				line_number++;
32061f28255Scgd 			break;
321ee3b307fSkre 		case CTLVAR: {
322ee3b307fSkre #ifdef DEBUG
323ee3b307fSkre 			unsigned int pos = expdest - stackblock();
324c9f333adSkre 			NULLTERM_4_TRACE(expdest);
325ee3b307fSkre #endif
326c6cbc16dSdsl 			p = evalvar(p, (flag & ~EXP_IFS_SPLIT) | (flag & ifs_split));
327701ac132Skre 			NULLTERM_4_TRACE(expdest);
328dd6b6414Skre 			VTRACE(DBG_EXPAND, ("argstr evalvar "
329c9f333adSkre 			   "added %zd \"%s\" to expdest\n",
330c9f333adSkre 			   (size_t)(expdest - (stackblock() + pos)),
331ee3b307fSkre 			   stackblock() + pos));
33261f28255Scgd 			break;
333ee3b307fSkre 		}
33461f28255Scgd 		case CTLBACKQ:
335ee3b307fSkre 		case CTLBACKQ|CTLQUOTE: {
336ee3b307fSkre #ifdef DEBUG
337ee3b307fSkre 			unsigned int pos = expdest - stackblock();
338ee3b307fSkre #endif
33937ed7877Sjtc 			expbackq(argbackq->n, c & CTLQUOTE, flag);
34061f28255Scgd 			argbackq = argbackq->next;
341701ac132Skre 			NULLTERM_4_TRACE(expdest);
342ee3b307fSkre 			VTRACE(DBG_EXPAND, ("argstr expbackq added \"%s\" "
343ee3b307fSkre 			   "to expdest\n", stackblock() + pos));
34461f28255Scgd 			break;
345ee3b307fSkre 		}
346ee3b307fSkre 		case CTLARI: {
347ee3b307fSkre #ifdef DEBUG
348ee3b307fSkre 			unsigned int pos = expdest - stackblock();
349ee3b307fSkre #endif
35051791eabSkre 			p = expari(p);
351701ac132Skre 			NULLTERM_4_TRACE(expdest);
352dd6b6414Skre 			VTRACE(DBG_EXPAND, ("argstr expari "
353ee3b307fSkre 			   "+ \"%s\" to expdest p=\"%.5s...\"\n",
354ee3b307fSkre 			   stackblock() + pos, p));
35537ed7877Sjtc 			break;
356ee3b307fSkre 		}
35737ed7877Sjtc 		case ':':
35837ed7877Sjtc 		case '=':
35937ed7877Sjtc 			/*
36037ed7877Sjtc 			 * sort of a hack - expand tildes in variable
36137ed7877Sjtc 			 * assignments (after the first '=' and after ':'s).
36237ed7877Sjtc 			 */
36337ed7877Sjtc 			STPUTC(c, expdest);
36437ed7877Sjtc 			if (flag & EXP_VARTILDE && *p == '~') {
36537ed7877Sjtc 				if (c == '=') {
36637ed7877Sjtc 					if (firsteq)
36737ed7877Sjtc 						firsteq = 0;
36837ed7877Sjtc 					else
36937ed7877Sjtc 						break;
37037ed7877Sjtc 				}
37137ed7877Sjtc 				p = exptilde(p, flag);
37237ed7877Sjtc 			}
37337ed7877Sjtc 			break;
37461f28255Scgd 		default:
375727a69dcSkre 			if (c == '\n')
376727a69dcSkre 				line_number++;
37761f28255Scgd 			STPUTC(c, expdest);
3787d60739aSdsl 			if (flag & ifs_split && strchr(ifs, c) != NULL) {
379c6cbc16dSdsl 				/* We need to get the output split here... */
380c6cbc16dSdsl 				recordregion(expdest - stackblock() - 1,
381c6cbc16dSdsl 						expdest - stackblock(), 0);
382c6cbc16dSdsl 			}
383e4a2a056Sdsl 			break;
38461f28255Scgd 		}
38561f28255Scgd 	}
38661f28255Scgd }
38761f28255Scgd 
388dd6b6414Skre STATIC const char *
389dd6b6414Skre exptilde(const char *p, int flag)
39037ed7877Sjtc {
391726d188aSkre 	char c, last;
392dd6b6414Skre 	const char *startp = p;
39337ed7877Sjtc 	struct passwd *pw;
3945dd823abSmycroft 	const char *home;
395df073671Skre 	const int quotes = flag & EXP_QNEEDED;
396dd6b6414Skre 	char *user;
397ee3b307fSkre 	struct stackmark smark;
398ee3b307fSkre #ifdef DEBUG
399ee3b307fSkre 	unsigned int offs = expdest - stackblock();
400ee3b307fSkre #endif
40137ed7877Sjtc 
402ee3b307fSkre 	setstackmark(&smark);
403bd208c69Skre 	(void) grabstackstr(expdest);
404ee3b307fSkre 	user = stackblock();		/* we will just borrow top of stack */
405ee3b307fSkre 
406aa681addSkre 	while ((c = *++p) != '\0') {
40737ed7877Sjtc 		switch(c) {
408ee3b307fSkre 		case CTLESC:		/* any of these occurring */
409ee3b307fSkre 		case CTLVAR:		/* means ~ expansion */
410ee3b307fSkre 		case CTLBACKQ:		/* does not happen at all */
411dd6b6414Skre 		case CTLBACKQ | CTLQUOTE:
412ee3b307fSkre 		case CTLARI:		/* just leave original unchanged */
413dd6b6414Skre 		case CTLENDARI:
4141fbf0781Smycroft 		case CTLQUOTEMARK:
415ee3b307fSkre 		case '\n':
416ee3b307fSkre 			popstackmark(&smark);
4171fbf0781Smycroft 			return (startp);
418727a69dcSkre 		case CTLNONL:
419ee3b307fSkre 			continue;
42037ed7877Sjtc 		case ':':
421bd208c69Skre 			if (!posix || flag & EXP_VARTILDE)
42237ed7877Sjtc 				goto done;
42337ed7877Sjtc 			break;
424bd208c69Skre 		case CTLENDVAR:
42537ed7877Sjtc 		case '/':
42637ed7877Sjtc 			goto done;
42737ed7877Sjtc 		}
428dd6b6414Skre 		STPUTC(c, user);
42937ed7877Sjtc 	}
43037ed7877Sjtc  done:
431dd6b6414Skre 	STACKSTRNUL(user);
432ee3b307fSkre 	user = stackblock();		/* to start of collected username */
433ea89b130Skre 
434ee3b307fSkre 	CTRACE(DBG_EXPAND, ("exptilde, found \"~%s\"", user));
435ee3b307fSkre 	if (*user == '\0') {
436dd6b6414Skre 		home = lookupvar("HOME");
437ee3b307fSkre 		/*
438ee3b307fSkre 		 * if HOME is unset, results are unspecified...
439ee3b307fSkre 		 * we used to just leave the ~ unchanged, but
440ee3b307fSkre 		 * (some) other shells do ... and this seems more useful.
441ee3b307fSkre 		 */
442ee3b307fSkre 		if (home == NULL && (pw = getpwuid(getuid())) != NULL)
44337ed7877Sjtc 			home = pw->pw_dir;
444ee3b307fSkre 	} else if ((pw = getpwnam(user)) == NULL) {
445ee3b307fSkre 		/*
446ee3b307fSkre 		 * If user does not exist, results are undefined.
447ee3b307fSkre 		 * so we can abort() here if we want, but let's not!
448ee3b307fSkre 		 */
449ee3b307fSkre 		home = NULL;
450ee3b307fSkre 	} else
451ee3b307fSkre 		home = pw->pw_dir;
452dd6b6414Skre 
453ee3b307fSkre 	VTRACE(DBG_EXPAND, (" ->\"%s\"", home ? home : "<<NULL>>"));
454ee3b307fSkre 	popstackmark(&smark);	/* now expdest is valid again */
455ee3b307fSkre 
456ee3b307fSkre 	/*
457ee3b307fSkre 	 * Posix XCU 2.6.1: The value of $HOME (for ~) or the initial
458ee3b307fSkre 	 *		working directory from getpwnam() for ~user
459ee3b307fSkre 	 * Nothing there about "except if a null string".  So do what it wants.
460726d188aSkre 	 * In later drafts (to become Issue 8), it is even required that in
461726d188aSkre 	 * this case, (where HOME='') a bare ~ expands to "" (which must not
462726d188aSkre 	 * be reduced to nothing).
463ee3b307fSkre 	 */
464726d188aSkre 	last = '\0';		/* just in case *home == '\0' (already) */
465726d188aSkre 	if (home == NULL) {
466ee3b307fSkre 		CTRACE(DBG_EXPAND, (": returning unused \"%s\"\n", startp));
467dd6b6414Skre 		return startp;
468391d4540Skre 	}
469391d4540Skre 	while ((c = *home++) != '\0') {
470256d645dSkre 		if ((quotes && NEEDESC(c)) || ISCTL(c))
47137ed7877Sjtc 			STPUTC(CTLESC, expdest);
47237ed7877Sjtc 		STPUTC(c, expdest);
473726d188aSkre 		last = c;
47437ed7877Sjtc 	}
475726d188aSkre 
476726d188aSkre 	/*
477726d188aSkre 	 * If HOME (or whatver) ended in a '/' (last == '/'), and
478726d188aSkre 	 * the ~prefix was terminated by a '/', then only keep one
479726d188aSkre 	 * of them - since we already took the one from HOME, just
480726d188aSkre 	 * skip over the one that ended the tilde prefix.
481726d188aSkre 	 *
482726d188aSkre 	 * Current (Issue 8) drafts say this is permitted, and recommend
483726d188aSkre 	 * it - a later version of the standard will probably require it.
484726d188aSkre 	 * This is to prevent ~/foo generating //foo when HOME=/ (and
485726d188aSkre 	 * other cases like it, but that's the important one).
486726d188aSkre 	 */
487726d188aSkre 	if (last == '/' && *p == '/')
488726d188aSkre 		p++;
489726d188aSkre 
490ee3b307fSkre 	CTRACE(DBG_EXPAND, (": added %d \"%.*s\" returning \"%s\"\n",
491ee3b307fSkre 	      expdest - stackblock() - offs, expdest - stackblock() - offs,
492ee3b307fSkre 	      stackblock() + offs, p));
493ea89b130Skre 
49437ed7877Sjtc 	return (p);
49537ed7877Sjtc }
49637ed7877Sjtc 
49737ed7877Sjtc 
4981fbf0781Smycroft STATIC void
499c02b3bbdSchristos removerecordregions(int endoff)
5001fbf0781Smycroft {
50158810bf0Skre 
50258810bf0Skre 	VTRACE(DBG_EXPAND, ("removerecordregions(%d):", endoff));
50358810bf0Skre 	if (ifslastp == NULL) {
50458810bf0Skre 		VTRACE(DBG_EXPAND, (" none\n", endoff));
5051fbf0781Smycroft 		return;
50658810bf0Skre 	}
5071fbf0781Smycroft 
5081fbf0781Smycroft 	if (ifsfirst.endoff > endoff) {
50958810bf0Skre 		VTRACE(DBG_EXPAND, (" first(%d)", ifsfirst.endoff));
5101fbf0781Smycroft 		while (ifsfirst.next != NULL) {
5111fbf0781Smycroft 			struct ifsregion *ifsp;
5121fbf0781Smycroft 			INTOFF;
5131fbf0781Smycroft 			ifsp = ifsfirst.next->next;
5141fbf0781Smycroft 			ckfree(ifsfirst.next);
5151fbf0781Smycroft 			ifsfirst.next = ifsp;
5161fbf0781Smycroft 			INTON;
5171fbf0781Smycroft 		}
5181fbf0781Smycroft 		if (ifsfirst.begoff > endoff)
5191fbf0781Smycroft 			ifslastp = NULL;
5201fbf0781Smycroft 		else {
52158810bf0Skre 			VTRACE(DBG_EXPAND,("->(%d,%d)",ifsfirst.begoff,endoff));
5221fbf0781Smycroft 			ifslastp = &ifsfirst;
5231fbf0781Smycroft 			ifsfirst.endoff = endoff;
5241fbf0781Smycroft 		}
52558810bf0Skre 		VTRACE(DBG_EXPAND, ("\n"));
5261fbf0781Smycroft 		return;
5271fbf0781Smycroft 	}
5281fbf0781Smycroft 
5291fbf0781Smycroft 	ifslastp = &ifsfirst;
5301fbf0781Smycroft 	while (ifslastp->next && ifslastp->next->begoff < endoff)
5311fbf0781Smycroft 		ifslastp=ifslastp->next;
53258810bf0Skre 	VTRACE(DBG_EXPAND, (" found(%d,%d)", ifslastp->begoff,ifslastp->endoff));
5331fbf0781Smycroft 	while (ifslastp->next != NULL) {
5341fbf0781Smycroft 		struct ifsregion *ifsp;
5351fbf0781Smycroft 		INTOFF;
5361fbf0781Smycroft 		ifsp = ifslastp->next->next;
5371fbf0781Smycroft 		ckfree(ifslastp->next);
5381fbf0781Smycroft 		ifslastp->next = ifsp;
5391fbf0781Smycroft 		INTON;
5401fbf0781Smycroft 	}
5411fbf0781Smycroft 	if (ifslastp->endoff > endoff)
5421fbf0781Smycroft 		ifslastp->endoff = endoff;
54358810bf0Skre 	VTRACE(DBG_EXPAND, ("->(%d,%d)", ifslastp->begoff,ifslastp->endoff));
5441fbf0781Smycroft }
5451fbf0781Smycroft 
5461fbf0781Smycroft 
54737ed7877Sjtc /*
548ee3b307fSkre  * Expand arithmetic expression.
549ee3b307fSkre  *
550ee3b307fSkre  * In this incarnation, we start at the beginning (yes, "Let's start at the
551ee3b307fSkre  * very beginning.  A very good place to start.") and collect the expression
552ee3b307fSkre  * until the end - which means expanding anything contained within.
553ee3b307fSkre  *
554ee3b307fSkre  * Fortunately, argstr() just happens to do that for us...
55537ed7877Sjtc  */
556dd6b6414Skre STATIC const char *
55751791eabSkre expari(const char *p)
55837ed7877Sjtc {
559dd6b6414Skre 	char *q, *start;
56091ce988bSapb 	intmax_t result;
56191ce988bSapb 	int adjustment;
5621fbf0781Smycroft 	int begoff;
5631fbf0781Smycroft 	int quoted;
564ee3b307fSkre 	struct stackmark smark;
56537ed7877Sjtc 
5661fbf0781Smycroft 	/*	ifsfree(); */
567633ceb6dSchristos 
56837ed7877Sjtc 	/*
569cc8e58edSkre 	 * SPACE_NEEDED is enough for all possible digits (rounded up)
570cc8e58edSkre 	 * plus possible "-", and the terminating '\0', hence, plus 2
571cc8e58edSkre 	 *
572cc8e58edSkre 	 * The calculation produces the number of bytes needed to
573cc8e58edSkre 	 * represent the biggest possible value, in octal.  We only
574cc8e58edSkre 	 * generate decimal, which takes (often) less digits (never more)
575cc8e58edSkre 	 * so this is safe, if occasionally slightly wasteful.
576cc8e58edSkre 	 */
577dd6b6414Skre #define SPACE_NEEDED ((int)((sizeof(intmax_t) * CHAR_BIT + 2) / 3 + 2))
578ee3b307fSkre 
579dd6b6414Skre 	quoted = *p++ == '"';
580dd6b6414Skre 	begoff = expdest - stackblock();
581ee3b307fSkre 	VTRACE(DBG_EXPAND, ("expari%s: \"%s\" begoff %d\n",
582ee3b307fSkre 	    quoted ? "(quoted)" : "", p, begoff));
583ee3b307fSkre 
584727a69dcSkre 	p = argstr(p, EXP_NL);			/* expand $(( )) string */
585dd6b6414Skre 	STPUTC('\0', expdest);
586dd6b6414Skre 	start = stackblock() + begoff;
587ee3b307fSkre 
588ee3b307fSkre 	removerecordregions(begoff);		/* nothing there is kept */
589727a69dcSkre 	rmescapes_nl(start);		/* convert CRTNONL back into \n's */
590ee3b307fSkre 
591ee3b307fSkre 	setstackmark(&smark);
592ee3b307fSkre 	q = grabstackstr(expdest);	/* keep the expression while eval'ing */
593727a69dcSkre 	result = arith(start, line_number);
594ee3b307fSkre 	popstackmark(&smark);		/* return the stack to before grab */
595633ceb6dSchristos 
596ee3b307fSkre 	start = stackblock() + begoff;		/* block may have moved */
597dd6b6414Skre 	adjustment = expdest - start;
598ee3b307fSkre 	STADJUST(-adjustment, expdest);		/* remove the argstr() result */
599dd6b6414Skre 
600ee3b307fSkre 	CHECKSTRSPACE(SPACE_NEEDED, expdest);	/* nb: stack block might move */
601dd6b6414Skre 	fmtstr(expdest, SPACE_NEEDED, "%"PRIdMAX, result);
602dd6b6414Skre 
603ee3b307fSkre 	for (q = expdest; *q++ != '\0'; )	/* find end of what we added */
60437ed7877Sjtc 		;
605633ceb6dSchristos 
606dd6b6414Skre 	if (quoted == 0)			/* allow weird splitting */
60758810bf0Skre 		recordregion(begoff, begoff + q - 1 - expdest, 0);
608d3573c91Skre 	adjustment = q - expdest - 1;
609ee3b307fSkre 	STADJUST(adjustment, expdest);		/* move expdest to end */
610f72bc19eSkre 	VTRACE(DBG_EXPAND, ("expari: adding %d \"%s\", returning \"%.5s...\"\n",
611f72bc19eSkre 	    adjustment, stackblock() + begoff, p));
612dd6b6414Skre 
613dd6b6414Skre 	return p;
61437ed7877Sjtc }
61537ed7877Sjtc 
61661f28255Scgd 
61761f28255Scgd /*
618ee3b307fSkre  * Expand stuff in backwards quotes (these days, any command substitution).
61961f28255Scgd  */
62061f28255Scgd 
62161f28255Scgd STATIC void
622c02b3bbdSchristos expbackq(union node *cmd, int quoted, int flag)
62361f28255Scgd {
62461f28255Scgd 	struct backcmd in;
62561f28255Scgd 	int i;
62661f28255Scgd 	char buf[128];
62761f28255Scgd 	char *p;
628ee3b307fSkre 	char *dest = expdest;	/* expdest may be reused by eval, use an alt */
62961f28255Scgd 	struct ifsregion saveifs, *savelastp;
63061f28255Scgd 	struct nodelist *saveargbackq;
63161f28255Scgd 	char lastc;
63261f28255Scgd 	int startloc = dest - stackblock();
63361f28255Scgd 	int saveherefd;
634df073671Skre 	const int quotes = flag & EXP_QNEEDED;
63569a4e2eeSchristos 	int nnl;
636ee3b307fSkre 	struct stackmark smark;
63761f28255Scgd 
638ee3b307fSkre 	VTRACE(DBG_EXPAND, ("expbackq( ..., q=%d flag=%#x) have %d\n",
639ee3b307fSkre 	    quoted, flag, startloc));
64061f28255Scgd 	INTOFF;
64161f28255Scgd 	saveifs = ifsfirst;
64261f28255Scgd 	savelastp = ifslastp;
64361f28255Scgd 	saveargbackq = argbackq;
64461f28255Scgd 	saveherefd = herefd;
64561f28255Scgd 	herefd = -1;
646ee3b307fSkre 
647ee3b307fSkre 	setstackmark(&smark);	/* preserve the stack */
648ee3b307fSkre 	p = grabstackstr(dest);	/* save what we have there currently */
649ee3b307fSkre 	evalbackcmd(cmd, &in);	/* evaluate the $( ) tree (using stack) */
650ee3b307fSkre 	popstackmark(&smark);	/* and return stack to when we entered */
651ee3b307fSkre 
65261f28255Scgd 	ifsfirst = saveifs;
65361f28255Scgd 	ifslastp = savelastp;
65461f28255Scgd 	argbackq = saveargbackq;
65561f28255Scgd 	herefd = saveherefd;
65661f28255Scgd 
657ee3b307fSkre 	p = in.buf;		/* now extract the results */
658ee3b307fSkre 	nnl = 0;		/* dropping trailing \n's */
65961f28255Scgd 	for (;;) {
66061f28255Scgd 		if (--in.nleft < 0) {
66161f28255Scgd 			if (in.fd < 0)
66261f28255Scgd 				break;
6631fca9bbfSkre 			INTON;
6643eb04a36Sjoerg 			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR)
6653eb04a36Sjoerg 				continue;
6661fca9bbfSkre 			INTOFF;
667ea89b130Skre 			VTRACE(DBG_EXPAND, ("expbackq: read returns %d\n", i));
66861f28255Scgd 			if (i <= 0)
66961f28255Scgd 				break;
67061f28255Scgd 			p = buf;
67161f28255Scgd 			in.nleft = i - 1;
67261f28255Scgd 		}
67361f28255Scgd 		lastc = *p++;
67461f28255Scgd 		if (lastc != '\0') {
675ee3b307fSkre 			if (lastc == '\n')	/* don't save \n yet */
676ee3b307fSkre 				nnl++;		/* it might be trailing */
67769a4e2eeSchristos 			else {
678ee3b307fSkre 				/*
679ee3b307fSkre 				 * We have something other than \n
680ee3b307fSkre 				 *
681ee3b307fSkre 				 * Before saving it, we need to insert
682ee3b307fSkre 				 * any \n's that we have just skipped.
683ee3b307fSkre 				 */
684ee3b307fSkre 
685ee3b307fSkre 				/* XXX
686ee3b307fSkre 				 * this hack is just because our
687ee3b307fSkre 				 * CHECKSTRSPACE() is lazy, and only
688ee3b307fSkre 				 * ever grows the stack once, even
689ee3b307fSkre 				 * if that does not allocate the space
690ee3b307fSkre 				 * we requested.  ie: safe for small
691ee3b307fSkre 				 * requests, but not large ones.
692ee3b307fSkre 				 * FIXME someday...
693ee3b307fSkre 				 */
694ee3b307fSkre 				if (nnl < 20) {
69569a4e2eeSchristos 					CHECKSTRSPACE(nnl + 2, dest);
69669a4e2eeSchristos 					while (nnl > 0) {
69769a4e2eeSchristos 						nnl--;
69869a4e2eeSchristos 						USTPUTC('\n', dest);
69969a4e2eeSchristos 					}
700ee3b307fSkre 				} else {
701ee3b307fSkre 					/* The slower, safer, way */
702ee3b307fSkre 					while (nnl > 0) {
703ee3b307fSkre 						nnl--;
704ee3b307fSkre 						STPUTC('\n', dest);
705ee3b307fSkre 					}
706ee3b307fSkre 					CHECKSTRSPACE(2, dest);
707ee3b307fSkre 				}
708256d645dSkre 				if ((quotes && quoted && NEEDESC(lastc)) ||
709256d645dSkre 				    ISCTL(lastc))
71069a4e2eeSchristos 					USTPUTC(CTLESC, dest);
71169a4e2eeSchristos 				USTPUTC(lastc, dest);
71261f28255Scgd 			}
71361f28255Scgd 		}
71469a4e2eeSchristos 	}
7156ab19545Schristos 
71661f28255Scgd 	if (in.fd >= 0)
71761f28255Scgd 		close(in.fd);
71861f28255Scgd 	if (in.buf)
71961f28255Scgd 		ckfree(in.buf);
72061f28255Scgd 	if (in.jp)
721c02b3bbdSchristos 		back_exitstatus = waitforjob(in.jp);
72261f28255Scgd 	if (quoted == 0)
72361f28255Scgd 		recordregion(startloc, dest - stackblock(), 0);
724b8bee70dSkre 	CTRACE(DBG_EXPAND, ("evalbackq: [%d] size=%d: \"%.*s\"\n",
725b8bee70dSkre 		back_exitstatus,
7267fb5a8c6Sdholland 		(int)((dest - stackblock()) - startloc),
7277fb5a8c6Sdholland 		(int)((dest - stackblock()) - startloc),
72861f28255Scgd 		stackblock() + startloc));
729ee3b307fSkre 
730ee3b307fSkre 	expdest = dest;		/* all done, expdest is all ours again */
73161f28255Scgd 	INTON;
73261f28255Scgd }
73361f28255Scgd 
73461f28255Scgd 
735ad8d5369Schristos STATIC int
736dd6b6414Skre subevalvar(const char *p, const char *str, int subtype, int startloc,
737dd6b6414Skre     int varflags)
738ad8d5369Schristos {
739ad8d5369Schristos 	char *startp;
740ad8d5369Schristos 	int saveherefd = herefd;
741ad8d5369Schristos 	struct nodelist *saveargbackq = argbackq;
742dd6b6414Skre 	int amount;
743d3aec206Schristos 
744ad8d5369Schristos 	herefd = -1;
745ee3b307fSkre 	VTRACE(DBG_EXPAND, ("subevalvar(%d) \"%.20s\" ${%.*s} sloc=%d vf=%x\n",
746ee3b307fSkre 	    subtype, p, p-str, str, startloc, varflags));
747bd208c69Skre 	argstr(p, subtype == VSASSIGN ? EXP_VARTILDE : EXP_TILDE);
748ad8d5369Schristos 	STACKSTRNUL(expdest);
749ad8d5369Schristos 	herefd = saveherefd;
750ad8d5369Schristos 	argbackq = saveargbackq;
751ad8d5369Schristos 	startp = stackblock() + startloc;
752ad8d5369Schristos 
753ad8d5369Schristos 	switch (subtype) {
754ad8d5369Schristos 	case VSASSIGN:
755ad8d5369Schristos 		setvar(str, startp, 0);
756ee3b307fSkre 		amount = startp - expdest;	/* remove what argstr added */
757d3aec206Schristos 		STADJUST(amount, expdest);
758ee3b307fSkre 		varflags &= ~VSNUL;	/*XXX Huh?   What's that achieve? */
759ee3b307fSkre 		return 1;			/* go back and eval var again */
760ad8d5369Schristos 
761ad8d5369Schristos 	case VSQUESTION:
762ad8d5369Schristos 		if (*p != CTLENDVAR) {
763ad8d5369Schristos 			outfmt(&errout, "%s\n", startp);
7649f61b804Splunky 			error(NULL);
765ad8d5369Schristos 		}
7667fb5a8c6Sdholland 		error("%.*s: parameter %snot set",
7677fb5a8c6Sdholland 		      (int)(p - str - 1),
768ad8d5369Schristos 		      str, (varflags & VSNUL) ? "null or "
769ad8d5369Schristos 					      : nullstr);
770ee9e50eaSmycroft 		/* NOTREACHED */
771ad8d5369Schristos 
772dd6b6414Skre 	default:
773dd6b6414Skre 		abort();
774dd6b6414Skre 	}
775dd6b6414Skre }
776dd6b6414Skre 
777dd6b6414Skre STATIC int
778dd6b6414Skre subevalvar_trim(const char *p, int strloc, int subtype, int startloc,
779dd6b6414Skre     int varflags, int quotes)
780dd6b6414Skre {
781dd6b6414Skre 	char *startp;
782dd6b6414Skre 	char *str;
783dd6b6414Skre 	char *loc = NULL;
784dd6b6414Skre 	char *q;
785dd6b6414Skre 	int c = 0;
786dd6b6414Skre 	int saveherefd = herefd;
787dd6b6414Skre 	struct nodelist *saveargbackq = argbackq;
788dd6b6414Skre 	int amount;
789dd6b6414Skre 
790dd6b6414Skre 	herefd = -1;
791dd6b6414Skre 	switch (subtype) {
792dd6b6414Skre 	case VSTRIMLEFT:
793dd6b6414Skre 	case VSTRIMLEFTMAX:
794dd6b6414Skre 	case VSTRIMRIGHT:
795dd6b6414Skre 	case VSTRIMRIGHTMAX:
796dd6b6414Skre 		break;
797dd6b6414Skre 	default:
798dd6b6414Skre 		abort();
799dd6b6414Skre 		break;
800dd6b6414Skre 	}
801ea89b130Skre 
802ea89b130Skre 	VTRACE(DBG_EXPAND,
803ea89b130Skre 	("subevalvar_trim(\"%.9s\", STR@%d, SUBT=%d, start@%d, vf=%x, q=%x)\n",
804ea89b130Skre 		p, strloc, subtype, startloc, varflags, quotes));
805ea89b130Skre 
806dd6b6414Skre 	argstr(p, (varflags & (VSQUOTE|VSPATQ)) == VSQUOTE ? 0 : EXP_CASE);
807dd6b6414Skre 	STACKSTRNUL(expdest);
808dd6b6414Skre 	herefd = saveherefd;
809dd6b6414Skre 	argbackq = saveargbackq;
810dd6b6414Skre 	startp = stackblock() + startloc;
811dd6b6414Skre 	str = stackblock() + strloc;
812dd6b6414Skre 
813dd6b6414Skre 	switch (subtype) {
814dd6b6414Skre 
815ad8d5369Schristos 	case VSTRIMLEFT:
816633ceb6dSchristos 		for (loc = startp; loc < str; loc++) {
817ad8d5369Schristos 			c = *loc;
818ad8d5369Schristos 			*loc = '\0';
81919d6b839Schristos 			if (patmatch(str, startp, quotes))
820ad8d5369Schristos 				goto recordleft;
821ad8d5369Schristos 			*loc = c;
82219d6b839Schristos 			if (quotes && *loc == CTLESC)
82328607542Schristos 				loc++;
824ad8d5369Schristos 		}
825ad8d5369Schristos 		return 0;
826ad8d5369Schristos 
827ad8d5369Schristos 	case VSTRIMLEFTMAX:
82828607542Schristos 		for (loc = str - 1; loc >= startp;) {
829ad8d5369Schristos 			c = *loc;
830ad8d5369Schristos 			*loc = '\0';
83119d6b839Schristos 			if (patmatch(str, startp, quotes))
832ad8d5369Schristos 				goto recordleft;
833ad8d5369Schristos 			*loc = c;
83428607542Schristos 			loc--;
83519d6b839Schristos 			if (quotes && loc > startp &&
83628607542Schristos 			    *(loc - 1) == CTLESC) {
83728607542Schristos 				for (q = startp; q < loc; q++)
83828607542Schristos 					if (*q == CTLESC)
83928607542Schristos 						q++;
84028607542Schristos 				if (q > loc)
84128607542Schristos 					loc--;
84228607542Schristos 			}
843ad8d5369Schristos 		}
844ad8d5369Schristos 		return 0;
845ad8d5369Schristos 
846ad8d5369Schristos 	case VSTRIMRIGHT:
84728607542Schristos 		for (loc = str - 1; loc >= startp;) {
84819d6b839Schristos 			if (patmatch(str, loc, quotes))
84904784d87Schristos 				goto recordright;
85028607542Schristos 			loc--;
85119d6b839Schristos 			if (quotes && loc > startp &&
85228607542Schristos 			    *(loc - 1) == CTLESC) {
85328607542Schristos 				for (q = startp; q < loc; q++)
85428607542Schristos 					if (*q == CTLESC)
85528607542Schristos 						q++;
85628607542Schristos 				if (q > loc)
85728607542Schristos 					loc--;
85828607542Schristos 			}
85928607542Schristos 		}
860ad8d5369Schristos 		return 0;
861ad8d5369Schristos 
862ad8d5369Schristos 	case VSTRIMRIGHTMAX:
86328607542Schristos 		for (loc = startp; loc < str - 1; loc++) {
86419d6b839Schristos 			if (patmatch(str, loc, quotes))
86504784d87Schristos 				goto recordright;
86619d6b839Schristos 			if (quotes && *loc == CTLESC)
86728607542Schristos 				loc++;
86828607542Schristos 		}
869ad8d5369Schristos 		return 0;
870ad8d5369Schristos 
871ad8d5369Schristos 	default:
872ad8d5369Schristos 		abort();
873ad8d5369Schristos 	}
874ad8d5369Schristos 
875ad8d5369Schristos  recordleft:
87604784d87Schristos 	*loc = c;
877d3aec206Schristos 	amount = ((str - 1) - (loc - startp)) - expdest;
878d3aec206Schristos 	STADJUST(amount, expdest);
879ad8d5369Schristos 	while (loc != str - 1)
880ad8d5369Schristos 		*startp++ = *loc++;
881ad8d5369Schristos 	return 1;
88204784d87Schristos 
88304784d87Schristos  recordright:
88404784d87Schristos 	amount = loc - expdest;
88504784d87Schristos 	STADJUST(amount, expdest);
88604784d87Schristos 	STPUTC('\0', expdest);
88704784d87Schristos 	STADJUST(-1, expdest);
88804784d87Schristos 	return 1;
889ad8d5369Schristos }
890ad8d5369Schristos 
891ad8d5369Schristos 
89261f28255Scgd /*
89361f28255Scgd  * Expand a variable, and return a pointer to the next character in the
89461f28255Scgd  * input string.
89561f28255Scgd  */
89661f28255Scgd 
897dd6b6414Skre STATIC const char *
898dd6b6414Skre evalvar(const char *p, int flag)
89961f28255Scgd {
90061f28255Scgd 	int subtype;
90137ed7877Sjtc 	int varflags;
902dd6b6414Skre 	const char *var;
90361f28255Scgd 	char *val;
904e97b0193She 	int patloc;
90561f28255Scgd 	int c;
90661f28255Scgd 	int set;
90761f28255Scgd 	int special;
90861f28255Scgd 	int startloc;
909ad8d5369Schristos 	int varlen;
910e4a2a056Sdsl 	int apply_ifs;
911df073671Skre 	const int quotes = flag & EXP_QNEEDED;
91261f28255Scgd 
9134dcb41aeSchristos 	varflags = (unsigned char)*p++;
91437ed7877Sjtc 	subtype = varflags & VSTYPE;
91561f28255Scgd 	var = p;
916*a7e8b4a5Skre 	special = subtype != VSUNKNOWN && !is_name(*p);
91761f28255Scgd 	p = strchr(p, '=') + 1;
918e4a2a056Sdsl 
919ea89b130Skre 	CTRACE(DBG_EXPAND,
920ea89b130Skre 	    ("evalvar \"%.*s\", flag=%#X quotes=%#X vf=%#X subtype=%X\n",
921*a7e8b4a5Skre 	    (int)(p - var - 1), var, flag, quotes, varflags, subtype));
922ea89b130Skre 
92361f28255Scgd  again: /* jump here after setting a variable with ${var=text} */
924018a6f78Schristos 	if (varflags & VSLINENO) {
925727a69dcSkre 		if (line_num.flags & VUNSET) {
926727a69dcSkre 			set = 0;
927727a69dcSkre 			val = NULL;
928727a69dcSkre 		} else {
929018a6f78Schristos 			set = 1;
930dd6b6414Skre 			special = p - var;
931dd6b6414Skre 			val = NULL;
932727a69dcSkre 		}
933018a6f78Schristos 	} else if (special) {
934633ceb6dSchristos 		set = varisset(var, varflags & VSNUL);
93561f28255Scgd 		val = NULL;
936265b0617Skre 		if (!set && *var == '@')
937265b0617Skre 			empty_dollar_at = 1;
93861f28255Scgd 	} else {
93961f28255Scgd 		val = lookupvar(var);
94007bae7edSchristos 		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
94161f28255Scgd 			val = NULL;
94261f28255Scgd 			set = 0;
94361f28255Scgd 		} else
94461f28255Scgd 			set = 1;
94561f28255Scgd 	}
946e4a2a056Sdsl 
947ad8d5369Schristos 	varlen = 0;
94861f28255Scgd 	startloc = expdest - stackblock();
949e4a2a056Sdsl 
95083d9b545Sast 	if (!set && uflag && *var != '@' && *var != '*') {
951eac95517Schristos 		switch (subtype) {
952eac95517Schristos 		case VSNORMAL:
953eac95517Schristos 		case VSTRIMLEFT:
954eac95517Schristos 		case VSTRIMLEFTMAX:
955eac95517Schristos 		case VSTRIMRIGHT:
956eac95517Schristos 		case VSTRIMRIGHTMAX:
957eac95517Schristos 		case VSLENGTH:
9587fb5a8c6Sdholland 			error("%.*s: parameter not set",
9597fb5a8c6Sdholland 			    (int)(p - var - 1), var);
960eac95517Schristos 			/* NOTREACHED */
961eac95517Schristos 		}
962e4a2a056Sdsl 	}
963e4a2a056Sdsl 
964265b0617Skre #if 0		/* no longer need this $@ evil ... */
965c9f333adSkre 	if (!set && subtype != VSPLUS && special && *var == '@')
966c9f333adSkre 		if (startloc > 0 && expdest[-1] == CTLQUOTEMARK)
967c9f333adSkre 			expdest--, startloc--;
968265b0617Skre #endif
969c9f333adSkre 
97061f28255Scgd 	if (set && subtype != VSPLUS) {
97161f28255Scgd 		/* insert the value of the variable */
97261f28255Scgd 		if (special) {
973dd6b6414Skre 			if (varflags & VSLINENO) {
974dd6b6414Skre 				/*
975ee3b307fSkre 				 * The LINENO hack (expansion part)
976dd6b6414Skre 				 */
977dd6b6414Skre 				while (--special > 0) {
978dd6b6414Skre /*						not needed, it is a number...
979df073671Skre 					if (quotes && NEEDESC(*var))
980dd6b6414Skre 						STPUTC(CTLESC, expdest);
981dd6b6414Skre */
982dd6b6414Skre 					STPUTC(*var++, expdest);
983dd6b6414Skre 				}
984dd6b6414Skre 			} else
985b17e1b5cSdsl 				varvalue(var, varflags&VSQUOTE, subtype, flag);
986ad8d5369Schristos 			if (subtype == VSLENGTH) {
987a0fa692dSchristos 				varlen = expdest - stackblock() - startloc;
988a0fa692dSchristos 				STADJUST(-varlen, expdest);
989ad8d5369Schristos 			}
99061f28255Scgd 		} else {
99161f28255Scgd 
992ad8d5369Schristos 			if (subtype == VSLENGTH) {
993ad8d5369Schristos 				for (; *val; val++)
994ad8d5369Schristos 					varlen++;
995df073671Skre 			} else if (quotes && varflags & VSQUOTE) {
99658e34de6Skre 				/*
99758e34de6Skre 				 * If we are going to look for magic in the
99858e34de6Skre 				 * value (quotes is set) and the expansion
99958e34de6Skre 				 * occurs inside "" (VSQUOTE) then any char
100058e34de6Skre 				 * that has any potential special meaning
100158e34de6Skre 				 * needs to have that meaning suppressed,
100258e34de6Skre 				 * so supply a CTLESC prefix for it.
100358e34de6Skre 				 */
1004df073671Skre 				for (; (c = *val) != '\0'; val++) {
1005df073671Skre 					if (NEEDESC(c))
100661f28255Scgd 						STPUTC(CTLESC, expdest);
1007df073671Skre 					STPUTC(c, expdest);
100861f28255Scgd 				}
1009df073671Skre 			} else {
101058e34de6Skre 				/*
101158e34de6Skre 				 * We are going to rmescapes() later,
101258e34de6Skre 				 * so make sure that any data char that
101358e34de6Skre 				 * might be mistaken for one of our CTLxxx
101458e34de6Skre 				 * magic chars is protected ... always.
101558e34de6Skre 				 */
101658e34de6Skre 				for (; (c = *val) != '\0'; val++) {
1017256d645dSkre 					if (ISCTL(c))
101858e34de6Skre 						STPUTC(CTLESC, expdest);
101958e34de6Skre 					STPUTC(c, expdest);
102058e34de6Skre 				}
102161f28255Scgd 			}
102261f28255Scgd 		}
1023ad8d5369Schristos 	}
1024ad8d5369Schristos 
1025ad8d5369Schristos 
1026c9f333adSkre 	if (varflags & VSQUOTE) {
10277d60739aSdsl 		if  (*var == '@' && shellparam.nparam != 1)
10287d60739aSdsl 		    apply_ifs = 1;
10297d60739aSdsl 		else {
10307d60739aSdsl 		    /*
10317d60739aSdsl 		     * Mark so that we don't apply IFS if we recurse through
10327d60739aSdsl 		     * here expanding $bar from "${foo-$bar}".
10337d60739aSdsl 		     */
10347d60739aSdsl 		    flag |= EXP_IN_QUOTES;
10357d60739aSdsl 		    apply_ifs = 0;
10367d60739aSdsl 		}
1037c9f333adSkre 	} else if (flag & EXP_IN_QUOTES) {
1038c9f333adSkre 		apply_ifs = 0;
10397d60739aSdsl 	} else
10407d60739aSdsl 		apply_ifs = 1;
1041ad8d5369Schristos 
1042ad8d5369Schristos 	switch (subtype) {
1043ad8d5369Schristos 	case VSLENGTH:
1044ad8d5369Schristos 		expdest = cvtnum(varlen, expdest);
1045e4a2a056Sdsl 		break;
1046ad8d5369Schristos 
1047ad8d5369Schristos 	case VSNORMAL:
1048ad8d5369Schristos 		break;
1049ad8d5369Schristos 
1050ad8d5369Schristos 	case VSPLUS:
1051e4a2a056Sdsl 		set = !set;
1052e4a2a056Sdsl 		/* FALLTHROUGH */
1053ad8d5369Schristos 	case VSMINUS:
1054cf199667Schristos 		if (!set) {
1055c6cbc16dSdsl 			argstr(p, flag | (apply_ifs ? EXP_IFS_SPLIT : 0));
1056e4a2a056Sdsl 			/*
1057e4a2a056Sdsl 			 * ${x-a b c} doesn't get split, but removing the
10585c056f67Swiz 			 * 'apply_ifs = 0' apparently breaks ${1+"$@"}..
1059e4a2a056Sdsl 			 * ${x-'a b' c} should generate 2 args.
1060e4a2a056Sdsl 			 */
1061dec2ca90Schristos 			if (*p != CTLENDVAR)
1062e4a2a056Sdsl 			/* We should have marked stuff already */
1063e4a2a056Sdsl 				apply_ifs = 0;
1064cf199667Schristos 		}
1065ad8d5369Schristos 		break;
1066ad8d5369Schristos 
1067ad8d5369Schristos 	case VSTRIMLEFT:
1068ad8d5369Schristos 	case VSTRIMLEFTMAX:
1069ad8d5369Schristos 	case VSTRIMRIGHT:
1070ad8d5369Schristos 	case VSTRIMRIGHTMAX:
1071df029fccSkre 		if (!set) {
1072df029fccSkre 			set = 1;  /* allow argbackq to be advanced if needed */
1073ad8d5369Schristos 			break;
1074df029fccSkre 		}
1075ad8d5369Schristos 		/*
1076ad8d5369Schristos 		 * Terminate the string and start recording the pattern
1077ad8d5369Schristos 		 * right after it
1078ad8d5369Schristos 		 */
1079ad8d5369Schristos 		STPUTC('\0', expdest);
1080e97b0193She 		patloc = expdest - stackblock();
1081dd6b6414Skre 		if (subevalvar_trim(p, patloc, subtype, startloc, varflags,
1082dd6b6414Skre 		    quotes) == 0) {
1083e97b0193She 			int amount = (expdest - stackblock() - patloc) + 1;
1084633ceb6dSchristos 			STADJUST(-amount, expdest);
1085633ceb6dSchristos 		}
10861fbf0781Smycroft 		/* Remove any recorded regions beyond start of variable */
10871fbf0781Smycroft 		removerecordregions(startloc);
1088e4a2a056Sdsl 		apply_ifs = 1;
1089e4a2a056Sdsl 		break;
1090ad8d5369Schristos 
1091ad8d5369Schristos 	case VSASSIGN:
1092ad8d5369Schristos 	case VSQUESTION:
1093e4a2a056Sdsl 		if (set)
1094e4a2a056Sdsl 			break;
1095dd6b6414Skre 		if (subevalvar(p, var, subtype, startloc, varflags)) {
1096dd6b6414Skre 			/* if subevalvar() returns, it always returns 1 */
1097dd6b6414Skre 
109829cad877Schristos 			varflags &= ~VSNUL;
10991fbf0781Smycroft 			/*
11001fbf0781Smycroft 			 * Remove any recorded regions beyond
11011fbf0781Smycroft 			 * start of variable
11021fbf0781Smycroft 			 */
11031fbf0781Smycroft 			removerecordregions(startloc);
110461f28255Scgd 			goto again;
110529cad877Schristos 		}
1106ee3b307fSkre 		apply_ifs = 0;		/* never executed */
1107ad8d5369Schristos 		break;
1108ad8d5369Schristos 
1109*a7e8b4a5Skre 	case VSUNKNOWN:
1110*a7e8b4a5Skre 		VTRACE(DBG_EXPAND,
1111*a7e8b4a5Skre 	    	   ("evalvar \"%.*s\", unknown [%p %p] \"%.3s\" (%#2x %#2x)\n",
1112*a7e8b4a5Skre 		    (int)(p - var - 1), var, var, p, p, p[0] & 0xFF, p[1] & 0xFF));
1113*a7e8b4a5Skre 
1114*a7e8b4a5Skre 		if ((p - var) <= 1)
1115*a7e8b4a5Skre 			error("%d: unknown expansion type", line_number);
1116*a7e8b4a5Skre 		else {
1117*a7e8b4a5Skre 			if (*p == '#')	/* only VSUNKNOWN as a ${#var:...} */
1118*a7e8b4a5Skre 				error("%d: ${#%.*s%c..}: unknown modifier",
1119*a7e8b4a5Skre 				     line_number, (int)(p - var - 1),
1120*a7e8b4a5Skre 				     var, p[1]&0xFF);
1121*a7e8b4a5Skre 
1122*a7e8b4a5Skre 			if (*p == CTLESC)
1123*a7e8b4a5Skre 				p++;
1124*a7e8b4a5Skre 			error("%d: ${%.*s%c..}: unknown modifier",
1125*a7e8b4a5Skre 			    line_number, (int)(p - var - 1), var, (*p & 0xFF));
1126*a7e8b4a5Skre 		}
1127*a7e8b4a5Skre 		/* NOTREACHED */
1128*a7e8b4a5Skre 
1129ad8d5369Schristos 	default:
1130ad8d5369Schristos 		abort();
113161f28255Scgd 	}
1132ad8d5369Schristos 
1133e4a2a056Sdsl 	if (apply_ifs)
1134e4a2a056Sdsl 		recordregion(startloc, expdest - stackblock(),
1135e4a2a056Sdsl 			     varflags & VSQUOTE);
1136e4a2a056Sdsl 
113761f28255Scgd 	if (subtype != VSNORMAL) {	/* skip to end of alternative */
113861f28255Scgd 		int nesting = 1;
113961f28255Scgd 		for (;;) {
114061f28255Scgd 			if ((c = *p++) == CTLESC)
114161f28255Scgd 				p++;
1142727a69dcSkre 			else if (c == CTLNONL)
1143727a69dcSkre 				;
114461f28255Scgd 			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
1145c823b55cSmycroft 				if (set)
114661f28255Scgd 					argbackq = argbackq->next;
114761f28255Scgd 			} else if (c == CTLVAR) {
114861f28255Scgd 				if ((*p++ & VSTYPE) != VSNORMAL)
114961f28255Scgd 					nesting++;
115061f28255Scgd 			} else if (c == CTLENDVAR) {
115161f28255Scgd 				if (--nesting == 0)
115261f28255Scgd 					break;
115361f28255Scgd 			}
115461f28255Scgd 		}
115561f28255Scgd 	}
115661f28255Scgd 	return p;
115761f28255Scgd }
115861f28255Scgd 
115961f28255Scgd 
116061f28255Scgd 
116161f28255Scgd /*
1162ee3b307fSkre  * Test whether a special parameter is set.
116361f28255Scgd  */
116461f28255Scgd 
116561f28255Scgd STATIC int
1166dd6b6414Skre varisset(const char *name, int nulok)
116761f28255Scgd {
1168633ceb6dSchristos 	if (*name == '!')
1169633ceb6dSchristos 		return backgndpid != -1;
1170633ceb6dSchristos 	else if (*name == '@' || *name == '*') {
117161f28255Scgd 		if (*shellparam.p == NULL)
117261f28255Scgd 			return 0;
1173633ceb6dSchristos 
1174633ceb6dSchristos 		if (nulok) {
11754a521d35Schristos 			char **av;
11764a521d35Schristos 
11774a521d35Schristos 			for (av = shellparam.p; *av; av++)
11784a521d35Schristos 				if (**av != '\0')
1179633ceb6dSchristos 					return 1;
1180633ceb6dSchristos 			return 0;
1181633ceb6dSchristos 		}
11824b99c4d7Schristos 	} else if (is_digit(*name)) {
11834a521d35Schristos 		char *ap;
1184ee3b307fSkre 		long num;
1185ee3b307fSkre 
1186ee3b307fSkre 		/*
1187ee3b307fSkre 		 * handle overflow sensibly (the *ap tests should never fail)
1188ee3b307fSkre 		 */
1189ee3b307fSkre 		errno = 0;
1190ee3b307fSkre 		num = strtol(name, &ap, 10);
1191ee3b307fSkre 		if (errno != 0 || (*ap != '\0' && *ap != '='))
1192ee3b307fSkre 			return 0;
1193633ceb6dSchristos 
11944a521d35Schristos 		if (num == 0)
11954a521d35Schristos 			ap = arg0;
1196dd6b6414Skre 		else if (num > shellparam.nparam)
1197dd6b6414Skre 			return 0;
11984a521d35Schristos 		else
11994a521d35Schristos 			ap = shellparam.p[num - 1];
12004a521d35Schristos 
12014a521d35Schristos 		if (nulok && (ap == NULL || *ap == '\0'))
120261f28255Scgd 			return 0;
120361f28255Scgd 	}
120461f28255Scgd 	return 1;
120561f28255Scgd }
120661f28255Scgd 
120761f28255Scgd 
120861f28255Scgd 
120961f28255Scgd /*
121061f28255Scgd  * Add the value of a specialized variable to the stack string.
121161f28255Scgd  */
121261f28255Scgd 
121361f28255Scgd STATIC void
1214dd6b6414Skre varvalue(const char *name, int quoted, int subtype, int flag)
121561f28255Scgd {
121661f28255Scgd 	int num;
121761f28255Scgd 	char *p;
121861f28255Scgd 	int i;
1219c83568a7Skre 	int sep;
122061f28255Scgd 	char **ap;
1221c9f333adSkre #ifdef DEBUG
1222c9f333adSkre 	char *start = expdest;
1223c9f333adSkre #endif
1224c9f333adSkre 
1225c9f333adSkre 	VTRACE(DBG_EXPAND, ("varvalue(%c%s, sub=%d, fl=%#x)", *name,
1226c9f333adSkre 	    quoted ? ", quoted" : "", subtype, flag));
122761f28255Scgd 
1228dd6b6414Skre 	if (subtype == VSLENGTH)	/* no magic required ... */
12296654ff1cSkre 		flag &= ~(EXP_FULL | EXP_QNEEDED);
1230dd6b6414Skre 
123137ed7877Sjtc #define STRTODEST(p) \
123237ed7877Sjtc 	do {\
1233df073671Skre 		if ((flag & EXP_QNEEDED) && quoted) { \
123437ed7877Sjtc 			while (*p) { \
1235df073671Skre 				if (NEEDESC(*p)) \
123637ed7877Sjtc 					STPUTC(CTLESC, expdest); \
123737ed7877Sjtc 				STPUTC(*p++, expdest); \
123837ed7877Sjtc 			} \
123937ed7877Sjtc 		} else \
1240256d645dSkre 			while (*p) { \
12416654ff1cSkre 				if ((flag & EXP_QNEEDED) && ISCTL(*p)) \
1242256d645dSkre 					STPUTC(CTLESC, expdest); \
124337ed7877Sjtc 				STPUTC(*p++, expdest); \
1244256d645dSkre 			} \
124537ed7877Sjtc 	} while (0)
124637ed7877Sjtc 
124737ed7877Sjtc 
12484b99c4d7Schristos 	switch (*name) {
124961f28255Scgd 	case '$':
125061f28255Scgd 		num = rootpid;
1251dd6b6414Skre 		break;
125261f28255Scgd 	case '?':
1253c02b3bbdSchristos 		num = exitstatus;
1254dd6b6414Skre 		break;
125561f28255Scgd 	case '#':
125661f28255Scgd 		num = shellparam.nparam;
1257dd6b6414Skre 		break;
125861f28255Scgd 	case '!':
125961f28255Scgd 		num = backgndpid;
126061f28255Scgd 		break;
126161f28255Scgd 	case '-':
1262f359a311Skre 		for (i = 0; i < option_flags; i++) {
1263f359a311Skre 			if (optlist[optorder[i]].val)
1264f359a311Skre 				STPUTC(optlist[optorder[i]].letter, expdest);
126561f28255Scgd 		}
1266c9f333adSkre 		VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start));
1267dd6b6414Skre 		return;
126861f28255Scgd 	case '@':
1269dd6b6414Skre 		if (flag & EXP_SPLIT && quoted) {
1270c9f333adSkre 			VTRACE(DBG_EXPAND, (": $@ split (%d)\n",
1271c9f333adSkre 			    shellparam.nparam));
1272265b0617Skre #if 0
1273c9f333adSkre 		/* GROSS HACK */
1274c9f333adSkre 			if (shellparam.nparam == 0 &&
1275c9f333adSkre 				expdest[-1] == CTLQUOTEMARK)
1276c9f333adSkre 					expdest--;
1277c9f333adSkre 		/* KCAH SSORG */
1278265b0617Skre #endif
1279265b0617Skre 			if (shellparam.nparam == 0)
1280265b0617Skre 				empty_dollar_at = 1;
1281265b0617Skre 
128261f28255Scgd 			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
1283265b0617Skre 				if (*p == '\0') {
1284265b0617Skre 					/* retain an explicit null string */
1285265b0617Skre 					STPUTC(CTLQUOTEMARK, expdest);
1286265b0617Skre 					STPUTC(CTLQUOTEEND, expdest);
1287265b0617Skre 				} else
128837ed7877Sjtc 					STRTODEST(p);
1289413b7762Sdsl 				if (*ap)
1290413b7762Sdsl 					/* A NUL separates args inside "" */
12911fbf0781Smycroft 					STPUTC('\0', expdest);
12921fbf0781Smycroft 			}
1293dd6b6414Skre 			return;
12941fbf0781Smycroft 		}
12951fbf0781Smycroft 		/* fall through */
12961fbf0781Smycroft 	case '*':
12971fbf0781Smycroft 		sep = ifsval()[0];
12981fbf0781Smycroft 		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
12991fbf0781Smycroft 			STRTODEST(p);
1300bda1f28dSkre 			if (!*ap)
1301bda1f28dSkre 				break;
1302c83568a7Skre 			if (sep) {
1303df073671Skre 				if (quoted && (flag & EXP_QNEEDED) &&
1304df073671Skre 				    NEEDESC(sep))
1305c83568a7Skre 					STPUTC(CTLESC, expdest);
130661f28255Scgd 				STPUTC(sep, expdest);
1307c83568a7Skre 			} else
1308c83568a7Skre 			    if ((flag & (EXP_SPLIT|EXP_IN_QUOTES)) == EXP_SPLIT
1309bda1f28dSkre 			      && !quoted && **ap != '\0')
1310bda1f28dSkre 				STPUTC('\0', expdest);
131161f28255Scgd 		}
1312c9f333adSkre 		VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start));
1313dd6b6414Skre 		return;
131461f28255Scgd 	default:
13154b99c4d7Schristos 		if (is_digit(*name)) {
1316dd6b6414Skre 			long lnum;
1317dd6b6414Skre 
1318dd6b6414Skre 			errno = 0;
1319dd6b6414Skre 			lnum = strtol(name, &p, 10);
1320dd6b6414Skre 			if (errno != 0 || (*p != '\0' && *p != '='))
1321dd6b6414Skre 				return;
1322dd6b6414Skre 
1323dd6b6414Skre 			if (lnum == 0)
1324dd6b6414Skre 				p = arg0;
1325dd6b6414Skre 			else if (lnum > 0 && lnum <= shellparam.nparam)
1326dd6b6414Skre 				p = shellparam.p[lnum - 1];
1327dd6b6414Skre 			else
1328dd6b6414Skre 				return;
132937ed7877Sjtc 			STRTODEST(p);
133061f28255Scgd 		}
1331c9f333adSkre 		VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start));
1332dd6b6414Skre 		return;
13334b99c4d7Schristos 	}
1334dd6b6414Skre 	/*
1335dd6b6414Skre 	 * only the specials with an int value arrive here
1336dd6b6414Skre 	 */
1337c9f333adSkre 	VTRACE(DBG_EXPAND, ("(%d)", num));
1338dd6b6414Skre 	expdest = cvtnum(num, expdest);
1339c9f333adSkre 	VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start));
134061f28255Scgd }
134161f28255Scgd 
134261f28255Scgd 
134361f28255Scgd 
134461f28255Scgd /*
13451594850fSsoren  * Record the fact that we have to scan this region of the
134661f28255Scgd  * string for IFS characters.
134761f28255Scgd  */
134861f28255Scgd 
134961f28255Scgd STATIC void
1350e4a2a056Sdsl recordregion(int start, int end, int inquotes)
13515dad1439Scgd {
135248250187Stls 	struct ifsregion *ifsp;
135361f28255Scgd 
135458810bf0Skre 	VTRACE(DBG_EXPAND, ("recordregion(%d,%d,%d)\n", start, end, inquotes));
135561f28255Scgd 	if (ifslastp == NULL) {
135661f28255Scgd 		ifsp = &ifsfirst;
135761f28255Scgd 	} else {
13582078d2c0Sdsl 		if (ifslastp->endoff == start
13592078d2c0Sdsl 		    && ifslastp->inquotes == inquotes) {
1360c6cbc16dSdsl 			/* extend previous area */
1361c6cbc16dSdsl 			ifslastp->endoff = end;
1362c6cbc16dSdsl 			return;
1363c6cbc16dSdsl 		}
136461f28255Scgd 		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
136561f28255Scgd 		ifslastp->next = ifsp;
136661f28255Scgd 	}
136761f28255Scgd 	ifslastp = ifsp;
136861f28255Scgd 	ifslastp->next = NULL;
136961f28255Scgd 	ifslastp->begoff = start;
137061f28255Scgd 	ifslastp->endoff = end;
1371e4a2a056Sdsl 	ifslastp->inquotes = inquotes;
137261f28255Scgd }
137361f28255Scgd 
137461f28255Scgd 
137561f28255Scgd 
137661f28255Scgd /*
137761f28255Scgd  * Break the argument string into pieces based upon IFS and add the
137861f28255Scgd  * strings to the argument list.  The regions of the string to be
137961f28255Scgd  * searched for IFS characters have been stored by recordregion.
138061f28255Scgd  */
138161f28255Scgd STATIC void
1382c02b3bbdSchristos ifsbreakup(char *string, struct arglist *arglist)
138361f28255Scgd {
138461f28255Scgd 	struct ifsregion *ifsp;
138561f28255Scgd 	struct strlist *sp;
138661f28255Scgd 	char *start;
138748250187Stls 	char *p;
138861f28255Scgd 	char *q;
13893d424690Schristos 	const char *ifs;
1390e4a2a056Sdsl 	const char *ifsspc;
1391413b7762Sdsl 	int had_param_ch = 0;
139261f28255Scgd 
139361f28255Scgd 	start = string;
1394e4a2a056Sdsl 
1395ea89b130Skre 	VTRACE(DBG_EXPAND, ("ifsbreakup(\"%s\")", string)); /* misses \0's */
1396e4a2a056Sdsl 	if (ifslastp == NULL) {
1397e4a2a056Sdsl 		/* Return entire argument, IFS doesn't apply to any of it */
1398ea89b130Skre 		VTRACE(DBG_EXPAND, ("no regions\n", string));
139978204bbfSchristos 		sp = stalloc(sizeof(*sp));
1400e4a2a056Sdsl 		sp->text = start;
1401e4a2a056Sdsl 		*arglist->lastp = sp;
1402e4a2a056Sdsl 		arglist->lastp = &sp->next;
1403e4a2a056Sdsl 		return;
1404e4a2a056Sdsl 	}
1405e4a2a056Sdsl 
1406ca12a0b8Schristos 	ifs = ifsval();
1407e4a2a056Sdsl 
1408e4a2a056Sdsl 	for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
140961f28255Scgd 		p = string + ifsp->begoff;
1410ea89b130Skre 		VTRACE(DBG_EXPAND, (" !%.*s!(%d)", ifsp->endoff-ifsp->begoff,
1411ea89b130Skre 		    p, ifsp->endoff-ifsp->begoff));
141261f28255Scgd 		while (p < string + ifsp->endoff) {
1413413b7762Sdsl 			had_param_ch = 1;
141461f28255Scgd 			q = p;
1415c9f333adSkre 			if (IS_BORING(*p)) {
1416727a69dcSkre 				p++;
1417727a69dcSkre 				continue;
1418727a69dcSkre 			}
141961f28255Scgd 			if (*p == CTLESC)
142061f28255Scgd 				p++;
1421413b7762Sdsl 			if (ifsp->inquotes) {
1422413b7762Sdsl 				/* Only NULs (should be from "$@") end args */
1423e4a2a056Sdsl 				if (*p != 0) {
1424e4a2a056Sdsl 					p++;
1425e4a2a056Sdsl 					continue;
1426e4a2a056Sdsl 				}
1427413b7762Sdsl 				ifsspc = NULL;
1428ea89b130Skre 				VTRACE(DBG_EXPAND, (" \\0 nxt:\"%s\" ", p));
1429e4a2a056Sdsl 			} else {
1430e4a2a056Sdsl 				if (!strchr(ifs, *p)) {
1431e4a2a056Sdsl 					p++;
1432e4a2a056Sdsl 					continue;
1433e4a2a056Sdsl 				}
1434c58b829dSdsl 				had_param_ch = 0;
1435e4a2a056Sdsl 				ifsspc = strchr(" \t\n", *p);
1436e4a2a056Sdsl 
14371fbf0781Smycroft 				/* Ignore IFS whitespace at start */
1438e4a2a056Sdsl 				if (q == start && ifsspc != NULL) {
14391fbf0781Smycroft 					p++;
14401fbf0781Smycroft 					start = p;
14411fbf0781Smycroft 					continue;
14421fbf0781Smycroft 				}
1443e4a2a056Sdsl 			}
1444e4a2a056Sdsl 
1445e4a2a056Sdsl 			/* Save this argument... */
144661f28255Scgd 			*q = '\0';
1447ea89b130Skre 			VTRACE(DBG_EXPAND, ("<%s>", start));
144878204bbfSchristos 			sp = stalloc(sizeof(*sp));
144961f28255Scgd 			sp->text = start;
145061f28255Scgd 			*arglist->lastp = sp;
145161f28255Scgd 			arglist->lastp = &sp->next;
14521fbf0781Smycroft 			p++;
1453e4a2a056Sdsl 
1454328c4365Sdsl 			if (ifsspc != NULL) {
1455328c4365Sdsl 				/* Ignore further trailing IFS whitespace */
1456e4a2a056Sdsl 				for (; p < string + ifsp->endoff; p++) {
145761f28255Scgd 					q = p;
1458727a69dcSkre 					if (*p == CTLNONL)
1459727a69dcSkre 						continue;
146061f28255Scgd 					if (*p == CTLESC)
146161f28255Scgd 						p++;
14621fbf0781Smycroft 					if (strchr(ifs, *p) == NULL) {
14631fbf0781Smycroft 						p = q;
14641fbf0781Smycroft 						break;
1465e4a2a056Sdsl 					}
1466e4a2a056Sdsl 					if (strchr(" \t\n", *p) == NULL) {
1467328c4365Sdsl 						p++;
146861f28255Scgd 						break;
146961f28255Scgd 					}
147061f28255Scgd 				}
147161f28255Scgd 			}
147261f28255Scgd 			start = p;
147361f28255Scgd 		}
147461f28255Scgd 	}
1475e4a2a056Sdsl 
1476265b0617Skre /*
1477c9f333adSkre 	while (*start == CTLQUOTEEND)
1478c9f333adSkre 		start++;
1479265b0617Skre */
1480c9f333adSkre 
1481328c4365Sdsl 	/*
1482328c4365Sdsl 	 * Save anything left as an argument.
1483328c4365Sdsl 	 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
1484328c4365Sdsl 	 * generating 2 arguments, the second of which is empty.
1485328c4365Sdsl 	 * Some recent clarification of the Posix spec say that it
1486328c4365Sdsl 	 * should only generate one....
1487328c4365Sdsl 	 */
1488413b7762Sdsl 	if (had_param_ch || *start != 0) {
1489ea89b130Skre 		VTRACE(DBG_EXPAND, (" T<%s>", start));
149078204bbfSchristos 		sp = stalloc(sizeof(*sp));
149161f28255Scgd 		sp->text = start;
149261f28255Scgd 		*arglist->lastp = sp;
149361f28255Scgd 		arglist->lastp = &sp->next;
149461f28255Scgd 	}
1495ea89b130Skre 	VTRACE(DBG_EXPAND, ("\n"));
149661f28255Scgd }
149761f28255Scgd 
1498f7c8df6dSchristos STATIC void
1499c02b3bbdSchristos ifsfree(void)
1500f7c8df6dSchristos {
1501f7c8df6dSchristos 	while (ifsfirst.next != NULL) {
1502f7c8df6dSchristos 		struct ifsregion *ifsp;
1503f7c8df6dSchristos 		INTOFF;
1504f7c8df6dSchristos 		ifsp = ifsfirst.next->next;
1505f7c8df6dSchristos 		ckfree(ifsfirst.next);
1506f7c8df6dSchristos 		ifsfirst.next = ifsp;
1507f7c8df6dSchristos 		INTON;
1508f7c8df6dSchristos 	}
1509f7c8df6dSchristos 	ifslastp = NULL;
1510f7c8df6dSchristos 	ifsfirst.next = NULL;
1511f7c8df6dSchristos }
1512f7c8df6dSchristos 
151361f28255Scgd 
151461f28255Scgd 
151561f28255Scgd /*
151661f28255Scgd  * Expand shell metacharacters.  At this point, the only control characters
151761f28255Scgd  * should be escapes.  The results are stored in the list exparg.
151861f28255Scgd  */
151961f28255Scgd 
152061f28255Scgd char *expdir;
152161f28255Scgd 
152261f28255Scgd 
152361f28255Scgd STATIC void
1524c02b3bbdSchristos expandmeta(struct strlist *str, int flag)
152561f28255Scgd {
152661f28255Scgd 	char *p;
152761f28255Scgd 	struct strlist **savelastp;
152861f28255Scgd 	struct strlist *sp;
152961f28255Scgd 	char c;
153037ed7877Sjtc 	/* TODO - EXP_REDIR */
153161f28255Scgd 
153261f28255Scgd 	while (str) {
153361f28255Scgd 		p = str->text;
153461f28255Scgd 		for (;;) {			/* fast check for meta chars */
153561f28255Scgd 			if ((c = *p++) == '\0')
153661f28255Scgd 				goto nometa;
1537c9f333adSkre 			if (c == '*' || c == '?' || c == '[' /* || c == '!' */)
153861f28255Scgd 				break;
153961f28255Scgd 		}
154061f28255Scgd 		savelastp = exparg.lastp;
154161f28255Scgd 		INTOFF;
154237ed7877Sjtc 		if (expdir == NULL) {
154337ed7877Sjtc 			int i = strlen(str->text);
154437ed7877Sjtc 			expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
154537ed7877Sjtc 		}
154637ed7877Sjtc 
154761f28255Scgd 		expmeta(expdir, str->text);
154861f28255Scgd 		ckfree(expdir);
154961f28255Scgd 		expdir = NULL;
155061f28255Scgd 		INTON;
155161f28255Scgd 		if (exparg.lastp == savelastp) {
155237ed7877Sjtc 			/*
155337ed7877Sjtc 			 * no matches
155437ed7877Sjtc 			 */
155561f28255Scgd  nometa:
155661f28255Scgd 			*exparg.lastp = str;
155761f28255Scgd 			rmescapes(str->text);
155861f28255Scgd 			exparg.lastp = &str->next;
155961f28255Scgd 		} else {
156061f28255Scgd 			*exparg.lastp = NULL;
156161f28255Scgd 			*savelastp = sp = expsort(*savelastp);
156261f28255Scgd 			while (sp->next != NULL)
156361f28255Scgd 				sp = sp->next;
156461f28255Scgd 			exparg.lastp = &sp->next;
156561f28255Scgd 		}
156661f28255Scgd 		str = str->next;
156761f28255Scgd 	}
156861f28255Scgd }
156961f28255Scgd 
1570dd6b6414Skre STATIC void
1571dd6b6414Skre add_args(struct strlist *str)
1572dd6b6414Skre {
1573dd6b6414Skre 	while (str) {
1574dd6b6414Skre 		*exparg.lastp = str;
1575dd6b6414Skre 		rmescapes(str->text);
1576dd6b6414Skre 		exparg.lastp = &str->next;
1577dd6b6414Skre 		str = str->next;
1578dd6b6414Skre 	}
1579dd6b6414Skre }
1580dd6b6414Skre 
158161f28255Scgd 
158261f28255Scgd /*
158361f28255Scgd  * Do metacharacter (i.e. *, ?, [...]) expansion.
158461f28255Scgd  */
158561f28255Scgd 
158661f28255Scgd STATIC void
1587c02b3bbdSchristos expmeta(char *enddir, char *name)
158861f28255Scgd {
158948250187Stls 	char *p;
15903d424690Schristos 	const char *cp;
159161f28255Scgd 	char *q;
159261f28255Scgd 	char *start;
159361f28255Scgd 	char *endname;
159461f28255Scgd 	int metaflag;
159561f28255Scgd 	struct stat statb;
159661f28255Scgd 	DIR *dirp;
159761f28255Scgd 	struct dirent *dp;
159861f28255Scgd 	int atend;
159961f28255Scgd 	int matchdot;
160061f28255Scgd 
1601bcacfd9aSkre 	CTRACE(DBG_EXPAND|DBG_MATCH, ("expmeta(\"%s\")\n", name));
160261f28255Scgd 	metaflag = 0;
160361f28255Scgd 	start = name;
160461f28255Scgd 	for (p = name ; ; p++) {
160561f28255Scgd 		if (*p == '*' || *p == '?')
160661f28255Scgd 			metaflag = 1;
160761f28255Scgd 		else if (*p == '[') {
160861f28255Scgd 			q = p + 1;
160914482abcSkre 			if (*q == '!' || *q == '^')
161061f28255Scgd 				q++;
161161f28255Scgd 			for (;;) {
1612c9f333adSkre 				while (IS_BORING(*q))
16131fbf0781Smycroft 					q++;
161414482abcSkre 				if (*q == ']') {
161514482abcSkre 					q++;
161614482abcSkre 					metaflag = 1;
161714482abcSkre 					break;
161814482abcSkre 				}
161914482abcSkre 				if (*q == '[' && q[1] == ':') {
162014482abcSkre 					/*
162114482abcSkre 					 * character class, look for :] ending
162214482abcSkre 					 * also stop on ']' (end bracket expr)
162314482abcSkre 					 * or '\0' or '/' (end pattern)
162414482abcSkre 					 */
162514482abcSkre 					while (*++q != '\0' && *q != ']' &&
162614482abcSkre 					    *q != '/') {
162714482abcSkre 						if (*q == CTLESC) {
162814482abcSkre 							if (*++q == '\0')
162914482abcSkre 								break;
163014482abcSkre 							if (*q == '/')
163114482abcSkre 								break;
163214482abcSkre 						} else if (*q == ':' &&
163314482abcSkre 						    q[1] == ']')
163414482abcSkre 							break;
163514482abcSkre 					}
163614482abcSkre 					if (*q == ':') {
163714482abcSkre 						/*
163814482abcSkre 						 * stopped at ':]'
163914482abcSkre 						 * still in [...]
164014482abcSkre 						 * skip ":]" and continue;
164114482abcSkre 						 */
164214482abcSkre 						q += 2;
164314482abcSkre 						continue;
164414482abcSkre 					}
164514482abcSkre 
164614482abcSkre 					/* done at end of pattern, not [...] */
164714482abcSkre 					if (*q == '\0' || *q == '/')
164814482abcSkre 						break;
164914482abcSkre 
165014482abcSkre 					/* found the ']', we have a [...] */
165114482abcSkre 					metaflag = 1;
165214482abcSkre 					q++;	/* skip ']' */
165314482abcSkre 					break;
165414482abcSkre 				}
165561f28255Scgd 				if (*q == CTLESC)
165661f28255Scgd 					q++;
165714482abcSkre 				/* end of pattern cannot be escaped */
165861f28255Scgd 				if (*q == '/' || *q == '\0')
165961f28255Scgd 					break;
166014482abcSkre 				q++;
166161f28255Scgd 			}
166261f28255Scgd 		} else if (*p == '\0')
166361f28255Scgd 			break;
1664c9f333adSkre 		else if (IS_BORING(*p))
16651fbf0781Smycroft 			continue;
166661f28255Scgd 		else if (*p == CTLESC)
166761f28255Scgd 			p++;
166861f28255Scgd 		if (*p == '/') {
166961f28255Scgd 			if (metaflag)
167061f28255Scgd 				break;
167161f28255Scgd 			start = p + 1;
167261f28255Scgd 		}
167361f28255Scgd 	}
167461f28255Scgd 	if (metaflag == 0) {	/* we've reached the end of the file name */
167561f28255Scgd 		if (enddir != expdir)
167661f28255Scgd 			metaflag++;
167761f28255Scgd 		for (p = name ; ; p++) {
1678c9f333adSkre 			if (IS_BORING(*p))
16791fbf0781Smycroft 				continue;
168061f28255Scgd 			if (*p == CTLESC)
168161f28255Scgd 				p++;
168261f28255Scgd 			*enddir++ = *p;
168361f28255Scgd 			if (*p == '\0')
168461f28255Scgd 				break;
168561f28255Scgd 		}
168659771e30Smycroft 		if (metaflag == 0 || lstat(expdir, &statb) >= 0)
168761f28255Scgd 			addfname(expdir);
168861f28255Scgd 		return;
168961f28255Scgd 	}
169061f28255Scgd 	endname = p;
169161f28255Scgd 	if (start != name) {
169261f28255Scgd 		p = name;
169361f28255Scgd 		while (p < start) {
1694c9f333adSkre 			while (IS_BORING(*p))
16951fbf0781Smycroft 				p++;
169661f28255Scgd 			if (*p == CTLESC)
169761f28255Scgd 				p++;
169861f28255Scgd 			*enddir++ = *p++;
169961f28255Scgd 		}
170061f28255Scgd 	}
170161f28255Scgd 	if (enddir == expdir) {
17023d424690Schristos 		cp = ".";
170361f28255Scgd 	} else if (enddir == expdir + 1 && *expdir == '/') {
17043d424690Schristos 		cp = "/";
170561f28255Scgd 	} else {
17063d424690Schristos 		cp = expdir;
170761f28255Scgd 		enddir[-1] = '\0';
170861f28255Scgd 	}
17093d424690Schristos 	if ((dirp = opendir(cp)) == NULL)
171061f28255Scgd 		return;
171161f28255Scgd 	if (enddir != expdir)
171261f28255Scgd 		enddir[-1] = '/';
171361f28255Scgd 	if (*endname == 0) {
171461f28255Scgd 		atend = 1;
171561f28255Scgd 	} else {
171661f28255Scgd 		atend = 0;
171761f28255Scgd 		*endname++ = '\0';
171861f28255Scgd 	}
171961f28255Scgd 	matchdot = 0;
17201fbf0781Smycroft 	p = start;
1721c9f333adSkre 	while (IS_BORING(*p))
17221fbf0781Smycroft 		p++;
17231fbf0781Smycroft 	if (*p == CTLESC)
17241fbf0781Smycroft 		p++;
17251fbf0781Smycroft 	if (*p == '.')
172661f28255Scgd 		matchdot++;
172761f28255Scgd 	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
172861f28255Scgd 		if (dp->d_name[0] == '.' && ! matchdot)
172961f28255Scgd 			continue;
173028607542Schristos 		if (patmatch(start, dp->d_name, 0)) {
173161f28255Scgd 			if (atend) {
173261f28255Scgd 				scopy(dp->d_name, enddir);
173361f28255Scgd 				addfname(expdir);
173461f28255Scgd 			} else {
17353d424690Schristos 				for (p = enddir, cp = dp->d_name;
17363d424690Schristos 				     (*p++ = *cp++) != '\0';)
173707bae7edSchristos 					continue;
173861f28255Scgd 				p[-1] = '/';
173961f28255Scgd 				expmeta(p, endname);
174061f28255Scgd 			}
174161f28255Scgd 		}
174261f28255Scgd 	}
174361f28255Scgd 	closedir(dirp);
174461f28255Scgd 	if (! atend)
174561f28255Scgd 		endname[-1] = '/';
174661f28255Scgd }
174761f28255Scgd 
174861f28255Scgd 
174961f28255Scgd /*
175061f28255Scgd  * Add a file name to the list.
175161f28255Scgd  */
175261f28255Scgd 
175361f28255Scgd STATIC void
1754c02b3bbdSchristos addfname(char *name)
175561f28255Scgd {
175661f28255Scgd 	char *p;
175761f28255Scgd 	struct strlist *sp;
175861f28255Scgd 
175961f28255Scgd 	p = stalloc(strlen(name) + 1);
176061f28255Scgd 	scopy(name, p);
176178204bbfSchristos 	sp = stalloc(sizeof(*sp));
176261f28255Scgd 	sp->text = p;
176361f28255Scgd 	*exparg.lastp = sp;
176461f28255Scgd 	exparg.lastp = &sp->next;
176561f28255Scgd }
176661f28255Scgd 
176761f28255Scgd 
176861f28255Scgd /*
176961f28255Scgd  * Sort the results of file name expansion.  It calculates the number of
177061f28255Scgd  * strings to sort and then calls msort (short for merge sort) to do the
177161f28255Scgd  * work.
177261f28255Scgd  */
177361f28255Scgd 
177461f28255Scgd STATIC struct strlist *
1775c02b3bbdSchristos expsort(struct strlist *str)
177661f28255Scgd {
177761f28255Scgd 	int len;
177861f28255Scgd 	struct strlist *sp;
177961f28255Scgd 
178061f28255Scgd 	len = 0;
178161f28255Scgd 	for (sp = str ; sp ; sp = sp->next)
178261f28255Scgd 		len++;
178361f28255Scgd 	return msort(str, len);
178461f28255Scgd }
178561f28255Scgd 
178661f28255Scgd 
178761f28255Scgd STATIC struct strlist *
1788c02b3bbdSchristos msort(struct strlist *list, int len)
178961f28255Scgd {
17904b99c4d7Schristos 	struct strlist *p, *q = NULL;
179161f28255Scgd 	struct strlist **lpp;
179261f28255Scgd 	int half;
179361f28255Scgd 	int n;
179461f28255Scgd 
179561f28255Scgd 	if (len <= 1)
179661f28255Scgd 		return list;
179761f28255Scgd 	half = len >> 1;
179861f28255Scgd 	p = list;
179961f28255Scgd 	for (n = half ; --n >= 0 ; ) {
180061f28255Scgd 		q = p;
180161f28255Scgd 		p = p->next;
180261f28255Scgd 	}
180361f28255Scgd 	q->next = NULL;			/* terminate first half of list */
180461f28255Scgd 	q = msort(list, half);		/* sort first half of list */
180561f28255Scgd 	p = msort(p, len - half);		/* sort second half */
180661f28255Scgd 	lpp = &list;
180761f28255Scgd 	for (;;) {
180861f28255Scgd 		if (strcmp(p->text, q->text) < 0) {
180961f28255Scgd 			*lpp = p;
181061f28255Scgd 			lpp = &p->next;
181161f28255Scgd 			if ((p = *lpp) == NULL) {
181261f28255Scgd 				*lpp = q;
181361f28255Scgd 				break;
181461f28255Scgd 			}
181561f28255Scgd 		} else {
181661f28255Scgd 			*lpp = q;
181761f28255Scgd 			lpp = &q->next;
181861f28255Scgd 			if ((q = *lpp) == NULL) {
181961f28255Scgd 				*lpp = p;
182061f28255Scgd 				break;
182161f28255Scgd 			}
182261f28255Scgd 		}
182361f28255Scgd 	}
182461f28255Scgd 	return list;
182561f28255Scgd }
182661f28255Scgd 
182761f28255Scgd 
18287969ec4dSroy /*
18297969ec4dSroy  * See if a character matches a character class, starting at the first colon
18307969ec4dSroy  * of "[:class:]".
18317969ec4dSroy  * If a valid character class is recognized, a pointer to the next character
18327969ec4dSroy  * after the final closing bracket is stored into *end, otherwise a null
18337969ec4dSroy  * pointer is stored into *end.
18347969ec4dSroy  */
18357969ec4dSroy static int
18368e4a570fSchristos match_charclass(const char *p, wchar_t chr, const char **end)
18377969ec4dSroy {
18387969ec4dSroy 	char name[20];
1839d47295ccSrillig 	const char *nameend;
18407969ec4dSroy 	wctype_t cclass;
18417dca2b7eSkre 	char *q;
18427969ec4dSroy 
18437969ec4dSroy 	*end = NULL;
18447969ec4dSroy 	p++;
18457dca2b7eSkre 	q = &name[0];
18467969ec4dSroy 	nameend = strstr(p, ":]");
1847b81009ceSkre 	if (nameend == NULL || nameend == p)	/* not a valid class */
18487969ec4dSroy 		return 0;
1849b81009ceSkre 
18507dca2b7eSkre 	if (*p == CTLESC) {
18517dca2b7eSkre 		if (*++p == CTLESC)
1852b81009ceSkre 			return 0;
18537dca2b7eSkre 		if (p == nameend)
18547dca2b7eSkre 			return 0;
18557dca2b7eSkre 	}
18567dca2b7eSkre 	if (!is_alpha(*p))
18577dca2b7eSkre 		return 0;
18587dca2b7eSkre 	while (p < nameend) {
18597dca2b7eSkre 		if (*p == CTLESC) {
18607dca2b7eSkre 			p++;
18617dca2b7eSkre 			if (p == nameend)
18627dca2b7eSkre 				return 0;
18637dca2b7eSkre 		}
18647dca2b7eSkre 		if (!is_in_name(*p))	/* '_' is a local extension */
18657dca2b7eSkre 			return 0;
18667dca2b7eSkre 		if (q < &name[sizeof name])
18677dca2b7eSkre 			*q++ = *p++;
18687dca2b7eSkre 		else
18697dca2b7eSkre 			p++;
18707dca2b7eSkre 	}
1871b81009ceSkre 
1872b81009ceSkre 	*end = nameend + 2;		/* committed to it being a char class */
18737dca2b7eSkre 
18747dca2b7eSkre 	if (q < &name[sizeof name])	/* a usable name found */
18757dca2b7eSkre 		*q++ = '\0';
18767dca2b7eSkre 	else				/* too long, valid, but no match */
18777dca2b7eSkre 		return 0;
18787dca2b7eSkre 
18797969ec4dSroy 	cclass = wctype(name);
18807969ec4dSroy 	/* An unknown class matches nothing but is valid nevertheless. */
18817969ec4dSroy 	if (cclass == 0)
18827969ec4dSroy 		return 0;
18837969ec4dSroy 	return iswctype(chr, cclass);
18847969ec4dSroy }
18857969ec4dSroy 
18867969ec4dSroy 
188761f28255Scgd /*
188861f28255Scgd  * Returns true if the pattern matches the string.
188961f28255Scgd  */
189061f28255Scgd 
189161f28255Scgd STATIC int
18928e4a570fSchristos patmatch(const char *pattern, const char *string, int squoted)
189361f28255Scgd {
18948e4a570fSchristos 	const char *p, *q, *end;
18958e4a570fSchristos 	const char *bt_p, *bt_q;
189648250187Stls 	char c;
18978e4a570fSchristos 	wchar_t wc, wc2;
189861f28255Scgd 
1899bcacfd9aSkre 	VTRACE(DBG_MATCH, ("patmatch(P=\"%s\", W=\"%s\"%s): ",
1900bcacfd9aSkre 	    pattern, string, squoted ? ", SQ" : ""));
190161f28255Scgd 	p = pattern;
190261f28255Scgd 	q = string;
19038e4a570fSchristos 	bt_p = NULL;
19048e4a570fSchristos 	bt_q = NULL;
190561f28255Scgd 	for (;;) {
190661f28255Scgd 		switch (c = *p++) {
190761f28255Scgd 		case '\0':
190814482abcSkre 			if (squoted && *q == CTLESC) {
190914482abcSkre 				if (q[1] == '\0')
191014482abcSkre 					q++;
191114482abcSkre 			}
19128e4a570fSchristos 			if (*q != '\0')
19138e4a570fSchristos 				goto backtrack;
1914bcacfd9aSkre 			VTRACE(DBG_MATCH, ("match\n"));
19158e4a570fSchristos 			return 1;
191661f28255Scgd 		case CTLESC:
191728607542Schristos 			if (squoted && *q == CTLESC)
191828607542Schristos 				q++;
191914482abcSkre 			if (*p == '\0' && *q == '\0') {
192014482abcSkre 				VTRACE(DBG_MATCH, ("match-\\\n"));
192114482abcSkre 				return 1;
192214482abcSkre 			}
192314482abcSkre 			if (*q++ != *p++)
192414482abcSkre 				goto backtrack;
192514482abcSkre 			break;
192614482abcSkre 		case '\\':
192714482abcSkre 			if (squoted && *q == CTLESC)
192814482abcSkre 				q++;
192961f28255Scgd 			if (*q++ != *p++)
19308e4a570fSchristos 				goto backtrack;
193161f28255Scgd 			break;
19321fbf0781Smycroft 		case CTLQUOTEMARK:
1933c9f333adSkre 		case CTLQUOTEEND:
1934727a69dcSkre 		case CTLNONL:
19351fbf0781Smycroft 			continue;
193661f28255Scgd 		case '?':
193728607542Schristos 			if (squoted && *q == CTLESC)
193828607542Schristos 				q++;
1939bcacfd9aSkre 			if (*q++ == '\0') {
1940bcacfd9aSkre 				VTRACE(DBG_MATCH, ("?fail\n"));
194161f28255Scgd 				return 0;
1942bcacfd9aSkre 			}
194361f28255Scgd 			break;
194461f28255Scgd 		case '*':
194561f28255Scgd 			c = *p;
19461fbf0781Smycroft 			while (c == CTLQUOTEMARK || c == '*')
19471fbf0781Smycroft 				c = *++p;
1948c9f333adSkre 			if (c != CTLESC && !IS_BORING(c) &&
19491fbf0781Smycroft 			    c != '?' && c != '*' && c != '[') {
195061f28255Scgd 				while (*q != c) {
195128607542Schristos 					if (squoted && *q == CTLESC &&
195228607542Schristos 					    q[1] == c)
195328607542Schristos 						break;
1954bcacfd9aSkre 					if (*q == '\0') {
1955bcacfd9aSkre 						VTRACE(DBG_MATCH, ("*fail\n"));
195661f28255Scgd 						return 0;
1957bcacfd9aSkre 					}
195828607542Schristos 					if (squoted && *q == CTLESC)
195928607542Schristos 						q++;
196061f28255Scgd 					q++;
196161f28255Scgd 				}
196261f28255Scgd 			}
196314482abcSkre 			if (c == CTLESC && p[1] == '\0') {
196414482abcSkre 				VTRACE(DBG_MATCH, ("match+\\\n"));
196514482abcSkre 				return 1;
196614482abcSkre 			}
19678e4a570fSchristos 			/*
19688e4a570fSchristos 			 * First try the shortest match for the '*' that
19698e4a570fSchristos 			 * could work. We can forget any earlier '*' since
19708e4a570fSchristos 			 * there is no way having it match more characters
19718e4a570fSchristos 			 * can help us, given that we are already here.
19728e4a570fSchristos 			 */
19738e4a570fSchristos 			bt_p = p;
19748e4a570fSchristos 			bt_q = q;
19758e4a570fSchristos 			break;
197661f28255Scgd 		case '[': {
19778e4a570fSchristos 			const char *savep, *saveq, *endp;
197861f28255Scgd 			int invert, found;
19798e4a570fSchristos 			unsigned char chr;
198061f28255Scgd 
1981c83568a7Skre 			/*
1982c83568a7Skre 			 * First quick check to see if there is a
1983c83568a7Skre 			 * possible matching ']' - if not, then this
1984c83568a7Skre 			 * is not a char class, and the '[' is just
1985c83568a7Skre 			 * a literal '['.
1986c83568a7Skre 			 *
1987c83568a7Skre 			 * This check will not detect all non classes, but
1988c83568a7Skre 			 * that's OK - It just means that we execute the
1989c83568a7Skre 			 * harder code sometimes when it it cannot succeed.
1990c83568a7Skre 			 */
199161f28255Scgd 			endp = p;
1992c83568a7Skre 			if (*endp == '!' || *endp == '^')
199361f28255Scgd 				endp++;
199461f28255Scgd 			for (;;) {
1995c9f333adSkre 				while (IS_BORING(*endp))
19961fbf0781Smycroft 					endp++;
199761f28255Scgd 				if (*endp == '\0')
199861f28255Scgd 					goto dft;	/* no matching ] */
1999c9f333adSkre 				if (*endp++ == ']')
200061f28255Scgd 					break;
200161f28255Scgd 			}
2002c83568a7Skre 			/* end shortcut */
2003c83568a7Skre 
20048e4a570fSchristos 			savep = p, saveq = q;
20058e4a570fSchristos 			invert = 0;
20068e4a570fSchristos 			if (*p == '!' || *p == '^') {
200761f28255Scgd 				invert++;
200861f28255Scgd 				p++;
200961f28255Scgd 			}
201061f28255Scgd 			found = 0;
2011bcacfd9aSkre 			if (*q == '\0') {
2012bcacfd9aSkre 				VTRACE(DBG_MATCH, ("[]fail\n"));
2013d22a5147Smycroft 				return 0;
2014bcacfd9aSkre 			}
201514482abcSkre 			if (squoted && *q == CTLESC)
201614482abcSkre 				q++;
20178e4a570fSchristos 			chr = (unsigned char)*q++;
201861f28255Scgd 			c = *p++;
201961f28255Scgd 			do {
2020c9f333adSkre 				if (IS_BORING(c))
20211fbf0781Smycroft 					continue;
20228e4a570fSchristos 				if (c == '\0') {
20238e4a570fSchristos 					p = savep, q = saveq;
20248e4a570fSchristos 					c = '[';
20258e4a570fSchristos 					goto dft;
20268e4a570fSchristos 				}
20277969ec4dSroy 				if (c == '[' && *p == ':') {
20287969ec4dSroy 					found |= match_charclass(p, chr, &end);
2029829cc62aSkre 					if (end != NULL) {
20307969ec4dSroy 						p = end;
2031829cc62aSkre 						continue;
2032829cc62aSkre 					}
20337969ec4dSroy 				}
2034c83568a7Skre 				if (c == CTLESC || c == '\\')
203561f28255Scgd 					c = *p++;
20368e4a570fSchristos 				wc = (unsigned char)c;
203761f28255Scgd 				if (*p == '-' && p[1] != ']') {
203861f28255Scgd 					p++;
2039c83568a7Skre 					if (*p == CTLESC || *p == '\\')
204061f28255Scgd 						p++;
20418e4a570fSchristos 					wc2 = (unsigned char)*p++;
20428e4a570fSchristos 					if (   collate_range_cmp(chr, wc) >= 0
20438e4a570fSchristos 					    && collate_range_cmp(chr, wc2) <= 0
20448e4a570fSchristos 					   )
204561f28255Scgd 						found = 1;
204661f28255Scgd 				} else {
20478e4a570fSchristos 					if (chr == wc)
204861f28255Scgd 						found = 1;
204961f28255Scgd 				}
205061f28255Scgd 			} while ((c = *p++) != ']');
205161f28255Scgd 			if (found == invert)
20528e4a570fSchristos 				goto backtrack;
205361f28255Scgd 			break;
205461f28255Scgd 		}
205561f28255Scgd   dft:		default:
205628607542Schristos 			if (squoted && *q == CTLESC)
205728607542Schristos 				q++;
20588e4a570fSchristos 			if (*q++ == c)
20598e4a570fSchristos 				break;
20608e4a570fSchristos   backtrack:
20618e4a570fSchristos 			/*
20628e4a570fSchristos 			 * If we have a mismatch (other than hitting the end
20638e4a570fSchristos 			 * of the string), go back to the last '*' seen and
20648e4a570fSchristos 			 * have it match one additional character.
20658e4a570fSchristos 			 */
2066bcacfd9aSkre 			if (bt_p == NULL) {
2067bcacfd9aSkre 				VTRACE(DBG_MATCH, ("BTP fail\n"));
206861f28255Scgd 				return 0;
2069bcacfd9aSkre 			}
2070bcacfd9aSkre 			if (*bt_q == '\0') {
2071bcacfd9aSkre 				VTRACE(DBG_MATCH, ("BTQ fail\n"));
20728e4a570fSchristos 				return 0;
2073bcacfd9aSkre 			}
20748e4a570fSchristos 			bt_q++;
20758e4a570fSchristos 			p = bt_p;
20768e4a570fSchristos 			q = bt_q;
207761f28255Scgd 			break;
207861f28255Scgd 		}
207961f28255Scgd 	}
208061f28255Scgd }
208161f28255Scgd 
208261f28255Scgd 
208361f28255Scgd 
208461f28255Scgd /*
2085727a69dcSkre  * Remove any CTLESC or CTLNONL characters from a string.
2086c69ada4cSkre  *
2087c69ada4cSkre  * String is modified in place, and we return the length of the result
208861f28255Scgd  */
208961f28255Scgd 
2090c69ada4cSkre int
2091c02b3bbdSchristos rmescapes(char *str)
209261f28255Scgd {
209348250187Stls 	char *p, *q;
209461f28255Scgd 
209561f28255Scgd 	p = str;
2096256d645dSkre 	while (!ISCTL(*p)) {
209761f28255Scgd 		if (*p++ == '\0')
2098c69ada4cSkre 			return ((int)(p - str) - 1);
209961f28255Scgd 	}
210061f28255Scgd 	q = p;
210161f28255Scgd 	while (*p) {
2102c9f333adSkre 		if (IS_BORING(*p)) {
21031fbf0781Smycroft 			p++;
21041fbf0781Smycroft 			continue;
21051fbf0781Smycroft 		}
21065f92382cSkre 		if (*p == CTLCNL) {
21075f92382cSkre 			p++;
21085f92382cSkre 			*q++ = '\n';
21095f92382cSkre 			continue;
21105f92382cSkre 		}
211161f28255Scgd 		if (*p == CTLESC)
211261f28255Scgd 			p++;
211358e34de6Skre #ifdef DEBUG
2114256d645dSkre 		else if (ISCTL(*p))
211558e34de6Skre 			abort();
211658e34de6Skre #endif
211761f28255Scgd 		*q++ = *p++;
211861f28255Scgd 	}
211961f28255Scgd 	*q = '\0';
2120c69ada4cSkre 
2121c69ada4cSkre 	return ((int)(q - str));
212261f28255Scgd }
212361f28255Scgd 
2124727a69dcSkre /*
2125727a69dcSkre  * and a special version for dealing with expressions to be parsed
2126727a69dcSkre  * by the arithmetic evaluator.   That needs to be able to count \n's
2127727a69dcSkre  * even ones that were \newline elided \n's, so we have to put the
2128727a69dcSkre  * latter back into the string - just being careful to put them only
2129727a69dcSkre  * at a place where white space can reasonably occur in the string
2130727a69dcSkre  * -- then the \n we insert will just be white space, and ignored
2131727a69dcSkre  * for all purposes except line counting.
2132727a69dcSkre  */
2133727a69dcSkre 
2134727a69dcSkre void
2135727a69dcSkre rmescapes_nl(char *str)
2136727a69dcSkre {
2137727a69dcSkre 	char *p, *q;
2138727a69dcSkre 	int nls = 0, holdnl = 0, holdlast;
2139727a69dcSkre 
2140727a69dcSkre 	p = str;
2141256d645dSkre 	while (!ISCTL(*p)) {
2142727a69dcSkre 		if (*p++ == '\0')
2143727a69dcSkre 			return;
2144727a69dcSkre 	}
2145727a69dcSkre 	if (p > str)	/* must reprocess char before stopper (if any) */
2146727a69dcSkre 		--p;	/* so we do not place a \n badly */
2147727a69dcSkre 	q = p;
2148727a69dcSkre 	while (*p) {
2149c9f333adSkre 		if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) {
2150727a69dcSkre 			p++;
2151727a69dcSkre 			continue;
2152727a69dcSkre 		}
2153727a69dcSkre 		if (*p == CTLNONL) {
2154727a69dcSkre 			p++;
2155727a69dcSkre 			nls++;
2156727a69dcSkre 			continue;
2157727a69dcSkre 		}
21585f92382cSkre 		if (*p == CTLCNL) {
21595f92382cSkre 			p++;
21605f92382cSkre 			*q++ = '\n';
21615f92382cSkre 			continue;
21625f92382cSkre 		}
2163727a69dcSkre 		if (*p == CTLESC)
2164727a69dcSkre 			p++;
216558e34de6Skre #ifdef DEBUG
2166256d645dSkre 		else if (ISCTL(*p))
216758e34de6Skre 			abort();
216858e34de6Skre #endif
2169727a69dcSkre 
2170727a69dcSkre 		holdlast = holdnl;
2171727a69dcSkre 		holdnl = is_in_name(*p);	/* letters, digits, _ */
2172727a69dcSkre 		if (q == str || is_space(q[-1]) || (*p != '=' && q[-1] != *p)) {
2173727a69dcSkre 			if (nls > 0 && holdnl != holdlast) {
2174727a69dcSkre 				while (nls > 0)
2175727a69dcSkre 					*q++ = '\n', nls--;
2176727a69dcSkre 			}
2177727a69dcSkre 		}
2178727a69dcSkre 		*q++ = *p++;
2179727a69dcSkre 	}
2180727a69dcSkre 	while (--nls >= 0)
2181727a69dcSkre 		*q++ = '\n';
2182727a69dcSkre 	*q = '\0';
2183727a69dcSkre }
2184727a69dcSkre 
218561f28255Scgd 
218661f28255Scgd 
218761f28255Scgd /*
218861f28255Scgd  * See if a pattern matches in a case statement.
218961f28255Scgd  */
219061f28255Scgd 
219161f28255Scgd int
2192c02b3bbdSchristos casematch(union node *pattern, char *val)
219361f28255Scgd {
219461f28255Scgd 	struct stackmark smark;
219561f28255Scgd 	int result;
219661f28255Scgd 	char *p;
219761f28255Scgd 
2198bcacfd9aSkre 	CTRACE(DBG_MATCH, ("casematch(P=\"%s\", W=\"%s\")\n",
2199bcacfd9aSkre 	    pattern->narg.text, val));
220061f28255Scgd 	setstackmark(&smark);
220161f28255Scgd 	argbackq = pattern->narg.backquote;
220261f28255Scgd 	STARTSTACKSTR(expdest);
220361f28255Scgd 	ifslastp = NULL;
22041fbf0781Smycroft 	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
220561f28255Scgd 	STPUTC('\0', expdest);
220661f28255Scgd 	p = grabstackstr(expdest);
220728607542Schristos 	result = patmatch(p, val, 0);
220861f28255Scgd 	popstackmark(&smark);
220961f28255Scgd 	return result;
221061f28255Scgd }
2211ad8d5369Schristos 
2212ad8d5369Schristos /*
2213dd6b6414Skre  * Our own itoa().   Assumes result buffer is on the stack
2214ad8d5369Schristos  */
2215ad8d5369Schristos 
2216ad8d5369Schristos STATIC char *
2217c02b3bbdSchristos cvtnum(int num, char *buf)
2218ad8d5369Schristos {
2219ad8d5369Schristos 	char temp[32];
2220ad8d5369Schristos 	int neg = num < 0;
2221dd6b6414Skre 	char *p = temp + sizeof temp - 1;
2222ad8d5369Schristos 
2223dd6b6414Skre 	if (neg)
2224dd6b6414Skre 		num = -num;
2225ad8d5369Schristos 
2226dd6b6414Skre 	*p = '\0';
2227ad8d5369Schristos 	do {
2228ad8d5369Schristos 		*--p = num % 10 + '0';
2229dd6b6414Skre 	} while ((num /= 10) != 0 && p > temp + 1);
2230ad8d5369Schristos 
2231ad8d5369Schristos 	if (neg)
2232ad8d5369Schristos 		*--p = '-';
2233ad8d5369Schristos 
2234ad8d5369Schristos 	while (*p)
2235ad8d5369Schristos 		STPUTC(*p++, buf);
2236ad8d5369Schristos 	return buf;
2237ad8d5369Schristos }
2238cf788c31Sseb 
2239cf788c31Sseb /*
2240cf788c31Sseb  * Do most of the work for wordexp(3).
2241cf788c31Sseb  */
2242cf788c31Sseb 
2243cf788c31Sseb int
2244cf788c31Sseb wordexpcmd(int argc, char **argv)
2245cf788c31Sseb {
2246cf788c31Sseb 	size_t len;
2247cf788c31Sseb 	int i;
2248cf788c31Sseb 
2249cf788c31Sseb 	out1fmt("%d", argc - 1);
2250cf788c31Sseb 	out1c('\0');
2251cf788c31Sseb 	for (i = 1, len = 0; i < argc; i++)
2252cf788c31Sseb 		len += strlen(argv[i]);
225349ee47d0Stsutsui 	out1fmt("%zu", len);
2254cf788c31Sseb 	out1c('\0');
2255cf788c31Sseb 	for (i = 1; i < argc; i++) {
2256cf788c31Sseb 		out1str(argv[i]);
2257cf788c31Sseb 		out1c('\0');
2258cf788c31Sseb 	}
2259cf788c31Sseb 	return (0);
2260cf788c31Sseb }
2261