expand.c revision 223010
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
4207944Sjilles * Copyright (c) 1997-2005
5207944Sjilles *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
61556Srgrimes *
71556Srgrimes * This code is derived from software contributed to Berkeley by
81556Srgrimes * Kenneth Almquist.
91556Srgrimes *
101556Srgrimes * Redistribution and use in source and binary forms, with or without
111556Srgrimes * modification, are permitted provided that the following conditions
121556Srgrimes * are met:
131556Srgrimes * 1. Redistributions of source code must retain the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer.
151556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
161556Srgrimes *    notice, this list of conditions and the following disclaimer in the
171556Srgrimes *    documentation and/or other materials provided with the distribution.
181556Srgrimes * 4. Neither the name of the University nor the names of its contributors
191556Srgrimes *    may be used to endorse or promote products derived from this software
201556Srgrimes *    without specific prior written permission.
211556Srgrimes *
221556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
231556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
241556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
251556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
261556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
271556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
281556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
291556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
301556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
311556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
321556Srgrimes * SUCH DAMAGE.
331556Srgrimes */
341556Srgrimes
351556Srgrimes#ifndef lint
3636150Scharnier#if 0
3736150Scharnierstatic char sccsid[] = "@(#)expand.c	8.5 (Berkeley) 5/15/95";
3836150Scharnier#endif
391556Srgrimes#endif /* not lint */
4099110Sobrien#include <sys/cdefs.h>
4199110Sobrien__FBSDID("$FreeBSD: head/bin/sh/expand.c 223010 2011-06-12 12:54:52Z jilles $");
421556Srgrimes
4317987Speter#include <sys/types.h>
4417987Speter#include <sys/time.h>
4517987Speter#include <sys/stat.h>
46213775Sjhb#include <dirent.h>
4717987Speter#include <errno.h>
48213775Sjhb#include <inttypes.h>
49213775Sjhb#include <limits.h>
5017987Speter#include <pwd.h>
51213775Sjhb#include <stdio.h>
5217987Speter#include <stdlib.h>
53108286Stjr#include <string.h>
54213775Sjhb#include <unistd.h>
55221646Sjilles#include <wchar.h>
5617987Speter
571556Srgrimes/*
581556Srgrimes * Routines to expand arguments to commands.  We have to deal with
591556Srgrimes * backquotes, shell variables, and file metacharacters.
601556Srgrimes */
611556Srgrimes
621556Srgrimes#include "shell.h"
631556Srgrimes#include "main.h"
641556Srgrimes#include "nodes.h"
651556Srgrimes#include "eval.h"
661556Srgrimes#include "expand.h"
671556Srgrimes#include "syntax.h"
681556Srgrimes#include "parser.h"
691556Srgrimes#include "jobs.h"
701556Srgrimes#include "options.h"
711556Srgrimes#include "var.h"
721556Srgrimes#include "input.h"
731556Srgrimes#include "output.h"
741556Srgrimes#include "memalloc.h"
751556Srgrimes#include "error.h"
761556Srgrimes#include "mystring.h"
7717987Speter#include "arith.h"
7817987Speter#include "show.h"
791556Srgrimes
801556Srgrimes/*
811556Srgrimes * Structure specifying which parts of the string should be searched
821556Srgrimes * for IFS characters.
831556Srgrimes */
841556Srgrimes
851556Srgrimesstruct ifsregion {
861556Srgrimes	struct ifsregion *next;	/* next region in list */
871556Srgrimes	int begoff;		/* offset of start of region */
881556Srgrimes	int endoff;		/* offset of end of region */
89194975Sjilles	int inquotes;		/* search for nul bytes only */
901556Srgrimes};
911556Srgrimes
921556Srgrimes
93213760Sobrienstatic char *expdest;			/* output of current string */
94213760Sobrienstatic struct nodelist *argbackq;	/* list of back quote expressions */
95213760Sobrienstatic struct ifsregion ifsfirst;	/* first struct in list of ifs regions */
96213760Sobrienstatic struct ifsregion *ifslastp;	/* last struct in list */
97213760Sobrienstatic struct arglist exparg;		/* holds expanded arg list */
981556Srgrimes
99213811Sobrienstatic void argstr(char *, int);
100213811Sobrienstatic char *exptilde(char *, int);
101213811Sobrienstatic void expbackq(union node *, int, int);
102214524Sjillesstatic int subevalvar(char *, char *, int, int, int, int, int);
103213811Sobrienstatic char *evalvar(char *, int);
104213811Sobrienstatic int varisset(char *, int);
105213811Sobrienstatic void varvalue(char *, int, int, int);
106213811Sobrienstatic void recordregion(int, int, int);
107213811Sobrienstatic void removerecordregions(int);
108213811Sobrienstatic void ifsbreakup(char *, struct arglist *);
109213811Sobrienstatic void expandmeta(struct strlist *, int);
110213811Sobrienstatic void expmeta(char *, char *);
111213811Sobrienstatic void addfname(char *);
112213811Sobrienstatic struct strlist *expsort(struct strlist *);
113213811Sobrienstatic struct strlist *msort(struct strlist *, int);
114213811Sobrienstatic char *cvtnum(int, char *);
115221646Sjillesstatic int collate_range_cmp(wchar_t, wchar_t);
1161556Srgrimes
117213811Sobrienstatic int
118221646Sjillescollate_range_cmp(wchar_t c1, wchar_t c2)
11919281Sache{
120221646Sjilles	static wchar_t s1[2], s2[2];
12119281Sache
12219281Sache	s1[0] = c1;
12319281Sache	s2[0] = c2;
124221646Sjilles	return (wcscoll(s1, s2));
12519281Sache}
12619281Sache
1271556Srgrimes/*
1281556Srgrimes * Expand shell variables and backquotes inside a here document.
12990111Simp *	union node *arg		the document
13090111Simp *	int fd;			where to write the expanded version
1311556Srgrimes */
1321556Srgrimes
1331556Srgrimesvoid
13490111Simpexpandhere(union node *arg, int fd)
13590111Simp{
1361556Srgrimes	expandarg(arg, (struct arglist *)NULL, 0);
13739137Stegge	xwrite(fd, stackblock(), expdest - stackblock());
1381556Srgrimes}
1391556Srgrimes
140216384Sjillesstatic char *
141216384Sjillesstputs_quotes(const char *data, const char *syntax, char *p)
142216384Sjilles{
143216384Sjilles	while (*data) {
144216384Sjilles		CHECKSTRSPACE(2, p);
145216384Sjilles		if (syntax[(int)*data] == CCTL)
146216384Sjilles			USTPUTC(CTLESC, p);
147216384Sjilles		USTPUTC(*data++, p);
148216384Sjilles	}
149216384Sjilles	return (p);
150216384Sjilles}
151216384Sjilles#define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p)
1521556Srgrimes
1531556Srgrimes/*
154212243Sjilles * Perform expansions on an argument, placing the resulting list of arguments
155212243Sjilles * in arglist.  Parameter expansion, command substitution and arithmetic
156212243Sjilles * expansion are always performed; additional expansions can be requested
157212243Sjilles * via flag (EXP_*).
158212243Sjilles * The result is left in the stack string.
159218203Sjilles * When arglist is NULL, perform here document expansion.
160212243Sjilles *
161212243Sjilles * Caution: this function uses global state and is not reentrant.
162212243Sjilles * However, a new invocation after an interrupted invocation is safe
163212243Sjilles * and will reset the global state for the new call.
1641556Srgrimes */
1651556Srgrimesvoid
16690111Simpexpandarg(union node *arg, struct arglist *arglist, int flag)
16717987Speter{
1681556Srgrimes	struct strlist *sp;
1691556Srgrimes	char *p;
1701556Srgrimes
1711556Srgrimes	argbackq = arg->narg.backquote;
1721556Srgrimes	STARTSTACKSTR(expdest);
1731556Srgrimes	ifsfirst.next = NULL;
1741556Srgrimes	ifslastp = NULL;
1751556Srgrimes	argstr(arg->narg.text, flag);
1761556Srgrimes	if (arglist == NULL) {
177222907Sjilles		STACKSTRNUL(expdest);
1781556Srgrimes		return;			/* here document expanded */
1791556Srgrimes	}
1801556Srgrimes	STPUTC('\0', expdest);
1811556Srgrimes	p = grabstackstr(expdest);
1821556Srgrimes	exparg.lastp = &exparg.list;
1831556Srgrimes	/*
1841556Srgrimes	 * TODO - EXP_REDIR
1851556Srgrimes	 */
1861556Srgrimes	if (flag & EXP_FULL) {
1871556Srgrimes		ifsbreakup(p, &exparg);
1881556Srgrimes		*exparg.lastp = NULL;
1891556Srgrimes		exparg.lastp = &exparg.list;
1901556Srgrimes		expandmeta(exparg.list, flag);
1911556Srgrimes	} else {
1921556Srgrimes		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
1931556Srgrimes			rmescapes(p);
1941556Srgrimes		sp = (struct strlist *)stalloc(sizeof (struct strlist));
1951556Srgrimes		sp->text = p;
1961556Srgrimes		*exparg.lastp = sp;
1971556Srgrimes		exparg.lastp = &sp->next;
1981556Srgrimes	}
1991556Srgrimes	while (ifsfirst.next != NULL) {
2001556Srgrimes		struct ifsregion *ifsp;
2011556Srgrimes		INTOFF;
2021556Srgrimes		ifsp = ifsfirst.next->next;
2031556Srgrimes		ckfree(ifsfirst.next);
2041556Srgrimes		ifsfirst.next = ifsp;
2051556Srgrimes		INTON;
2061556Srgrimes	}
2071556Srgrimes	*exparg.lastp = NULL;
2081556Srgrimes	if (exparg.list) {
2091556Srgrimes		*arglist->lastp = exparg.list;
2101556Srgrimes		arglist->lastp = exparg.lastp;
2111556Srgrimes	}
2121556Srgrimes}
2131556Srgrimes
2141556Srgrimes
2151556Srgrimes
2161556Srgrimes/*
217212243Sjilles * Perform parameter expansion, command substitution and arithmetic
218212243Sjilles * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE.
219212243Sjilles * Processing ends at a CTLENDVAR character as well as '\0'.
220212243Sjilles * This is used to expand word in ${var+word} etc.
221212243Sjilles * If EXP_FULL, EXP_CASE or EXP_REDIR are set, keep and/or generate CTLESC
222212243Sjilles * characters to allow for further processing.
223212243Sjilles * If EXP_FULL is set, also preserve CTLQUOTEMARK characters.
2241556Srgrimes */
225213811Sobrienstatic void
22690111Simpargstr(char *p, int flag)
22717987Speter{
22825233Ssteve	char c;
229104672Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);	/* do CTLESC */
2301556Srgrimes	int firsteq = 1;
231214512Sjilles	int split_lit;
232214512Sjilles	int lit_quoted;
2331556Srgrimes
234214512Sjilles	split_lit = flag & EXP_SPLIT_LIT;
235214512Sjilles	lit_quoted = flag & EXP_LIT_QUOTED;
236214512Sjilles	flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED);
2371556Srgrimes	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
2381556Srgrimes		p = exptilde(p, flag);
2391556Srgrimes	for (;;) {
240215783Sjilles		CHECKSTRSPACE(2, expdest);
2411556Srgrimes		switch (c = *p++) {
2421556Srgrimes		case '\0':
243212243Sjilles		case CTLENDVAR:
2441556Srgrimes			goto breakloop;
24538887Stegge		case CTLQUOTEMARK:
246214512Sjilles			lit_quoted = 1;
24738887Stegge			/* "$@" syntax adherence hack */
24838887Stegge			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
24938887Stegge				break;
25039137Stegge			if ((flag & EXP_FULL) != 0)
251215783Sjilles				USTPUTC(c, expdest);
25238887Stegge			break;
253214512Sjilles		case CTLQUOTEEND:
254214512Sjilles			lit_quoted = 0;
255214512Sjilles			break;
2561556Srgrimes		case CTLESC:
2571556Srgrimes			if (quotes)
258215783Sjilles				USTPUTC(c, expdest);
2591556Srgrimes			c = *p++;
260215783Sjilles			USTPUTC(c, expdest);
261214512Sjilles			if (split_lit && !lit_quoted)
262214512Sjilles				recordregion(expdest - stackblock() -
263214512Sjilles				    (quotes ? 2 : 1),
264214512Sjilles				    expdest - stackblock(), 0);
2651556Srgrimes			break;
2661556Srgrimes		case CTLVAR:
2671556Srgrimes			p = evalvar(p, flag);
2681556Srgrimes			break;
2691556Srgrimes		case CTLBACKQ:
2701556Srgrimes		case CTLBACKQ|CTLQUOTE:
2711556Srgrimes			expbackq(argbackq->n, c & CTLQUOTE, flag);
2721556Srgrimes			argbackq = argbackq->next;
2731556Srgrimes			break;
2741556Srgrimes		case CTLENDARI:
2751556Srgrimes			expari(flag);
2761556Srgrimes			break;
2771556Srgrimes		case ':':
2781556Srgrimes		case '=':
2791556Srgrimes			/*
2801556Srgrimes			 * sort of a hack - expand tildes in variable
2811556Srgrimes			 * assignments (after the first '=' and after ':'s).
2821556Srgrimes			 */
283215783Sjilles			USTPUTC(c, expdest);
284214512Sjilles			if (split_lit && !lit_quoted)
285214512Sjilles				recordregion(expdest - stackblock() - 1,
286214512Sjilles				    expdest - stackblock(), 0);
287214512Sjilles			if (flag & EXP_VARTILDE && *p == '~' &&
288214512Sjilles			    (c != '=' || firsteq)) {
289214512Sjilles				if (c == '=')
290214512Sjilles					firsteq = 0;
2911556Srgrimes				p = exptilde(p, flag);
2921556Srgrimes			}
2931556Srgrimes			break;
2941556Srgrimes		default:
295215783Sjilles			USTPUTC(c, expdest);
296214512Sjilles			if (split_lit && !lit_quoted)
297214512Sjilles				recordregion(expdest - stackblock() - 1,
298214512Sjilles				    expdest - stackblock(), 0);
2991556Srgrimes		}
3001556Srgrimes	}
3011556Srgrimesbreakloop:;
3021556Srgrimes}
3031556Srgrimes
304212243Sjilles/*
305212243Sjilles * Perform tilde expansion, placing the result in the stack string and
306212243Sjilles * returning the next position in the input string to process.
307212243Sjilles */
308213811Sobrienstatic char *
30990111Simpexptilde(char *p, int flag)
31017987Speter{
3111556Srgrimes	char c, *startp = p;
3121556Srgrimes	struct passwd *pw;
3131556Srgrimes	char *home;
314108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
3151556Srgrimes
31617987Speter	while ((c = *p) != '\0') {
3171556Srgrimes		switch(c) {
318200988Sjilles		case CTLESC: /* This means CTL* are always considered quoted. */
319200988Sjilles		case CTLVAR:
320200988Sjilles		case CTLBACKQ:
321200988Sjilles		case CTLBACKQ | CTLQUOTE:
322200988Sjilles		case CTLARI:
323200988Sjilles		case CTLENDARI:
32439137Stegge		case CTLQUOTEMARK:
32539137Stegge			return (startp);
3261556Srgrimes		case ':':
3271556Srgrimes			if (flag & EXP_VARTILDE)
3281556Srgrimes				goto done;
3291556Srgrimes			break;
3301556Srgrimes		case '/':
331206150Sjilles		case CTLENDVAR:
3321556Srgrimes			goto done;
3331556Srgrimes		}
3341556Srgrimes		p++;
3351556Srgrimes	}
3361556Srgrimesdone:
3371556Srgrimes	*p = '\0';
3381556Srgrimes	if (*(startp+1) == '\0') {
3391556Srgrimes		if ((home = lookupvar("HOME")) == NULL)
3401556Srgrimes			goto lose;
3411556Srgrimes	} else {
3421556Srgrimes		if ((pw = getpwnam(startp+1)) == NULL)
3431556Srgrimes			goto lose;
3441556Srgrimes		home = pw->pw_dir;
3451556Srgrimes	}
3461556Srgrimes	if (*home == '\0')
3471556Srgrimes		goto lose;
3481556Srgrimes	*p = c;
349216384Sjilles	if (quotes)
350216384Sjilles		STPUTS_QUOTES(home, SQSYNTAX, expdest);
351216384Sjilles	else
352216384Sjilles		STPUTS(home, expdest);
3531556Srgrimes	return (p);
3541556Srgrimeslose:
3551556Srgrimes	*p = c;
3561556Srgrimes	return (startp);
3571556Srgrimes}
3581556Srgrimes
3591556Srgrimes
360213811Sobrienstatic void
36190111Simpremoverecordregions(int endoff)
36238887Stegge{
36338887Stegge	if (ifslastp == NULL)
36438887Stegge		return;
36538887Stegge
36638887Stegge	if (ifsfirst.endoff > endoff) {
36738887Stegge		while (ifsfirst.next != NULL) {
36838887Stegge			struct ifsregion *ifsp;
36938887Stegge			INTOFF;
37038887Stegge			ifsp = ifsfirst.next->next;
37138887Stegge			ckfree(ifsfirst.next);
37238887Stegge			ifsfirst.next = ifsp;
37338887Stegge			INTON;
37438887Stegge		}
37538887Stegge		if (ifsfirst.begoff > endoff)
37638887Stegge			ifslastp = NULL;
37738887Stegge		else {
37838887Stegge			ifslastp = &ifsfirst;
37938887Stegge			ifsfirst.endoff = endoff;
38038887Stegge		}
38138887Stegge		return;
38238887Stegge	}
383155301Sschweikh
38438887Stegge	ifslastp = &ifsfirst;
38538887Stegge	while (ifslastp->next && ifslastp->next->begoff < endoff)
38638887Stegge		ifslastp=ifslastp->next;
38738887Stegge	while (ifslastp->next != NULL) {
38838887Stegge		struct ifsregion *ifsp;
38938887Stegge		INTOFF;
39038887Stegge		ifsp = ifslastp->next->next;
39138887Stegge		ckfree(ifslastp->next);
39238887Stegge		ifslastp->next = ifsp;
39338887Stegge		INTON;
39438887Stegge	}
39538887Stegge	if (ifslastp->endoff > endoff)
39638887Stegge		ifslastp->endoff = endoff;
39738887Stegge}
39838887Stegge
3991556Srgrimes/*
4001556Srgrimes * Expand arithmetic expression.  Backup to start of expression,
4011556Srgrimes * evaluate, place result in (backed up) result, adjust string position.
4021556Srgrimes */
4031556Srgrimesvoid
40490111Simpexpari(int flag)
40517987Speter{
406207206Sjilles	char *p, *q, *start;
407178631Sstefanf	arith_t result;
40838887Stegge	int begoff;
409108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
41038887Stegge	int quoted;
4111556Srgrimes
4121556Srgrimes	/*
41346684Skris	 * This routine is slightly over-complicated for
4141556Srgrimes	 * efficiency.  First we make sure there is
4151556Srgrimes	 * enough space for the result, which may be bigger
416212243Sjilles	 * than the expression.  Next we
4171556Srgrimes	 * scan backwards looking for the start of arithmetic.  If the
4181556Srgrimes	 * next previous character is a CTLESC character, then we
4191556Srgrimes	 * have to rescan starting from the beginning since CTLESC
4208855Srgrimes	 * characters have to be processed left to right.
4211556Srgrimes	 */
422178631Sstefanf	CHECKSTRSPACE(DIGITS(result) - 2, expdest);
4238855Srgrimes	USTPUTC('\0', expdest);
4241556Srgrimes	start = stackblock();
42583676Stegge	p = expdest - 2;
42683676Stegge	while (p >= start && *p != CTLARI)
4271556Srgrimes		--p;
42883676Stegge	if (p < start || *p != CTLARI)
4291556Srgrimes		error("missing CTLARI (shouldn't happen)");
43083676Stegge	if (p > start && *(p - 1) == CTLESC)
4311556Srgrimes		for (p = start; *p != CTLARI; p++)
4321556Srgrimes			if (*p == CTLESC)
4331556Srgrimes				p++;
43438887Stegge
43538887Stegge	if (p[1] == '"')
43638887Stegge		quoted=1;
43738887Stegge	else
43838887Stegge		quoted=0;
43938887Stegge	begoff = p - start;
44038887Stegge	removerecordregions(begoff);
4411556Srgrimes	if (quotes)
44238887Stegge		rmescapes(p+2);
443207206Sjilles	q = grabstackstr(expdest);
44438887Stegge	result = arith(p+2);
445207206Sjilles	ungrabstackstr(q, expdest);
446178631Sstefanf	fmtstr(p, DIGITS(result), ARITH_FORMAT_STR, result);
4471556Srgrimes	while (*p++)
4481556Srgrimes		;
44938887Stegge	if (quoted == 0)
45038887Stegge		recordregion(begoff, p - 1 - start, 0);
4511556Srgrimes	result = expdest - p + 1;
4521556Srgrimes	STADJUST(-result, expdest);
4531556Srgrimes}
4541556Srgrimes
4551556Srgrimes
4561556Srgrimes/*
457212243Sjilles * Perform command substitution.
4581556Srgrimes */
459213811Sobrienstatic void
46090111Simpexpbackq(union node *cmd, int quoted, int flag)
46117987Speter{
4621556Srgrimes	struct backcmd in;
4631556Srgrimes	int i;
4641556Srgrimes	char buf[128];
4651556Srgrimes	char *p;
4661556Srgrimes	char *dest = expdest;
4671556Srgrimes	struct ifsregion saveifs, *savelastp;
4681556Srgrimes	struct nodelist *saveargbackq;
4691556Srgrimes	char lastc;
4701556Srgrimes	int startloc = dest - stackblock();
4711556Srgrimes	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
472108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
473115424Sfenner	int nnl;
4741556Srgrimes
4751556Srgrimes	INTOFF;
4761556Srgrimes	saveifs = ifsfirst;
4771556Srgrimes	savelastp = ifslastp;
4781556Srgrimes	saveargbackq = argbackq;
4791556Srgrimes	p = grabstackstr(dest);
4801556Srgrimes	evalbackcmd(cmd, &in);
4811556Srgrimes	ungrabstackstr(p, dest);
4821556Srgrimes	ifsfirst = saveifs;
4831556Srgrimes	ifslastp = savelastp;
4841556Srgrimes	argbackq = saveargbackq;
4851556Srgrimes
4861556Srgrimes	p = in.buf;
4871556Srgrimes	lastc = '\0';
488115424Sfenner	nnl = 0;
489115424Sfenner	/* Don't copy trailing newlines */
4901556Srgrimes	for (;;) {
4911556Srgrimes		if (--in.nleft < 0) {
4921556Srgrimes			if (in.fd < 0)
4931556Srgrimes				break;
4941556Srgrimes			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
4951556Srgrimes			TRACE(("expbackq: read returns %d\n", i));
4961556Srgrimes			if (i <= 0)
4971556Srgrimes				break;
4981556Srgrimes			p = buf;
4991556Srgrimes			in.nleft = i - 1;
5001556Srgrimes		}
5011556Srgrimes		lastc = *p++;
5021556Srgrimes		if (lastc != '\0') {
503115424Sfenner			if (lastc == '\n') {
504115424Sfenner				nnl++;
505115424Sfenner			} else {
506216706Sjilles				CHECKSTRSPACE(nnl + 2, dest);
507115424Sfenner				while (nnl > 0) {
508115424Sfenner					nnl--;
509216706Sjilles					USTPUTC('\n', dest);
510115424Sfenner				}
511216496Sjilles				if (quotes && syntax[(int)lastc] == CCTL)
512216706Sjilles					USTPUTC(CTLESC, dest);
513216706Sjilles				USTPUTC(lastc, dest);
514115424Sfenner			}
5151556Srgrimes		}
5161556Srgrimes	}
51717987Speter
5181556Srgrimes	if (in.fd >= 0)
5191556Srgrimes		close(in.fd);
5201556Srgrimes	if (in.buf)
5211556Srgrimes		ckfree(in.buf);
5221556Srgrimes	if (in.jp)
52345916Scracauer		exitstatus = waitforjob(in.jp, (int *)NULL);
5241556Srgrimes	if (quoted == 0)
5251556Srgrimes		recordregion(startloc, dest - stackblock(), 0);
526213775Sjhb	TRACE(("expbackq: size=%td: \"%.*s\"\n",
527213775Sjhb		((dest - stackblock()) - startloc),
528213775Sjhb		(int)((dest - stackblock()) - startloc),
5291556Srgrimes		stackblock() + startloc));
5301556Srgrimes	expdest = dest;
5311556Srgrimes	INTON;
5321556Srgrimes}
5331556Srgrimes
5341556Srgrimes
5351556Srgrimes
536213811Sobrienstatic int
53790111Simpsubevalvar(char *p, char *str, int strloc, int subtype, int startloc,
538214524Sjilles  int varflags, int quotes)
53917987Speter{
54017987Speter	char *startp;
54117987Speter	char *loc = NULL;
54245514Stegge	char *q;
54317987Speter	int c = 0;
54417987Speter	struct nodelist *saveargbackq = argbackq;
54520425Ssteve	int amount;
54620425Ssteve
547206150Sjilles	argstr(p, (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX ||
548206147Sjilles	    subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX ?
549206150Sjilles	    EXP_CASE : 0) | EXP_TILDE);
55017987Speter	STACKSTRNUL(expdest);
55117987Speter	argbackq = saveargbackq;
55217987Speter	startp = stackblock() + startloc;
55320425Ssteve	if (str == NULL)
55420425Ssteve	    str = stackblock() + strloc;
55517987Speter
55617987Speter	switch (subtype) {
55717987Speter	case VSASSIGN:
55817987Speter		setvar(str, startp, 0);
55920425Ssteve		amount = startp - expdest;
56020425Ssteve		STADJUST(amount, expdest);
56117987Speter		varflags &= ~VSNUL;
56217987Speter		return 1;
56317987Speter
56417987Speter	case VSQUESTION:
56517987Speter		if (*p != CTLENDVAR) {
566201366Sjilles			outfmt(out2, "%s\n", startp);
56717987Speter			error((char *)NULL);
56817987Speter		}
569112254Sru		error("%.*s: parameter %snot set", (int)(p - str - 1),
57017987Speter		      str, (varflags & VSNUL) ? "null or "
57117987Speter					      : nullstr);
57217987Speter		return 0;
57317987Speter
57417987Speter	case VSTRIMLEFT:
57525233Ssteve		for (loc = startp; loc < str; loc++) {
57617987Speter			c = *loc;
57717987Speter			*loc = '\0';
578214524Sjilles			if (patmatch(str, startp, quotes)) {
57917987Speter				*loc = c;
58017987Speter				goto recordleft;
58117987Speter			}
58217987Speter			*loc = c;
583214524Sjilles			if (quotes && *loc == CTLESC)
58445514Stegge				loc++;
58517987Speter		}
58617987Speter		return 0;
58717987Speter
58817987Speter	case VSTRIMLEFTMAX:
58945514Stegge		for (loc = str - 1; loc >= startp;) {
59017987Speter			c = *loc;
59117987Speter			*loc = '\0';
592214524Sjilles			if (patmatch(str, startp, quotes)) {
59317987Speter				*loc = c;
59417987Speter				goto recordleft;
59517987Speter			}
59617987Speter			*loc = c;
59745514Stegge			loc--;
598214524Sjilles			if (quotes && loc > startp && *(loc - 1) == CTLESC) {
59945514Stegge				for (q = startp; q < loc; q++)
60045514Stegge					if (*q == CTLESC)
60145514Stegge						q++;
60245514Stegge				if (q > loc)
60345514Stegge					loc--;
60445514Stegge			}
60517987Speter		}
60617987Speter		return 0;
60717987Speter
60817987Speter	case VSTRIMRIGHT:
60945514Stegge		for (loc = str - 1; loc >= startp;) {
610214524Sjilles			if (patmatch(str, loc, quotes)) {
61120425Ssteve				amount = loc - expdest;
61220425Ssteve				STADJUST(amount, expdest);
61317987Speter				return 1;
61417987Speter			}
61545514Stegge			loc--;
616214524Sjilles			if (quotes && loc > startp && *(loc - 1) == CTLESC) {
61745514Stegge				for (q = startp; q < loc; q++)
61845514Stegge					if (*q == CTLESC)
61945514Stegge						q++;
62045514Stegge				if (q > loc)
62145514Stegge					loc--;
62245514Stegge			}
62317987Speter		}
62417987Speter		return 0;
62517987Speter
62617987Speter	case VSTRIMRIGHTMAX:
62717987Speter		for (loc = startp; loc < str - 1; loc++) {
628214524Sjilles			if (patmatch(str, loc, quotes)) {
62920425Ssteve				amount = loc - expdest;
63020425Ssteve				STADJUST(amount, expdest);
63117987Speter				return 1;
63217987Speter			}
633214524Sjilles			if (quotes && *loc == CTLESC)
63445514Stegge				loc++;
63517987Speter		}
63617987Speter		return 0;
63717987Speter
63817987Speter
63917987Speter	default:
64017987Speter		abort();
64117987Speter	}
64217987Speter
64317987Speterrecordleft:
64420425Ssteve	amount = ((str - 1) - (loc - startp)) - expdest;
64520425Ssteve	STADJUST(amount, expdest);
64617987Speter	while (loc != str - 1)
64717987Speter		*startp++ = *loc++;
64817987Speter	return 1;
64917987Speter}
65017987Speter
65117987Speter
6521556Srgrimes/*
6531556Srgrimes * Expand a variable, and return a pointer to the next character in the
6541556Srgrimes * input string.
6551556Srgrimes */
6561556Srgrimes
657213811Sobrienstatic char *
65890111Simpevalvar(char *p, int flag)
65917987Speter{
6601556Srgrimes	int subtype;
6611556Srgrimes	int varflags;
6621556Srgrimes	char *var;
6631556Srgrimes	char *val;
66445644Stegge	int patloc;
6651556Srgrimes	int c;
6661556Srgrimes	int set;
6671556Srgrimes	int special;
6681556Srgrimes	int startloc;
66917987Speter	int varlen;
670221602Sjilles	int varlenb;
67117987Speter	int easy;
672108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
6731556Srgrimes
674164081Sstefanf	varflags = (unsigned char)*p++;
6751556Srgrimes	subtype = varflags & VSTYPE;
6761556Srgrimes	var = p;
6771556Srgrimes	special = 0;
6781556Srgrimes	if (! is_name(*p))
6791556Srgrimes		special = 1;
6801556Srgrimes	p = strchr(p, '=') + 1;
6811556Srgrimesagain: /* jump here after setting a variable with ${var=text} */
682179022Sstefanf	if (varflags & VSLINENO) {
683179022Sstefanf		set = 1;
684179022Sstefanf		special = 0;
685179022Sstefanf		val = var;
686179022Sstefanf		p[-1] = '\0';	/* temporarily overwrite '=' to have \0
687179022Sstefanf				   terminated string */
688179022Sstefanf	} else if (special) {
68925233Ssteve		set = varisset(var, varflags & VSNUL);
6901556Srgrimes		val = NULL;
6911556Srgrimes	} else {
69260592Scracauer		val = bltinlookup(var, 1);
69317987Speter		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
6941556Srgrimes			val = NULL;
6951556Srgrimes			set = 0;
6961556Srgrimes		} else
6971556Srgrimes			set = 1;
6981556Srgrimes	}
69917987Speter	varlen = 0;
7001556Srgrimes	startloc = expdest - stackblock();
701198454Sjilles	if (!set && uflag && *var != '@' && *var != '*') {
70296939Stjr		switch (subtype) {
70396939Stjr		case VSNORMAL:
70496939Stjr		case VSTRIMLEFT:
70596939Stjr		case VSTRIMLEFTMAX:
70696939Stjr		case VSTRIMRIGHT:
70796939Stjr		case VSTRIMRIGHTMAX:
70896939Stjr		case VSLENGTH:
709112254Sru			error("%.*s: parameter not set", (int)(p - var - 1),
710112254Sru			    var);
71196939Stjr		}
71296939Stjr	}
7131556Srgrimes	if (set && subtype != VSPLUS) {
7141556Srgrimes		/* insert the value of the variable */
7151556Srgrimes		if (special) {
716164081Sstefanf			varvalue(var, varflags & VSQUOTE, subtype, flag);
71717987Speter			if (subtype == VSLENGTH) {
718221602Sjilles				varlenb = expdest - stackblock() - startloc;
719221602Sjilles				varlen = varlenb;
720221602Sjilles				if (localeisutf8) {
721221602Sjilles					val = stackblock() + startloc;
722221602Sjilles					for (;val != expdest; val++)
723221602Sjilles						if ((*val & 0xC0) == 0x80)
724221602Sjilles							varlen--;
725221602Sjilles				}
726221602Sjilles				STADJUST(-varlenb, expdest);
72717987Speter			}
7281556Srgrimes		} else {
72920425Ssteve			char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
73017987Speter								  : BASESYNTAX;
7311556Srgrimes
73217987Speter			if (subtype == VSLENGTH) {
73317987Speter				for (;*val; val++)
734221602Sjilles					if (!localeisutf8 ||
735221602Sjilles					    (*val & 0xC0) != 0x80)
736221602Sjilles						varlen++;
7371556Srgrimes			}
73817987Speter			else {
739216384Sjilles				if (quotes)
740216384Sjilles					STPUTS_QUOTES(val, syntax, expdest);
741216384Sjilles				else
742216384Sjilles					STPUTS(val, expdest);
74317987Speter
74417987Speter			}
7451556Srgrimes		}
7461556Srgrimes	}
74720425Ssteve
7481556Srgrimes	if (subtype == VSPLUS)
7491556Srgrimes		set = ! set;
75017987Speter
75120425Ssteve	easy = ((varflags & VSQUOTE) == 0 ||
75217987Speter		(*var == '@' && shellparam.nparam != 1));
75317987Speter
75417987Speter
75517987Speter	switch (subtype) {
75617987Speter	case VSLENGTH:
75717987Speter		expdest = cvtnum(varlen, expdest);
75817987Speter		goto record;
75917987Speter
76017987Speter	case VSNORMAL:
76117987Speter		if (!easy)
76217987Speter			break;
76317987Speterrecord:
76420425Ssteve		recordregion(startloc, expdest - stackblock(),
765222361Sjilles		    varflags & VSQUOTE || (ifsset() && ifsval()[0] == '\0' &&
766222361Sjilles		    (*var == '@' || *var == '*')));
76717987Speter		break;
76817987Speter
76917987Speter	case VSPLUS:
77017987Speter	case VSMINUS:
77117987Speter		if (!set) {
772214512Sjilles			argstr(p, flag | (flag & EXP_FULL ? EXP_SPLIT_LIT : 0) |
773214512Sjilles			    (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0));
77417987Speter			break;
77517987Speter		}
77617987Speter		if (easy)
77717987Speter			goto record;
77817987Speter		break;
77917987Speter
78017987Speter	case VSTRIMLEFT:
78117987Speter	case VSTRIMLEFTMAX:
78217987Speter	case VSTRIMRIGHT:
78317987Speter	case VSTRIMRIGHTMAX:
78417987Speter		if (!set)
78517987Speter			break;
78617987Speter		/*
78717987Speter		 * Terminate the string and start recording the pattern
78817987Speter		 * right after it
78917987Speter		 */
79017987Speter		STPUTC('\0', expdest);
79145644Stegge		patloc = expdest - stackblock();
79245644Stegge		if (subevalvar(p, NULL, patloc, subtype,
793214524Sjilles		    startloc, varflags, quotes) == 0) {
79445644Stegge			int amount = (expdest - stackblock() - patloc) + 1;
79525233Ssteve			STADJUST(-amount, expdest);
79625233Ssteve		}
79738887Stegge		/* Remove any recorded regions beyond start of variable */
79838887Stegge		removerecordregions(startloc);
79938887Stegge		goto record;
80017987Speter
80117987Speter	case VSASSIGN:
80217987Speter	case VSQUESTION:
80317987Speter		if (!set) {
804214524Sjilles			if (subevalvar(p, var, 0, subtype, startloc, varflags,
805214524Sjilles			    quotes)) {
80620425Ssteve				varflags &= ~VSNUL;
807155301Sschweikh				/*
808155301Sschweikh				 * Remove any recorded regions beyond
809155301Sschweikh				 * start of variable
81038887Stegge				 */
81138887Stegge				removerecordregions(startloc);
8121556Srgrimes				goto again;
81320425Ssteve			}
81417987Speter			break;
8151556Srgrimes		}
81617987Speter		if (easy)
81717987Speter			goto record;
81817987Speter		break;
81917987Speter
820164003Sstefanf	case VSERROR:
821164003Sstefanf		c = p - var - 1;
822164003Sstefanf		error("${%.*s%s}: Bad substitution", c, var,
823164003Sstefanf		    (c > 0 && *p != CTLENDVAR) ? "..." : "");
824164003Sstefanf
82517987Speter	default:
82617987Speter		abort();
8271556Srgrimes	}
828179022Sstefanf	p[-1] = '=';	/* recover overwritten '=' */
82917987Speter
8301556Srgrimes	if (subtype != VSNORMAL) {	/* skip to end of alternative */
8311556Srgrimes		int nesting = 1;
8321556Srgrimes		for (;;) {
8331556Srgrimes			if ((c = *p++) == CTLESC)
8341556Srgrimes				p++;
8351556Srgrimes			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
8361556Srgrimes				if (set)
8371556Srgrimes					argbackq = argbackq->next;
8381556Srgrimes			} else if (c == CTLVAR) {
8391556Srgrimes				if ((*p++ & VSTYPE) != VSNORMAL)
8401556Srgrimes					nesting++;
8411556Srgrimes			} else if (c == CTLENDVAR) {
8421556Srgrimes				if (--nesting == 0)
8431556Srgrimes					break;
8441556Srgrimes			}
8451556Srgrimes		}
8461556Srgrimes	}
8471556Srgrimes	return p;
8481556Srgrimes}
8491556Srgrimes
8501556Srgrimes
8511556Srgrimes
8521556Srgrimes/*
8531556Srgrimes * Test whether a specialized variable is set.
8541556Srgrimes */
8551556Srgrimes
856213811Sobrienstatic int
85790111Simpvarisset(char *name, int nulok)
85825233Ssteve{
8591556Srgrimes
86025233Ssteve	if (*name == '!')
861209600Sjilles		return backgndpidset();
86225233Ssteve	else if (*name == '@' || *name == '*') {
8631556Srgrimes		if (*shellparam.p == NULL)
8641556Srgrimes			return 0;
86525233Ssteve
86625233Ssteve		if (nulok) {
86725233Ssteve			char **av;
86825233Ssteve
86925233Ssteve			for (av = shellparam.p; *av; av++)
87025233Ssteve				if (**av != '\0')
87125233Ssteve					return 1;
87225233Ssteve			return 0;
87325233Ssteve		}
87418202Speter	} else if (is_digit(*name)) {
87525233Ssteve		char *ap;
87620425Ssteve		int num = atoi(name);
87725233Ssteve
87825233Ssteve		if (num > shellparam.nparam)
87925233Ssteve			return 0;
88025233Ssteve
88125233Ssteve		if (num == 0)
88225233Ssteve			ap = arg0;
88325233Ssteve		else
88425233Ssteve			ap = shellparam.p[num - 1];
88525233Ssteve
88625233Ssteve		if (nulok && (ap == NULL || *ap == '\0'))
88725233Ssteve			return 0;
8881556Srgrimes	}
8891556Srgrimes	return 1;
8901556Srgrimes}
8911556Srgrimes
892216384Sjillesstatic void
893216384Sjillesstrtodest(const char *p, int flag, int subtype, int quoted)
894216384Sjilles{
895216384Sjilles	if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH)
896216384Sjilles		STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest);
897216384Sjilles	else
898216384Sjilles		STPUTS(p, expdest);
899216384Sjilles}
9001556Srgrimes
9011556Srgrimes/*
9021556Srgrimes * Add the value of a specialized variable to the stack string.
9031556Srgrimes */
9041556Srgrimes
905213811Sobrienstatic void
906164081Sstefanfvarvalue(char *name, int quoted, int subtype, int flag)
90717987Speter{
9081556Srgrimes	int num;
9091556Srgrimes	char *p;
9101556Srgrimes	int i;
9111556Srgrimes	char sep;
9121556Srgrimes	char **ap;
9131556Srgrimes
91418202Speter	switch (*name) {
9151556Srgrimes	case '$':
9161556Srgrimes		num = rootpid;
9171556Srgrimes		goto numvar;
9181556Srgrimes	case '?':
91917987Speter		num = oexitstatus;
9201556Srgrimes		goto numvar;
9211556Srgrimes	case '#':
9221556Srgrimes		num = shellparam.nparam;
9231556Srgrimes		goto numvar;
9241556Srgrimes	case '!':
925209600Sjilles		num = backgndpidval();
9261556Srgrimesnumvar:
92717987Speter		expdest = cvtnum(num, expdest);
9281556Srgrimes		break;
9291556Srgrimes	case '-':
9301556Srgrimes		for (i = 0 ; i < NOPTS ; i++) {
9311556Srgrimes			if (optlist[i].val)
9321556Srgrimes				STPUTC(optlist[i].letter, expdest);
9331556Srgrimes		}
9341556Srgrimes		break;
9351556Srgrimes	case '@':
936164081Sstefanf		if (flag & EXP_FULL && quoted) {
93738887Stegge			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
938216384Sjilles				strtodest(p, flag, subtype, quoted);
93938887Stegge				if (*ap)
94038887Stegge					STPUTC('\0', expdest);
94138887Stegge			}
94238887Stegge			break;
9431556Srgrimes		}
944102410Scharnier		/* FALLTHROUGH */
9451556Srgrimes	case '*':
946149825Srse		if (ifsset())
94738887Stegge			sep = ifsval()[0];
94838887Stegge		else
94938887Stegge			sep = ' ';
9501556Srgrimes		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
951216384Sjilles			strtodest(p, flag, subtype, quoted);
952222361Sjilles			if (!*ap)
953222361Sjilles				break;
954222361Sjilles			if (sep || (flag & EXP_FULL && !quoted && **ap != '\0'))
9551556Srgrimes				STPUTC(sep, expdest);
9561556Srgrimes		}
9571556Srgrimes		break;
9581556Srgrimes	case '0':
9591556Srgrimes		p = arg0;
960216384Sjilles		strtodest(p, flag, subtype, quoted);
9611556Srgrimes		break;
9621556Srgrimes	default:
96318202Speter		if (is_digit(*name)) {
96418202Speter			num = atoi(name);
96518202Speter			if (num > 0 && num <= shellparam.nparam) {
96618202Speter				p = shellparam.p[num - 1];
967216384Sjilles				strtodest(p, flag, subtype, quoted);
96818202Speter			}
9691556Srgrimes		}
9701556Srgrimes		break;
9711556Srgrimes	}
9721556Srgrimes}
9731556Srgrimes
9741556Srgrimes
9751556Srgrimes
9761556Srgrimes/*
977218909Sbrucec * Record the fact that we have to scan this region of the
9781556Srgrimes * string for IFS characters.
9791556Srgrimes */
9801556Srgrimes
981213811Sobrienstatic void
982194975Sjillesrecordregion(int start, int end, int inquotes)
98317987Speter{
98425233Ssteve	struct ifsregion *ifsp;
9851556Srgrimes
9861556Srgrimes	if (ifslastp == NULL) {
9871556Srgrimes		ifsp = &ifsfirst;
9881556Srgrimes	} else {
989194975Sjilles		if (ifslastp->endoff == start
990194975Sjilles		    && ifslastp->inquotes == inquotes) {
991194975Sjilles			/* extend previous area */
992194975Sjilles			ifslastp->endoff = end;
993194975Sjilles			return;
994194975Sjilles		}
9951556Srgrimes		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
9961556Srgrimes		ifslastp->next = ifsp;
9971556Srgrimes	}
9981556Srgrimes	ifslastp = ifsp;
9991556Srgrimes	ifslastp->next = NULL;
10001556Srgrimes	ifslastp->begoff = start;
10011556Srgrimes	ifslastp->endoff = end;
1002194975Sjilles	ifslastp->inquotes = inquotes;
10031556Srgrimes}
10041556Srgrimes
10051556Srgrimes
10061556Srgrimes
10071556Srgrimes/*
10081556Srgrimes * Break the argument string into pieces based upon IFS and add the
10091556Srgrimes * strings to the argument list.  The regions of the string to be
10101556Srgrimes * searched for IFS characters have been stored by recordregion.
1011212243Sjilles * CTLESC characters are preserved but have little effect in this pass
1012212243Sjilles * other than escaping CTL* characters.  In particular, they do not escape
1013212243Sjilles * IFS characters: that should be done with the ifsregion mechanism.
1014212243Sjilles * CTLQUOTEMARK characters are used to preserve empty quoted strings.
1015212243Sjilles * This pass treats them as a regular character, making the string non-empty.
1016212243Sjilles * Later, they are removed along with the other CTL* characters.
10171556Srgrimes */
1018213811Sobrienstatic void
101990111Simpifsbreakup(char *string, struct arglist *arglist)
102090111Simp{
10211556Srgrimes	struct ifsregion *ifsp;
10221556Srgrimes	struct strlist *sp;
10231556Srgrimes	char *start;
102425233Ssteve	char *p;
10251556Srgrimes	char *q;
1026201053Sjilles	const char *ifs;
1027194975Sjilles	const char *ifsspc;
1028194975Sjilles	int had_param_ch = 0;
10291556Srgrimes
1030194975Sjilles	start = string;
103117987Speter
1032194975Sjilles	if (ifslastp == NULL) {
1033194975Sjilles		/* Return entire argument, IFS doesn't apply to any of it */
1034194975Sjilles		sp = (struct strlist *)stalloc(sizeof *sp);
1035194975Sjilles		sp->text = start;
1036194975Sjilles		*arglist->lastp = sp;
1037194975Sjilles		arglist->lastp = &sp->next;
1038194975Sjilles		return;
1039194975Sjilles	}
1040194975Sjilles
1041194975Sjilles	ifs = ifsset() ? ifsval() : " \t\n";
1042194975Sjilles
1043194975Sjilles	for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
1044194975Sjilles		p = string + ifsp->begoff;
1045194975Sjilles		while (p < string + ifsp->endoff) {
1046194975Sjilles			q = p;
1047194975Sjilles			if (*p == CTLESC)
1048194975Sjilles				p++;
1049194975Sjilles			if (ifsp->inquotes) {
1050194975Sjilles				/* Only NULs (should be from "$@") end args */
1051194977Sjilles				had_param_ch = 1;
1052194975Sjilles				if (*p != 0) {
10531556Srgrimes					p++;
1054194975Sjilles					continue;
1055194975Sjilles				}
1056194975Sjilles				ifsspc = NULL;
1057194975Sjilles			} else {
1058194975Sjilles				if (!strchr(ifs, *p)) {
1059194977Sjilles					had_param_ch = 1;
106038887Stegge					p++;
1061194975Sjilles					continue;
1062194975Sjilles				}
1063194975Sjilles				ifsspc = strchr(" \t\n", *p);
1064194975Sjilles
1065194975Sjilles				/* Ignore IFS whitespace at start */
1066194975Sjilles				if (q == start && ifsspc != NULL) {
1067194975Sjilles					p++;
10681556Srgrimes					start = p;
1069194975Sjilles					continue;
1070194975Sjilles				}
1071194977Sjilles				had_param_ch = 0;
10721556Srgrimes			}
1073194975Sjilles
1074194975Sjilles			/* Save this argument... */
1075194975Sjilles			*q = '\0';
10761556Srgrimes			sp = (struct strlist *)stalloc(sizeof *sp);
10771556Srgrimes			sp->text = start;
10781556Srgrimes			*arglist->lastp = sp;
10791556Srgrimes			arglist->lastp = &sp->next;
1080194975Sjilles			p++;
1081194975Sjilles
1082194975Sjilles			if (ifsspc != NULL) {
1083194975Sjilles				/* Ignore further trailing IFS whitespace */
1084194975Sjilles				for (; p < string + ifsp->endoff; p++) {
1085194975Sjilles					q = p;
1086194975Sjilles					if (*p == CTLESC)
1087194975Sjilles						p++;
1088194975Sjilles					if (strchr(ifs, *p) == NULL) {
1089194975Sjilles						p = q;
1090194975Sjilles						break;
1091194975Sjilles					}
1092194975Sjilles					if (strchr(" \t\n", *p) == NULL) {
1093194975Sjilles						p++;
1094194975Sjilles						break;
1095194975Sjilles					}
1096194975Sjilles				}
1097194975Sjilles			}
1098194975Sjilles			start = p;
10991556Srgrimes		}
1100194975Sjilles	}
1101194975Sjilles
1102194975Sjilles	/*
1103194975Sjilles	 * Save anything left as an argument.
1104194975Sjilles	 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
1105194975Sjilles	 * generating 2 arguments, the second of which is empty.
1106194975Sjilles	 * Some recent clarification of the Posix spec say that it
1107194975Sjilles	 * should only generate one....
1108194975Sjilles	 */
1109194975Sjilles	if (had_param_ch || *start != 0) {
11101556Srgrimes		sp = (struct strlist *)stalloc(sizeof *sp);
11111556Srgrimes		sp->text = start;
11121556Srgrimes		*arglist->lastp = sp;
11131556Srgrimes		arglist->lastp = &sp->next;
11141556Srgrimes	}
11151556Srgrimes}
11161556Srgrimes
11171556Srgrimes
1118213760Sobrienstatic char expdir[PATH_MAX];
1119212243Sjilles#define expdir_end (expdir + sizeof(expdir))
11201556Srgrimes
11211556Srgrimes/*
1122212243Sjilles * Perform pathname generation and remove control characters.
1123212243Sjilles * At this point, the only control characters should be CTLESC and CTLQUOTEMARK.
1124212243Sjilles * The results are stored in the list exparg.
11251556Srgrimes */
1126213811Sobrienstatic void
112790111Simpexpandmeta(struct strlist *str, int flag __unused)
112817987Speter{
11291556Srgrimes	char *p;
11301556Srgrimes	struct strlist **savelastp;
11311556Srgrimes	struct strlist *sp;
11321556Srgrimes	char c;
11331556Srgrimes	/* TODO - EXP_REDIR */
11341556Srgrimes
11351556Srgrimes	while (str) {
11361556Srgrimes		if (fflag)
11371556Srgrimes			goto nometa;
11381556Srgrimes		p = str->text;
11391556Srgrimes		for (;;) {			/* fast check for meta chars */
11401556Srgrimes			if ((c = *p++) == '\0')
11411556Srgrimes				goto nometa;
1142211646Sjilles			if (c == '*' || c == '?' || c == '[')
11431556Srgrimes				break;
11441556Srgrimes		}
11451556Srgrimes		savelastp = exparg.lastp;
11461556Srgrimes		INTOFF;
11471556Srgrimes		expmeta(expdir, str->text);
11481556Srgrimes		INTON;
11491556Srgrimes		if (exparg.lastp == savelastp) {
11508855Srgrimes			/*
11518855Srgrimes			 * no matches
11521556Srgrimes			 */
11531556Srgrimesnometa:
11541556Srgrimes			*exparg.lastp = str;
11551556Srgrimes			rmescapes(str->text);
11561556Srgrimes			exparg.lastp = &str->next;
11571556Srgrimes		} else {
11581556Srgrimes			*exparg.lastp = NULL;
11591556Srgrimes			*savelastp = sp = expsort(*savelastp);
11601556Srgrimes			while (sp->next != NULL)
11611556Srgrimes				sp = sp->next;
11621556Srgrimes			exparg.lastp = &sp->next;
11631556Srgrimes		}
11641556Srgrimes		str = str->next;
11651556Srgrimes	}
11661556Srgrimes}
11671556Srgrimes
11681556Srgrimes
11691556Srgrimes/*
11701556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion.
11711556Srgrimes */
11721556Srgrimes
1173213811Sobrienstatic void
117490111Simpexpmeta(char *enddir, char *name)
117590111Simp{
117625233Ssteve	char *p;
11771556Srgrimes	char *q;
11781556Srgrimes	char *start;
11791556Srgrimes	char *endname;
11801556Srgrimes	int metaflag;
11811556Srgrimes	struct stat statb;
11821556Srgrimes	DIR *dirp;
11831556Srgrimes	struct dirent *dp;
11841556Srgrimes	int atend;
11851556Srgrimes	int matchdot;
1186207944Sjilles	int esc;
11871556Srgrimes
11881556Srgrimes	metaflag = 0;
11891556Srgrimes	start = name;
1190207944Sjilles	for (p = name; esc = 0, *p; p += esc + 1) {
11911556Srgrimes		if (*p == '*' || *p == '?')
11921556Srgrimes			metaflag = 1;
11931556Srgrimes		else if (*p == '[') {
11941556Srgrimes			q = p + 1;
119526488Sache			if (*q == '!' || *q == '^')
11961556Srgrimes				q++;
11971556Srgrimes			for (;;) {
119838887Stegge				while (*q == CTLQUOTEMARK)
119938887Stegge					q++;
12001556Srgrimes				if (*q == CTLESC)
12011556Srgrimes					q++;
12021556Srgrimes				if (*q == '/' || *q == '\0')
12031556Srgrimes					break;
12041556Srgrimes				if (*++q == ']') {
12051556Srgrimes					metaflag = 1;
12061556Srgrimes					break;
12071556Srgrimes				}
12081556Srgrimes			}
12091556Srgrimes		} else if (*p == '\0')
12101556Srgrimes			break;
121138887Stegge		else if (*p == CTLQUOTEMARK)
121238887Stegge			continue;
1213207944Sjilles		else {
1214207944Sjilles			if (*p == CTLESC)
1215207944Sjilles				esc++;
1216207944Sjilles			if (p[esc] == '/') {
1217207944Sjilles				if (metaflag)
1218207944Sjilles					break;
1219207944Sjilles				start = p + esc + 1;
1220207944Sjilles			}
12211556Srgrimes		}
12221556Srgrimes	}
12231556Srgrimes	if (metaflag == 0) {	/* we've reached the end of the file name */
12241556Srgrimes		if (enddir != expdir)
12251556Srgrimes			metaflag++;
12261556Srgrimes		for (p = name ; ; p++) {
122738887Stegge			if (*p == CTLQUOTEMARK)
122838887Stegge				continue;
12291556Srgrimes			if (*p == CTLESC)
12301556Srgrimes				p++;
12311556Srgrimes			*enddir++ = *p;
12321556Srgrimes			if (*p == '\0')
12331556Srgrimes				break;
1234211155Sjilles			if (enddir == expdir_end)
1235211155Sjilles				return;
12361556Srgrimes		}
1237147812Sdelphij		if (metaflag == 0 || lstat(expdir, &statb) >= 0)
12381556Srgrimes			addfname(expdir);
12391556Srgrimes		return;
12401556Srgrimes	}
12411556Srgrimes	endname = p;
12421556Srgrimes	if (start != name) {
12431556Srgrimes		p = name;
12441556Srgrimes		while (p < start) {
124538887Stegge			while (*p == CTLQUOTEMARK)
124638887Stegge				p++;
12471556Srgrimes			if (*p == CTLESC)
12481556Srgrimes				p++;
12491556Srgrimes			*enddir++ = *p++;
1250211155Sjilles			if (enddir == expdir_end)
1251211155Sjilles				return;
12521556Srgrimes		}
12531556Srgrimes	}
12541556Srgrimes	if (enddir == expdir) {
12551556Srgrimes		p = ".";
12561556Srgrimes	} else if (enddir == expdir + 1 && *expdir == '/') {
12571556Srgrimes		p = "/";
12581556Srgrimes	} else {
12591556Srgrimes		p = expdir;
12601556Srgrimes		enddir[-1] = '\0';
12611556Srgrimes	}
12621556Srgrimes	if ((dirp = opendir(p)) == NULL)
12631556Srgrimes		return;
12641556Srgrimes	if (enddir != expdir)
12651556Srgrimes		enddir[-1] = '/';
12661556Srgrimes	if (*endname == 0) {
12671556Srgrimes		atend = 1;
12681556Srgrimes	} else {
12691556Srgrimes		atend = 0;
1270207944Sjilles		*endname = '\0';
1271207944Sjilles		endname += esc + 1;
12721556Srgrimes	}
12731556Srgrimes	matchdot = 0;
127438887Stegge	p = start;
127538887Stegge	while (*p == CTLQUOTEMARK)
127638887Stegge		p++;
127738887Stegge	if (*p == CTLESC)
127838887Stegge		p++;
127938887Stegge	if (*p == '.')
12801556Srgrimes		matchdot++;
12811556Srgrimes	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
12821556Srgrimes		if (dp->d_name[0] == '.' && ! matchdot)
12831556Srgrimes			continue;
128445514Stegge		if (patmatch(start, dp->d_name, 0)) {
1285211155Sjilles			if (enddir + dp->d_namlen + 1 > expdir_end)
1286211155Sjilles				continue;
1287211155Sjilles			memcpy(enddir, dp->d_name, dp->d_namlen + 1);
1288211155Sjilles			if (atend)
12891556Srgrimes				addfname(expdir);
1290211155Sjilles			else {
1291211155Sjilles				if (enddir + dp->d_namlen + 2 > expdir_end)
129217987Speter					continue;
1293211155Sjilles				enddir[dp->d_namlen] = '/';
1294211155Sjilles				enddir[dp->d_namlen + 1] = '\0';
1295211155Sjilles				expmeta(enddir + dp->d_namlen + 1, endname);
12961556Srgrimes			}
12971556Srgrimes		}
12981556Srgrimes	}
12991556Srgrimes	closedir(dirp);
13001556Srgrimes	if (! atend)
1301207944Sjilles		endname[-esc - 1] = esc ? CTLESC : '/';
13021556Srgrimes}
13031556Srgrimes
13041556Srgrimes
13051556Srgrimes/*
13061556Srgrimes * Add a file name to the list.
13071556Srgrimes */
13081556Srgrimes
1309213811Sobrienstatic void
131090111Simpaddfname(char *name)
131190111Simp{
13121556Srgrimes	char *p;
13131556Srgrimes	struct strlist *sp;
13141556Srgrimes
13151556Srgrimes	p = stalloc(strlen(name) + 1);
13161556Srgrimes	scopy(name, p);
13171556Srgrimes	sp = (struct strlist *)stalloc(sizeof *sp);
13181556Srgrimes	sp->text = p;
13191556Srgrimes	*exparg.lastp = sp;
13201556Srgrimes	exparg.lastp = &sp->next;
13211556Srgrimes}
13221556Srgrimes
13231556Srgrimes
13241556Srgrimes/*
13251556Srgrimes * Sort the results of file name expansion.  It calculates the number of
13261556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the
13271556Srgrimes * work.
13281556Srgrimes */
13291556Srgrimes
1330213811Sobrienstatic struct strlist *
133190111Simpexpsort(struct strlist *str)
133290111Simp{
13331556Srgrimes	int len;
13341556Srgrimes	struct strlist *sp;
13351556Srgrimes
13361556Srgrimes	len = 0;
13371556Srgrimes	for (sp = str ; sp ; sp = sp->next)
13381556Srgrimes		len++;
13391556Srgrimes	return msort(str, len);
13401556Srgrimes}
13411556Srgrimes
13421556Srgrimes
1343213811Sobrienstatic struct strlist *
134490111Simpmsort(struct strlist *list, int len)
134517987Speter{
134617987Speter	struct strlist *p, *q = NULL;
13471556Srgrimes	struct strlist **lpp;
13481556Srgrimes	int half;
13491556Srgrimes	int n;
13501556Srgrimes
13511556Srgrimes	if (len <= 1)
13521556Srgrimes		return list;
13538855Srgrimes	half = len >> 1;
13541556Srgrimes	p = list;
13551556Srgrimes	for (n = half ; --n >= 0 ; ) {
13561556Srgrimes		q = p;
13571556Srgrimes		p = p->next;
13581556Srgrimes	}
13591556Srgrimes	q->next = NULL;			/* terminate first half of list */
13601556Srgrimes	q = msort(list, half);		/* sort first half of list */
13611556Srgrimes	p = msort(p, len - half);		/* sort second half */
13621556Srgrimes	lpp = &list;
13631556Srgrimes	for (;;) {
13641556Srgrimes		if (strcmp(p->text, q->text) < 0) {
13651556Srgrimes			*lpp = p;
13661556Srgrimes			lpp = &p->next;
13671556Srgrimes			if ((p = *lpp) == NULL) {
13681556Srgrimes				*lpp = q;
13691556Srgrimes				break;
13701556Srgrimes			}
13711556Srgrimes		} else {
13721556Srgrimes			*lpp = q;
13731556Srgrimes			lpp = &q->next;
13741556Srgrimes			if ((q = *lpp) == NULL) {
13751556Srgrimes				*lpp = p;
13761556Srgrimes				break;
13771556Srgrimes			}
13781556Srgrimes		}
13791556Srgrimes	}
13801556Srgrimes	return list;
13811556Srgrimes}
13821556Srgrimes
13831556Srgrimes
13841556Srgrimes
1385221646Sjillesstatic wchar_t
1386221646Sjillesget_wc(const char **p)
1387221646Sjilles{
1388221646Sjilles	wchar_t c;
1389221646Sjilles	int chrlen;
1390221646Sjilles
1391221646Sjilles	chrlen = mbtowc(&c, *p, 4);
1392221646Sjilles	if (chrlen == 0)
1393221646Sjilles		return 0;
1394221646Sjilles	else if (chrlen == -1)
1395221646Sjilles		c = 0;
1396221646Sjilles	else
1397221646Sjilles		*p += chrlen;
1398221646Sjilles	return c;
1399221646Sjilles}
1400221646Sjilles
1401221646Sjilles
14021556Srgrimes/*
14031556Srgrimes * Returns true if the pattern matches the string.
14041556Srgrimes */
14051556Srgrimes
14061556Srgrimesint
1407200956Sjillespatmatch(const char *pattern, const char *string, int squoted)
140890111Simp{
1409200956Sjilles	const char *p, *q;
141025233Ssteve	char c;
1411221646Sjilles	wchar_t wc, wc2;
14121556Srgrimes
14131556Srgrimes	p = pattern;
14141556Srgrimes	q = string;
14151556Srgrimes	for (;;) {
14161556Srgrimes		switch (c = *p++) {
14171556Srgrimes		case '\0':
14181556Srgrimes			goto breakloop;
14191556Srgrimes		case CTLESC:
142045514Stegge			if (squoted && *q == CTLESC)
142145514Stegge				q++;
14221556Srgrimes			if (*q++ != *p++)
14231556Srgrimes				return 0;
14241556Srgrimes			break;
142538887Stegge		case CTLQUOTEMARK:
142638887Stegge			continue;
14271556Srgrimes		case '?':
142845514Stegge			if (squoted && *q == CTLESC)
142945514Stegge				q++;
1430221646Sjilles			if (localeisutf8)
1431221646Sjilles				wc = get_wc(&q);
1432221646Sjilles			else
1433223010Sjilles				wc = (unsigned char)*q++;
1434221646Sjilles			if (wc == '\0')
14351556Srgrimes				return 0;
14361556Srgrimes			break;
14371556Srgrimes		case '*':
14381556Srgrimes			c = *p;
143938887Stegge			while (c == CTLQUOTEMARK || c == '*')
144038887Stegge				c = *++p;
144138887Stegge			if (c != CTLESC &&  c != CTLQUOTEMARK &&
144238887Stegge			    c != '?' && c != '*' && c != '[') {
14431556Srgrimes				while (*q != c) {
144445514Stegge					if (squoted && *q == CTLESC &&
144545514Stegge					    q[1] == c)
144645514Stegge						break;
14471556Srgrimes					if (*q == '\0')
14481556Srgrimes						return 0;
144945514Stegge					if (squoted && *q == CTLESC)
145045514Stegge						q++;
14511556Srgrimes					q++;
14521556Srgrimes				}
14531556Srgrimes			}
14541556Srgrimes			do {
1455211646Sjilles				if (patmatch(p, q, squoted))
14561556Srgrimes					return 1;
145745514Stegge				if (squoted && *q == CTLESC)
145845514Stegge					q++;
14591556Srgrimes			} while (*q++ != '\0');
14601556Srgrimes			return 0;
14611556Srgrimes		case '[': {
1462200956Sjilles			const char *endp;
14631556Srgrimes			int invert, found;
1464221646Sjilles			wchar_t chr;
14651556Srgrimes
14661556Srgrimes			endp = p;
146726488Sache			if (*endp == '!' || *endp == '^')
14681556Srgrimes				endp++;
14691556Srgrimes			for (;;) {
147038887Stegge				while (*endp == CTLQUOTEMARK)
147138887Stegge					endp++;
14721556Srgrimes				if (*endp == '\0')
14731556Srgrimes					goto dft;		/* no matching ] */
14741556Srgrimes				if (*endp == CTLESC)
14751556Srgrimes					endp++;
14761556Srgrimes				if (*++endp == ']')
14771556Srgrimes					break;
14781556Srgrimes			}
14791556Srgrimes			invert = 0;
148026488Sache			if (*p == '!' || *p == '^') {
14811556Srgrimes				invert++;
14821556Srgrimes				p++;
14831556Srgrimes			}
14841556Srgrimes			found = 0;
1485221646Sjilles			if (squoted && *q == CTLESC)
1486221646Sjilles				q++;
1487221646Sjilles			if (localeisutf8)
1488221646Sjilles				chr = get_wc(&q);
1489221646Sjilles			else
1490223010Sjilles				chr = (unsigned char)*q++;
149117987Speter			if (chr == '\0')
149217987Speter				return 0;
14931556Srgrimes			c = *p++;
14941556Srgrimes			do {
149538887Stegge				if (c == CTLQUOTEMARK)
149638887Stegge					continue;
14971556Srgrimes				if (c == CTLESC)
14981556Srgrimes					c = *p++;
1499221646Sjilles				if (localeisutf8 && c & 0x80) {
1500221646Sjilles					p--;
1501221646Sjilles					wc = get_wc(&p);
1502221646Sjilles					if (wc == 0) /* bad utf-8 */
1503221646Sjilles						return 0;
1504221646Sjilles				} else
1505223010Sjilles					wc = (unsigned char)c;
15061556Srgrimes				if (*p == '-' && p[1] != ']') {
15071556Srgrimes					p++;
150838887Stegge					while (*p == CTLQUOTEMARK)
150938887Stegge						p++;
15101556Srgrimes					if (*p == CTLESC)
15111556Srgrimes						p++;
1512221646Sjilles					if (localeisutf8) {
1513221646Sjilles						wc2 = get_wc(&p);
1514221646Sjilles						if (wc2 == 0) /* bad utf-8 */
1515221646Sjilles							return 0;
1516221646Sjilles					} else
1517223010Sjilles						wc2 = (unsigned char)*p++;
1518221646Sjilles					if (   collate_range_cmp(chr, wc) >= 0
1519221646Sjilles					    && collate_range_cmp(chr, wc2) <= 0
152017525Sache					   )
15211556Srgrimes						found = 1;
15221556Srgrimes				} else {
1523221646Sjilles					if (chr == wc)
15241556Srgrimes						found = 1;
15251556Srgrimes				}
15261556Srgrimes			} while ((c = *p++) != ']');
15271556Srgrimes			if (found == invert)
15281556Srgrimes				return 0;
15291556Srgrimes			break;
15301556Srgrimes		}
15311556Srgrimesdft:	        default:
153245514Stegge			if (squoted && *q == CTLESC)
153345514Stegge				q++;
15341556Srgrimes			if (*q++ != c)
15351556Srgrimes				return 0;
15361556Srgrimes			break;
15371556Srgrimes		}
15381556Srgrimes	}
15391556Srgrimesbreakloop:
15401556Srgrimes	if (*q != '\0')
15411556Srgrimes		return 0;
15421556Srgrimes	return 1;
15431556Srgrimes}
15441556Srgrimes
15451556Srgrimes
15461556Srgrimes
15471556Srgrimes/*
1548212243Sjilles * Remove any CTLESC and CTLQUOTEMARK characters from a string.
15491556Srgrimes */
15501556Srgrimes
15511556Srgrimesvoid
155290111Simprmescapes(char *str)
155338887Stegge{
155425233Ssteve	char *p, *q;
15551556Srgrimes
15561556Srgrimes	p = str;
1557214512Sjilles	while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) {
15581556Srgrimes		if (*p++ == '\0')
15591556Srgrimes			return;
15601556Srgrimes	}
15611556Srgrimes	q = p;
15621556Srgrimes	while (*p) {
1563214512Sjilles		if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) {
156438887Stegge			p++;
156538887Stegge			continue;
156638887Stegge		}
15671556Srgrimes		if (*p == CTLESC)
15681556Srgrimes			p++;
15691556Srgrimes		*q++ = *p++;
15701556Srgrimes	}
15711556Srgrimes	*q = '\0';
15721556Srgrimes}
15731556Srgrimes
15741556Srgrimes
15751556Srgrimes
15761556Srgrimes/*
15771556Srgrimes * See if a pattern matches in a case statement.
15781556Srgrimes */
15791556Srgrimes
15801556Srgrimesint
1581200956Sjillescasematch(union node *pattern, const char *val)
158290111Simp{
15831556Srgrimes	struct stackmark smark;
15841556Srgrimes	int result;
15851556Srgrimes	char *p;
15861556Srgrimes
15871556Srgrimes	setstackmark(&smark);
15881556Srgrimes	argbackq = pattern->narg.backquote;
15891556Srgrimes	STARTSTACKSTR(expdest);
15901556Srgrimes	ifslastp = NULL;
15911556Srgrimes	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
15921556Srgrimes	STPUTC('\0', expdest);
15931556Srgrimes	p = grabstackstr(expdest);
159445514Stegge	result = patmatch(p, val, 0);
15951556Srgrimes	popstackmark(&smark);
15961556Srgrimes	return result;
15971556Srgrimes}
159817987Speter
159917987Speter/*
160017987Speter * Our own itoa().
160117987Speter */
160217987Speter
1603213811Sobrienstatic char *
160490111Simpcvtnum(int num, char *buf)
160590111Simp{
160617987Speter	char temp[32];
160717987Speter	int neg = num < 0;
160817987Speter	char *p = temp + 31;
160917987Speter
161017987Speter	temp[31] = '\0';
161117987Speter
161217987Speter	do {
161317987Speter		*--p = num % 10 + '0';
161417987Speter	} while ((num /= 10) != 0);
161517987Speter
161617987Speter	if (neg)
161717987Speter		*--p = '-';
161817987Speter
1619215783Sjilles	STPUTS(p, buf);
162017987Speter	return buf;
162117987Speter}
1622108286Stjr
1623108286Stjr/*
1624216778Sjilles * Check statically if expanding a string may have side effects.
1625216778Sjilles */
1626216778Sjillesint
1627216778Sjillesexpandhassideeffects(const char *p)
1628216778Sjilles{
1629216778Sjilles	int c;
1630216778Sjilles	int arinest;
1631216778Sjilles
1632216778Sjilles	arinest = 0;
1633216778Sjilles	while ((c = *p++) != '\0') {
1634216778Sjilles		switch (c) {
1635216778Sjilles		case CTLESC:
1636216778Sjilles			p++;
1637216778Sjilles			break;
1638216778Sjilles		case CTLVAR:
1639216778Sjilles			c = *p++;
1640216778Sjilles			/* Expanding $! sets the job to remembered. */
1641216778Sjilles			if (*p == '!')
1642216778Sjilles				return 1;
1643216778Sjilles			if ((c & VSTYPE) == VSASSIGN)
1644216778Sjilles				return 1;
1645216778Sjilles			/*
1646216778Sjilles			 * If we are in arithmetic, the parameter may contain
1647216778Sjilles			 * '=' which may cause side effects. Exceptions are
1648216778Sjilles			 * the length of a parameter and $$, $# and $? which
1649216778Sjilles			 * are always numeric.
1650216778Sjilles			 */
1651216778Sjilles			if ((c & VSTYPE) == VSLENGTH) {
1652216778Sjilles				while (*p != '=')
1653216778Sjilles					p++;
1654216778Sjilles				p++;
1655216778Sjilles				break;
1656216778Sjilles			}
1657216778Sjilles			if ((*p == '$' || *p == '#' || *p == '?') &&
1658216778Sjilles			    p[1] == '=') {
1659216778Sjilles				p += 2;
1660216778Sjilles				break;
1661216778Sjilles			}
1662216778Sjilles			if (arinest > 0)
1663216778Sjilles				return 1;
1664216778Sjilles			break;
1665216778Sjilles		case CTLBACKQ:
1666216778Sjilles		case CTLBACKQ | CTLQUOTE:
1667216778Sjilles			if (arinest > 0)
1668216778Sjilles				return 1;
1669216778Sjilles			break;
1670216778Sjilles		case CTLARI:
1671216778Sjilles			arinest++;
1672216778Sjilles			break;
1673216778Sjilles		case CTLENDARI:
1674216778Sjilles			arinest--;
1675216778Sjilles			break;
1676216778Sjilles		case '=':
1677216778Sjilles			if (*p == '=') {
1678216778Sjilles				/* Allow '==' operator. */
1679216778Sjilles				p++;
1680216778Sjilles				continue;
1681216778Sjilles			}
1682216778Sjilles			if (arinest > 0)
1683216778Sjilles				return 1;
1684216778Sjilles			break;
1685216778Sjilles		case '!': case '<': case '>':
1686216778Sjilles			/* Allow '!=', '<=', '>=' operators. */
1687216778Sjilles			if (*p == '=')
1688216778Sjilles				p++;
1689216778Sjilles			break;
1690216778Sjilles		}
1691216778Sjilles	}
1692216778Sjilles	return 0;
1693216778Sjilles}
1694216778Sjilles
1695216778Sjilles/*
1696108286Stjr * Do most of the work for wordexp(3).
1697108286Stjr */
1698108286Stjr
1699108286Stjrint
1700108286Stjrwordexpcmd(int argc, char **argv)
1701108286Stjr{
1702108286Stjr	size_t len;
1703108286Stjr	int i;
1704108286Stjr
1705108286Stjr	out1fmt("%08x", argc - 1);
1706108286Stjr	for (i = 1, len = 0; i < argc; i++)
1707108286Stjr		len += strlen(argv[i]);
1708108286Stjr	out1fmt("%08x", (int)len);
1709215567Sjilles	for (i = 1; i < argc; i++)
1710215567Sjilles		outbin(argv[i], strlen(argv[i]) + 1, out1);
1711108286Stjr        return (0);
1712108286Stjr}
1713