expand.c revision 218909
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 218909 2011-02-21 09:01:34Z brucec $");
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>
5517987Speter
561556Srgrimes/*
571556Srgrimes * Routines to expand arguments to commands.  We have to deal with
581556Srgrimes * backquotes, shell variables, and file metacharacters.
591556Srgrimes */
601556Srgrimes
611556Srgrimes#include "shell.h"
621556Srgrimes#include "main.h"
631556Srgrimes#include "nodes.h"
641556Srgrimes#include "eval.h"
651556Srgrimes#include "expand.h"
661556Srgrimes#include "syntax.h"
671556Srgrimes#include "parser.h"
681556Srgrimes#include "jobs.h"
691556Srgrimes#include "options.h"
701556Srgrimes#include "var.h"
711556Srgrimes#include "input.h"
721556Srgrimes#include "output.h"
731556Srgrimes#include "memalloc.h"
741556Srgrimes#include "error.h"
751556Srgrimes#include "mystring.h"
7617987Speter#include "arith.h"
7717987Speter#include "show.h"
781556Srgrimes
791556Srgrimes/*
801556Srgrimes * Structure specifying which parts of the string should be searched
811556Srgrimes * for IFS characters.
821556Srgrimes */
831556Srgrimes
841556Srgrimesstruct ifsregion {
851556Srgrimes	struct ifsregion *next;	/* next region in list */
861556Srgrimes	int begoff;		/* offset of start of region */
871556Srgrimes	int endoff;		/* offset of end of region */
88194975Sjilles	int inquotes;		/* search for nul bytes only */
891556Srgrimes};
901556Srgrimes
911556Srgrimes
92213760Sobrienstatic char *expdest;			/* output of current string */
93213760Sobrienstatic struct nodelist *argbackq;	/* list of back quote expressions */
94213760Sobrienstatic struct ifsregion ifsfirst;	/* first struct in list of ifs regions */
95213760Sobrienstatic struct ifsregion *ifslastp;	/* last struct in list */
96213760Sobrienstatic struct arglist exparg;		/* holds expanded arg list */
971556Srgrimes
98213811Sobrienstatic void argstr(char *, int);
99213811Sobrienstatic char *exptilde(char *, int);
100213811Sobrienstatic void expbackq(union node *, int, int);
101214524Sjillesstatic int subevalvar(char *, char *, int, int, int, int, int);
102213811Sobrienstatic char *evalvar(char *, int);
103213811Sobrienstatic int varisset(char *, int);
104213811Sobrienstatic void varvalue(char *, int, int, int);
105213811Sobrienstatic void recordregion(int, int, int);
106213811Sobrienstatic void removerecordregions(int);
107213811Sobrienstatic void ifsbreakup(char *, struct arglist *);
108213811Sobrienstatic void expandmeta(struct strlist *, int);
109213811Sobrienstatic void expmeta(char *, char *);
110213811Sobrienstatic void addfname(char *);
111213811Sobrienstatic struct strlist *expsort(struct strlist *);
112213811Sobrienstatic struct strlist *msort(struct strlist *, int);
113213811Sobrienstatic char *cvtnum(int, char *);
114213811Sobrienstatic int collate_range_cmp(int, int);
1151556Srgrimes
116213811Sobrienstatic int
117118374Sachecollate_range_cmp(int c1, int c2)
11819281Sache{
11919281Sache	static char s1[2], s2[2];
12019281Sache
12119281Sache	s1[0] = c1;
12219281Sache	s2[0] = c2;
123118374Sache	return (strcoll(s1, s2));
12419281Sache}
12519281Sache
1261556Srgrimes/*
1271556Srgrimes * Expand shell variables and backquotes inside a here document.
12890111Simp *	union node *arg		the document
12990111Simp *	int fd;			where to write the expanded version
1301556Srgrimes */
1311556Srgrimes
1321556Srgrimesvoid
13390111Simpexpandhere(union node *arg, int fd)
13490111Simp{
1351556Srgrimes	expandarg(arg, (struct arglist *)NULL, 0);
13639137Stegge	xwrite(fd, stackblock(), expdest - stackblock());
1371556Srgrimes}
1381556Srgrimes
139216384Sjillesstatic char *
140216384Sjillesstputs_quotes(const char *data, const char *syntax, char *p)
141216384Sjilles{
142216384Sjilles	while (*data) {
143216384Sjilles		CHECKSTRSPACE(2, p);
144216384Sjilles		if (syntax[(int)*data] == CCTL)
145216384Sjilles			USTPUTC(CTLESC, p);
146216384Sjilles		USTPUTC(*data++, p);
147216384Sjilles	}
148216384Sjilles	return (p);
149216384Sjilles}
150216384Sjilles#define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p)
1511556Srgrimes
1521556Srgrimes/*
153212243Sjilles * Perform expansions on an argument, placing the resulting list of arguments
154212243Sjilles * in arglist.  Parameter expansion, command substitution and arithmetic
155212243Sjilles * expansion are always performed; additional expansions can be requested
156212243Sjilles * via flag (EXP_*).
157212243Sjilles * The result is left in the stack string.
158218203Sjilles * When arglist is NULL, perform here document expansion.
159212243Sjilles *
160212243Sjilles * Caution: this function uses global state and is not reentrant.
161212243Sjilles * However, a new invocation after an interrupted invocation is safe
162212243Sjilles * and will reset the global state for the new call.
1631556Srgrimes */
1641556Srgrimesvoid
16590111Simpexpandarg(union node *arg, struct arglist *arglist, int flag)
16617987Speter{
1671556Srgrimes	struct strlist *sp;
1681556Srgrimes	char *p;
1691556Srgrimes
1701556Srgrimes	argbackq = arg->narg.backquote;
1711556Srgrimes	STARTSTACKSTR(expdest);
1721556Srgrimes	ifsfirst.next = NULL;
1731556Srgrimes	ifslastp = NULL;
1741556Srgrimes	argstr(arg->narg.text, flag);
1751556Srgrimes	if (arglist == NULL) {
1761556Srgrimes		return;			/* here document expanded */
1771556Srgrimes	}
1781556Srgrimes	STPUTC('\0', expdest);
1791556Srgrimes	p = grabstackstr(expdest);
1801556Srgrimes	exparg.lastp = &exparg.list;
1811556Srgrimes	/*
1821556Srgrimes	 * TODO - EXP_REDIR
1831556Srgrimes	 */
1841556Srgrimes	if (flag & EXP_FULL) {
1851556Srgrimes		ifsbreakup(p, &exparg);
1861556Srgrimes		*exparg.lastp = NULL;
1871556Srgrimes		exparg.lastp = &exparg.list;
1881556Srgrimes		expandmeta(exparg.list, flag);
1891556Srgrimes	} else {
1901556Srgrimes		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
1911556Srgrimes			rmescapes(p);
1921556Srgrimes		sp = (struct strlist *)stalloc(sizeof (struct strlist));
1931556Srgrimes		sp->text = p;
1941556Srgrimes		*exparg.lastp = sp;
1951556Srgrimes		exparg.lastp = &sp->next;
1961556Srgrimes	}
1971556Srgrimes	while (ifsfirst.next != NULL) {
1981556Srgrimes		struct ifsregion *ifsp;
1991556Srgrimes		INTOFF;
2001556Srgrimes		ifsp = ifsfirst.next->next;
2011556Srgrimes		ckfree(ifsfirst.next);
2021556Srgrimes		ifsfirst.next = ifsp;
2031556Srgrimes		INTON;
2041556Srgrimes	}
2051556Srgrimes	*exparg.lastp = NULL;
2061556Srgrimes	if (exparg.list) {
2071556Srgrimes		*arglist->lastp = exparg.list;
2081556Srgrimes		arglist->lastp = exparg.lastp;
2091556Srgrimes	}
2101556Srgrimes}
2111556Srgrimes
2121556Srgrimes
2131556Srgrimes
2141556Srgrimes/*
215212243Sjilles * Perform parameter expansion, command substitution and arithmetic
216212243Sjilles * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE.
217212243Sjilles * Processing ends at a CTLENDVAR character as well as '\0'.
218212243Sjilles * This is used to expand word in ${var+word} etc.
219212243Sjilles * If EXP_FULL, EXP_CASE or EXP_REDIR are set, keep and/or generate CTLESC
220212243Sjilles * characters to allow for further processing.
221212243Sjilles * If EXP_FULL is set, also preserve CTLQUOTEMARK characters.
2221556Srgrimes */
223213811Sobrienstatic void
22490111Simpargstr(char *p, int flag)
22517987Speter{
22625233Ssteve	char c;
227104672Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);	/* do CTLESC */
2281556Srgrimes	int firsteq = 1;
229214512Sjilles	int split_lit;
230214512Sjilles	int lit_quoted;
2311556Srgrimes
232214512Sjilles	split_lit = flag & EXP_SPLIT_LIT;
233214512Sjilles	lit_quoted = flag & EXP_LIT_QUOTED;
234214512Sjilles	flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED);
2351556Srgrimes	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
2361556Srgrimes		p = exptilde(p, flag);
2371556Srgrimes	for (;;) {
238215783Sjilles		CHECKSTRSPACE(2, expdest);
2391556Srgrimes		switch (c = *p++) {
2401556Srgrimes		case '\0':
241212243Sjilles		case CTLENDVAR:
2421556Srgrimes			goto breakloop;
24338887Stegge		case CTLQUOTEMARK:
244214512Sjilles			lit_quoted = 1;
24538887Stegge			/* "$@" syntax adherence hack */
24638887Stegge			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
24738887Stegge				break;
24839137Stegge			if ((flag & EXP_FULL) != 0)
249215783Sjilles				USTPUTC(c, expdest);
25038887Stegge			break;
251214512Sjilles		case CTLQUOTEEND:
252214512Sjilles			lit_quoted = 0;
253214512Sjilles			break;
2541556Srgrimes		case CTLESC:
2551556Srgrimes			if (quotes)
256215783Sjilles				USTPUTC(c, expdest);
2571556Srgrimes			c = *p++;
258215783Sjilles			USTPUTC(c, expdest);
259214512Sjilles			if (split_lit && !lit_quoted)
260214512Sjilles				recordregion(expdest - stackblock() -
261214512Sjilles				    (quotes ? 2 : 1),
262214512Sjilles				    expdest - stackblock(), 0);
2631556Srgrimes			break;
2641556Srgrimes		case CTLVAR:
2651556Srgrimes			p = evalvar(p, flag);
2661556Srgrimes			break;
2671556Srgrimes		case CTLBACKQ:
2681556Srgrimes		case CTLBACKQ|CTLQUOTE:
2691556Srgrimes			expbackq(argbackq->n, c & CTLQUOTE, flag);
2701556Srgrimes			argbackq = argbackq->next;
2711556Srgrimes			break;
2721556Srgrimes		case CTLENDARI:
2731556Srgrimes			expari(flag);
2741556Srgrimes			break;
2751556Srgrimes		case ':':
2761556Srgrimes		case '=':
2771556Srgrimes			/*
2781556Srgrimes			 * sort of a hack - expand tildes in variable
2791556Srgrimes			 * assignments (after the first '=' and after ':'s).
2801556Srgrimes			 */
281215783Sjilles			USTPUTC(c, expdest);
282214512Sjilles			if (split_lit && !lit_quoted)
283214512Sjilles				recordregion(expdest - stackblock() - 1,
284214512Sjilles				    expdest - stackblock(), 0);
285214512Sjilles			if (flag & EXP_VARTILDE && *p == '~' &&
286214512Sjilles			    (c != '=' || firsteq)) {
287214512Sjilles				if (c == '=')
288214512Sjilles					firsteq = 0;
2891556Srgrimes				p = exptilde(p, flag);
2901556Srgrimes			}
2911556Srgrimes			break;
2921556Srgrimes		default:
293215783Sjilles			USTPUTC(c, expdest);
294214512Sjilles			if (split_lit && !lit_quoted)
295214512Sjilles				recordregion(expdest - stackblock() - 1,
296214512Sjilles				    expdest - stackblock(), 0);
2971556Srgrimes		}
2981556Srgrimes	}
2991556Srgrimesbreakloop:;
3001556Srgrimes}
3011556Srgrimes
302212243Sjilles/*
303212243Sjilles * Perform tilde expansion, placing the result in the stack string and
304212243Sjilles * returning the next position in the input string to process.
305212243Sjilles */
306213811Sobrienstatic char *
30790111Simpexptilde(char *p, int flag)
30817987Speter{
3091556Srgrimes	char c, *startp = p;
3101556Srgrimes	struct passwd *pw;
3111556Srgrimes	char *home;
312108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
3131556Srgrimes
31417987Speter	while ((c = *p) != '\0') {
3151556Srgrimes		switch(c) {
316200988Sjilles		case CTLESC: /* This means CTL* are always considered quoted. */
317200988Sjilles		case CTLVAR:
318200988Sjilles		case CTLBACKQ:
319200988Sjilles		case CTLBACKQ | CTLQUOTE:
320200988Sjilles		case CTLARI:
321200988Sjilles		case CTLENDARI:
32239137Stegge		case CTLQUOTEMARK:
32339137Stegge			return (startp);
3241556Srgrimes		case ':':
3251556Srgrimes			if (flag & EXP_VARTILDE)
3261556Srgrimes				goto done;
3271556Srgrimes			break;
3281556Srgrimes		case '/':
329206150Sjilles		case CTLENDVAR:
3301556Srgrimes			goto done;
3311556Srgrimes		}
3321556Srgrimes		p++;
3331556Srgrimes	}
3341556Srgrimesdone:
3351556Srgrimes	*p = '\0';
3361556Srgrimes	if (*(startp+1) == '\0') {
3371556Srgrimes		if ((home = lookupvar("HOME")) == NULL)
3381556Srgrimes			goto lose;
3391556Srgrimes	} else {
3401556Srgrimes		if ((pw = getpwnam(startp+1)) == NULL)
3411556Srgrimes			goto lose;
3421556Srgrimes		home = pw->pw_dir;
3431556Srgrimes	}
3441556Srgrimes	if (*home == '\0')
3451556Srgrimes		goto lose;
3461556Srgrimes	*p = c;
347216384Sjilles	if (quotes)
348216384Sjilles		STPUTS_QUOTES(home, SQSYNTAX, expdest);
349216384Sjilles	else
350216384Sjilles		STPUTS(home, expdest);
3511556Srgrimes	return (p);
3521556Srgrimeslose:
3531556Srgrimes	*p = c;
3541556Srgrimes	return (startp);
3551556Srgrimes}
3561556Srgrimes
3571556Srgrimes
358213811Sobrienstatic void
35990111Simpremoverecordregions(int endoff)
36038887Stegge{
36138887Stegge	if (ifslastp == NULL)
36238887Stegge		return;
36338887Stegge
36438887Stegge	if (ifsfirst.endoff > endoff) {
36538887Stegge		while (ifsfirst.next != NULL) {
36638887Stegge			struct ifsregion *ifsp;
36738887Stegge			INTOFF;
36838887Stegge			ifsp = ifsfirst.next->next;
36938887Stegge			ckfree(ifsfirst.next);
37038887Stegge			ifsfirst.next = ifsp;
37138887Stegge			INTON;
37238887Stegge		}
37338887Stegge		if (ifsfirst.begoff > endoff)
37438887Stegge			ifslastp = NULL;
37538887Stegge		else {
37638887Stegge			ifslastp = &ifsfirst;
37738887Stegge			ifsfirst.endoff = endoff;
37838887Stegge		}
37938887Stegge		return;
38038887Stegge	}
381155301Sschweikh
38238887Stegge	ifslastp = &ifsfirst;
38338887Stegge	while (ifslastp->next && ifslastp->next->begoff < endoff)
38438887Stegge		ifslastp=ifslastp->next;
38538887Stegge	while (ifslastp->next != NULL) {
38638887Stegge		struct ifsregion *ifsp;
38738887Stegge		INTOFF;
38838887Stegge		ifsp = ifslastp->next->next;
38938887Stegge		ckfree(ifslastp->next);
39038887Stegge		ifslastp->next = ifsp;
39138887Stegge		INTON;
39238887Stegge	}
39338887Stegge	if (ifslastp->endoff > endoff)
39438887Stegge		ifslastp->endoff = endoff;
39538887Stegge}
39638887Stegge
3971556Srgrimes/*
3981556Srgrimes * Expand arithmetic expression.  Backup to start of expression,
3991556Srgrimes * evaluate, place result in (backed up) result, adjust string position.
4001556Srgrimes */
4011556Srgrimesvoid
40290111Simpexpari(int flag)
40317987Speter{
404207206Sjilles	char *p, *q, *start;
405178631Sstefanf	arith_t result;
40638887Stegge	int begoff;
407108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
40838887Stegge	int quoted;
4091556Srgrimes
4101556Srgrimes	/*
41146684Skris	 * This routine is slightly over-complicated for
4121556Srgrimes	 * efficiency.  First we make sure there is
4131556Srgrimes	 * enough space for the result, which may be bigger
414212243Sjilles	 * than the expression.  Next we
4151556Srgrimes	 * scan backwards looking for the start of arithmetic.  If the
4161556Srgrimes	 * next previous character is a CTLESC character, then we
4171556Srgrimes	 * have to rescan starting from the beginning since CTLESC
4188855Srgrimes	 * characters have to be processed left to right.
4191556Srgrimes	 */
420178631Sstefanf	CHECKSTRSPACE(DIGITS(result) - 2, expdest);
4218855Srgrimes	USTPUTC('\0', expdest);
4221556Srgrimes	start = stackblock();
42383676Stegge	p = expdest - 2;
42483676Stegge	while (p >= start && *p != CTLARI)
4251556Srgrimes		--p;
42683676Stegge	if (p < start || *p != CTLARI)
4271556Srgrimes		error("missing CTLARI (shouldn't happen)");
42883676Stegge	if (p > start && *(p - 1) == CTLESC)
4291556Srgrimes		for (p = start; *p != CTLARI; p++)
4301556Srgrimes			if (*p == CTLESC)
4311556Srgrimes				p++;
43238887Stegge
43338887Stegge	if (p[1] == '"')
43438887Stegge		quoted=1;
43538887Stegge	else
43638887Stegge		quoted=0;
43738887Stegge	begoff = p - start;
43838887Stegge	removerecordregions(begoff);
4391556Srgrimes	if (quotes)
44038887Stegge		rmescapes(p+2);
441207206Sjilles	q = grabstackstr(expdest);
44238887Stegge	result = arith(p+2);
443207206Sjilles	ungrabstackstr(q, expdest);
444178631Sstefanf	fmtstr(p, DIGITS(result), ARITH_FORMAT_STR, result);
4451556Srgrimes	while (*p++)
4461556Srgrimes		;
44738887Stegge	if (quoted == 0)
44838887Stegge		recordregion(begoff, p - 1 - start, 0);
4491556Srgrimes	result = expdest - p + 1;
4501556Srgrimes	STADJUST(-result, expdest);
4511556Srgrimes}
4521556Srgrimes
4531556Srgrimes
4541556Srgrimes/*
455212243Sjilles * Perform command substitution.
4561556Srgrimes */
457213811Sobrienstatic void
45890111Simpexpbackq(union node *cmd, int quoted, int flag)
45917987Speter{
4601556Srgrimes	struct backcmd in;
4611556Srgrimes	int i;
4621556Srgrimes	char buf[128];
4631556Srgrimes	char *p;
4641556Srgrimes	char *dest = expdest;
4651556Srgrimes	struct ifsregion saveifs, *savelastp;
4661556Srgrimes	struct nodelist *saveargbackq;
4671556Srgrimes	char lastc;
4681556Srgrimes	int startloc = dest - stackblock();
4691556Srgrimes	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
470108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
471115424Sfenner	int nnl;
4721556Srgrimes
4731556Srgrimes	INTOFF;
4741556Srgrimes	saveifs = ifsfirst;
4751556Srgrimes	savelastp = ifslastp;
4761556Srgrimes	saveargbackq = argbackq;
4771556Srgrimes	p = grabstackstr(dest);
4781556Srgrimes	evalbackcmd(cmd, &in);
4791556Srgrimes	ungrabstackstr(p, dest);
4801556Srgrimes	ifsfirst = saveifs;
4811556Srgrimes	ifslastp = savelastp;
4821556Srgrimes	argbackq = saveargbackq;
4831556Srgrimes
4841556Srgrimes	p = in.buf;
4851556Srgrimes	lastc = '\0';
486115424Sfenner	nnl = 0;
487115424Sfenner	/* Don't copy trailing newlines */
4881556Srgrimes	for (;;) {
4891556Srgrimes		if (--in.nleft < 0) {
4901556Srgrimes			if (in.fd < 0)
4911556Srgrimes				break;
4921556Srgrimes			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
4931556Srgrimes			TRACE(("expbackq: read returns %d\n", i));
4941556Srgrimes			if (i <= 0)
4951556Srgrimes				break;
4961556Srgrimes			p = buf;
4971556Srgrimes			in.nleft = i - 1;
4981556Srgrimes		}
4991556Srgrimes		lastc = *p++;
5001556Srgrimes		if (lastc != '\0') {
501115424Sfenner			if (lastc == '\n') {
502115424Sfenner				nnl++;
503115424Sfenner			} else {
504216706Sjilles				CHECKSTRSPACE(nnl + 2, dest);
505115424Sfenner				while (nnl > 0) {
506115424Sfenner					nnl--;
507216706Sjilles					USTPUTC('\n', dest);
508115424Sfenner				}
509216496Sjilles				if (quotes && syntax[(int)lastc] == CCTL)
510216706Sjilles					USTPUTC(CTLESC, dest);
511216706Sjilles				USTPUTC(lastc, dest);
512115424Sfenner			}
5131556Srgrimes		}
5141556Srgrimes	}
51517987Speter
5161556Srgrimes	if (in.fd >= 0)
5171556Srgrimes		close(in.fd);
5181556Srgrimes	if (in.buf)
5191556Srgrimes		ckfree(in.buf);
5201556Srgrimes	if (in.jp)
52145916Scracauer		exitstatus = waitforjob(in.jp, (int *)NULL);
5221556Srgrimes	if (quoted == 0)
5231556Srgrimes		recordregion(startloc, dest - stackblock(), 0);
524213775Sjhb	TRACE(("expbackq: size=%td: \"%.*s\"\n",
525213775Sjhb		((dest - stackblock()) - startloc),
526213775Sjhb		(int)((dest - stackblock()) - startloc),
5271556Srgrimes		stackblock() + startloc));
5281556Srgrimes	expdest = dest;
5291556Srgrimes	INTON;
5301556Srgrimes}
5311556Srgrimes
5321556Srgrimes
5331556Srgrimes
534213811Sobrienstatic int
53590111Simpsubevalvar(char *p, char *str, int strloc, int subtype, int startloc,
536214524Sjilles  int varflags, int quotes)
53717987Speter{
53817987Speter	char *startp;
53917987Speter	char *loc = NULL;
54045514Stegge	char *q;
54117987Speter	int c = 0;
54217987Speter	struct nodelist *saveargbackq = argbackq;
54320425Ssteve	int amount;
54420425Ssteve
545206150Sjilles	argstr(p, (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX ||
546206147Sjilles	    subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX ?
547206150Sjilles	    EXP_CASE : 0) | EXP_TILDE);
54817987Speter	STACKSTRNUL(expdest);
54917987Speter	argbackq = saveargbackq;
55017987Speter	startp = stackblock() + startloc;
55120425Ssteve	if (str == NULL)
55220425Ssteve	    str = stackblock() + strloc;
55317987Speter
55417987Speter	switch (subtype) {
55517987Speter	case VSASSIGN:
55617987Speter		setvar(str, startp, 0);
55720425Ssteve		amount = startp - expdest;
55820425Ssteve		STADJUST(amount, expdest);
55917987Speter		varflags &= ~VSNUL;
56017987Speter		return 1;
56117987Speter
56217987Speter	case VSQUESTION:
56317987Speter		if (*p != CTLENDVAR) {
564201366Sjilles			outfmt(out2, "%s\n", startp);
56517987Speter			error((char *)NULL);
56617987Speter		}
567112254Sru		error("%.*s: parameter %snot set", (int)(p - str - 1),
56817987Speter		      str, (varflags & VSNUL) ? "null or "
56917987Speter					      : nullstr);
57017987Speter		return 0;
57117987Speter
57217987Speter	case VSTRIMLEFT:
57325233Ssteve		for (loc = startp; loc < str; loc++) {
57417987Speter			c = *loc;
57517987Speter			*loc = '\0';
576214524Sjilles			if (patmatch(str, startp, quotes)) {
57717987Speter				*loc = c;
57817987Speter				goto recordleft;
57917987Speter			}
58017987Speter			*loc = c;
581214524Sjilles			if (quotes && *loc == CTLESC)
58245514Stegge				loc++;
58317987Speter		}
58417987Speter		return 0;
58517987Speter
58617987Speter	case VSTRIMLEFTMAX:
58745514Stegge		for (loc = str - 1; loc >= startp;) {
58817987Speter			c = *loc;
58917987Speter			*loc = '\0';
590214524Sjilles			if (patmatch(str, startp, quotes)) {
59117987Speter				*loc = c;
59217987Speter				goto recordleft;
59317987Speter			}
59417987Speter			*loc = c;
59545514Stegge			loc--;
596214524Sjilles			if (quotes && loc > startp && *(loc - 1) == CTLESC) {
59745514Stegge				for (q = startp; q < loc; q++)
59845514Stegge					if (*q == CTLESC)
59945514Stegge						q++;
60045514Stegge				if (q > loc)
60145514Stegge					loc--;
60245514Stegge			}
60317987Speter		}
60417987Speter		return 0;
60517987Speter
60617987Speter	case VSTRIMRIGHT:
60745514Stegge		for (loc = str - 1; loc >= startp;) {
608214524Sjilles			if (patmatch(str, loc, quotes)) {
60920425Ssteve				amount = loc - expdest;
61020425Ssteve				STADJUST(amount, expdest);
61117987Speter				return 1;
61217987Speter			}
61345514Stegge			loc--;
614214524Sjilles			if (quotes && loc > startp && *(loc - 1) == CTLESC) {
61545514Stegge				for (q = startp; q < loc; q++)
61645514Stegge					if (*q == CTLESC)
61745514Stegge						q++;
61845514Stegge				if (q > loc)
61945514Stegge					loc--;
62045514Stegge			}
62117987Speter		}
62217987Speter		return 0;
62317987Speter
62417987Speter	case VSTRIMRIGHTMAX:
62517987Speter		for (loc = startp; loc < str - 1; loc++) {
626214524Sjilles			if (patmatch(str, loc, quotes)) {
62720425Ssteve				amount = loc - expdest;
62820425Ssteve				STADJUST(amount, expdest);
62917987Speter				return 1;
63017987Speter			}
631214524Sjilles			if (quotes && *loc == CTLESC)
63245514Stegge				loc++;
63317987Speter		}
63417987Speter		return 0;
63517987Speter
63617987Speter
63717987Speter	default:
63817987Speter		abort();
63917987Speter	}
64017987Speter
64117987Speterrecordleft:
64220425Ssteve	amount = ((str - 1) - (loc - startp)) - expdest;
64320425Ssteve	STADJUST(amount, expdest);
64417987Speter	while (loc != str - 1)
64517987Speter		*startp++ = *loc++;
64617987Speter	return 1;
64717987Speter}
64817987Speter
64917987Speter
6501556Srgrimes/*
6511556Srgrimes * Expand a variable, and return a pointer to the next character in the
6521556Srgrimes * input string.
6531556Srgrimes */
6541556Srgrimes
655213811Sobrienstatic char *
65690111Simpevalvar(char *p, int flag)
65717987Speter{
6581556Srgrimes	int subtype;
6591556Srgrimes	int varflags;
6601556Srgrimes	char *var;
6611556Srgrimes	char *val;
66245644Stegge	int patloc;
6631556Srgrimes	int c;
6641556Srgrimes	int set;
6651556Srgrimes	int special;
6661556Srgrimes	int startloc;
66717987Speter	int varlen;
66817987Speter	int easy;
669108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
6701556Srgrimes
671164081Sstefanf	varflags = (unsigned char)*p++;
6721556Srgrimes	subtype = varflags & VSTYPE;
6731556Srgrimes	var = p;
6741556Srgrimes	special = 0;
6751556Srgrimes	if (! is_name(*p))
6761556Srgrimes		special = 1;
6771556Srgrimes	p = strchr(p, '=') + 1;
6781556Srgrimesagain: /* jump here after setting a variable with ${var=text} */
679179022Sstefanf	if (varflags & VSLINENO) {
680179022Sstefanf		set = 1;
681179022Sstefanf		special = 0;
682179022Sstefanf		val = var;
683179022Sstefanf		p[-1] = '\0';	/* temporarily overwrite '=' to have \0
684179022Sstefanf				   terminated string */
685179022Sstefanf	} else if (special) {
68625233Ssteve		set = varisset(var, varflags & VSNUL);
6871556Srgrimes		val = NULL;
6881556Srgrimes	} else {
68960592Scracauer		val = bltinlookup(var, 1);
69017987Speter		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
6911556Srgrimes			val = NULL;
6921556Srgrimes			set = 0;
6931556Srgrimes		} else
6941556Srgrimes			set = 1;
6951556Srgrimes	}
69617987Speter	varlen = 0;
6971556Srgrimes	startloc = expdest - stackblock();
698198454Sjilles	if (!set && uflag && *var != '@' && *var != '*') {
69996939Stjr		switch (subtype) {
70096939Stjr		case VSNORMAL:
70196939Stjr		case VSTRIMLEFT:
70296939Stjr		case VSTRIMLEFTMAX:
70396939Stjr		case VSTRIMRIGHT:
70496939Stjr		case VSTRIMRIGHTMAX:
70596939Stjr		case VSLENGTH:
706112254Sru			error("%.*s: parameter not set", (int)(p - var - 1),
707112254Sru			    var);
70896939Stjr		}
70996939Stjr	}
7101556Srgrimes	if (set && subtype != VSPLUS) {
7111556Srgrimes		/* insert the value of the variable */
7121556Srgrimes		if (special) {
713164081Sstefanf			varvalue(var, varflags & VSQUOTE, subtype, flag);
71417987Speter			if (subtype == VSLENGTH) {
71525233Ssteve				varlen = expdest - stackblock() - startloc;
71625233Ssteve				STADJUST(-varlen, expdest);
71717987Speter			}
7181556Srgrimes		} else {
71920425Ssteve			char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
72017987Speter								  : BASESYNTAX;
7211556Srgrimes
72217987Speter			if (subtype == VSLENGTH) {
72317987Speter				for (;*val; val++)
72417987Speter					varlen++;
7251556Srgrimes			}
72617987Speter			else {
727216384Sjilles				if (quotes)
728216384Sjilles					STPUTS_QUOTES(val, syntax, expdest);
729216384Sjilles				else
730216384Sjilles					STPUTS(val, expdest);
73117987Speter
73217987Speter			}
7331556Srgrimes		}
7341556Srgrimes	}
73520425Ssteve
7361556Srgrimes	if (subtype == VSPLUS)
7371556Srgrimes		set = ! set;
73817987Speter
73920425Ssteve	easy = ((varflags & VSQUOTE) == 0 ||
74017987Speter		(*var == '@' && shellparam.nparam != 1));
74117987Speter
74217987Speter
74317987Speter	switch (subtype) {
74417987Speter	case VSLENGTH:
74517987Speter		expdest = cvtnum(varlen, expdest);
74617987Speter		goto record;
74717987Speter
74817987Speter	case VSNORMAL:
74917987Speter		if (!easy)
75017987Speter			break;
75117987Speterrecord:
75220425Ssteve		recordregion(startloc, expdest - stackblock(),
75317987Speter			     varflags & VSQUOTE);
75417987Speter		break;
75517987Speter
75617987Speter	case VSPLUS:
75717987Speter	case VSMINUS:
75817987Speter		if (!set) {
759214512Sjilles			argstr(p, flag | (flag & EXP_FULL ? EXP_SPLIT_LIT : 0) |
760214512Sjilles			    (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0));
76117987Speter			break;
76217987Speter		}
76317987Speter		if (easy)
76417987Speter			goto record;
76517987Speter		break;
76617987Speter
76717987Speter	case VSTRIMLEFT:
76817987Speter	case VSTRIMLEFTMAX:
76917987Speter	case VSTRIMRIGHT:
77017987Speter	case VSTRIMRIGHTMAX:
77117987Speter		if (!set)
77217987Speter			break;
77317987Speter		/*
77417987Speter		 * Terminate the string and start recording the pattern
77517987Speter		 * right after it
77617987Speter		 */
77717987Speter		STPUTC('\0', expdest);
77845644Stegge		patloc = expdest - stackblock();
77945644Stegge		if (subevalvar(p, NULL, patloc, subtype,
780214524Sjilles		    startloc, varflags, quotes) == 0) {
78145644Stegge			int amount = (expdest - stackblock() - patloc) + 1;
78225233Ssteve			STADJUST(-amount, expdest);
78325233Ssteve		}
78438887Stegge		/* Remove any recorded regions beyond start of variable */
78538887Stegge		removerecordregions(startloc);
78638887Stegge		goto record;
78717987Speter
78817987Speter	case VSASSIGN:
78917987Speter	case VSQUESTION:
79017987Speter		if (!set) {
791214524Sjilles			if (subevalvar(p, var, 0, subtype, startloc, varflags,
792214524Sjilles			    quotes)) {
79320425Ssteve				varflags &= ~VSNUL;
794155301Sschweikh				/*
795155301Sschweikh				 * Remove any recorded regions beyond
796155301Sschweikh				 * start of variable
79738887Stegge				 */
79838887Stegge				removerecordregions(startloc);
7991556Srgrimes				goto again;
80020425Ssteve			}
80117987Speter			break;
8021556Srgrimes		}
80317987Speter		if (easy)
80417987Speter			goto record;
80517987Speter		break;
80617987Speter
807164003Sstefanf	case VSERROR:
808164003Sstefanf		c = p - var - 1;
809164003Sstefanf		error("${%.*s%s}: Bad substitution", c, var,
810164003Sstefanf		    (c > 0 && *p != CTLENDVAR) ? "..." : "");
811164003Sstefanf
81217987Speter	default:
81317987Speter		abort();
8141556Srgrimes	}
815179022Sstefanf	p[-1] = '=';	/* recover overwritten '=' */
81617987Speter
8171556Srgrimes	if (subtype != VSNORMAL) {	/* skip to end of alternative */
8181556Srgrimes		int nesting = 1;
8191556Srgrimes		for (;;) {
8201556Srgrimes			if ((c = *p++) == CTLESC)
8211556Srgrimes				p++;
8221556Srgrimes			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
8231556Srgrimes				if (set)
8241556Srgrimes					argbackq = argbackq->next;
8251556Srgrimes			} else if (c == CTLVAR) {
8261556Srgrimes				if ((*p++ & VSTYPE) != VSNORMAL)
8271556Srgrimes					nesting++;
8281556Srgrimes			} else if (c == CTLENDVAR) {
8291556Srgrimes				if (--nesting == 0)
8301556Srgrimes					break;
8311556Srgrimes			}
8321556Srgrimes		}
8331556Srgrimes	}
8341556Srgrimes	return p;
8351556Srgrimes}
8361556Srgrimes
8371556Srgrimes
8381556Srgrimes
8391556Srgrimes/*
8401556Srgrimes * Test whether a specialized variable is set.
8411556Srgrimes */
8421556Srgrimes
843213811Sobrienstatic int
84490111Simpvarisset(char *name, int nulok)
84525233Ssteve{
8461556Srgrimes
84725233Ssteve	if (*name == '!')
848209600Sjilles		return backgndpidset();
84925233Ssteve	else if (*name == '@' || *name == '*') {
8501556Srgrimes		if (*shellparam.p == NULL)
8511556Srgrimes			return 0;
85225233Ssteve
85325233Ssteve		if (nulok) {
85425233Ssteve			char **av;
85525233Ssteve
85625233Ssteve			for (av = shellparam.p; *av; av++)
85725233Ssteve				if (**av != '\0')
85825233Ssteve					return 1;
85925233Ssteve			return 0;
86025233Ssteve		}
86118202Speter	} else if (is_digit(*name)) {
86225233Ssteve		char *ap;
86320425Ssteve		int num = atoi(name);
86425233Ssteve
86525233Ssteve		if (num > shellparam.nparam)
86625233Ssteve			return 0;
86725233Ssteve
86825233Ssteve		if (num == 0)
86925233Ssteve			ap = arg0;
87025233Ssteve		else
87125233Ssteve			ap = shellparam.p[num - 1];
87225233Ssteve
87325233Ssteve		if (nulok && (ap == NULL || *ap == '\0'))
87425233Ssteve			return 0;
8751556Srgrimes	}
8761556Srgrimes	return 1;
8771556Srgrimes}
8781556Srgrimes
879216384Sjillesstatic void
880216384Sjillesstrtodest(const char *p, int flag, int subtype, int quoted)
881216384Sjilles{
882216384Sjilles	if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH)
883216384Sjilles		STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest);
884216384Sjilles	else
885216384Sjilles		STPUTS(p, expdest);
886216384Sjilles}
8871556Srgrimes
8881556Srgrimes/*
8891556Srgrimes * Add the value of a specialized variable to the stack string.
8901556Srgrimes */
8911556Srgrimes
892213811Sobrienstatic void
893164081Sstefanfvarvalue(char *name, int quoted, int subtype, int flag)
89417987Speter{
8951556Srgrimes	int num;
8961556Srgrimes	char *p;
8971556Srgrimes	int i;
8981556Srgrimes	char sep;
8991556Srgrimes	char **ap;
9001556Srgrimes
90118202Speter	switch (*name) {
9021556Srgrimes	case '$':
9031556Srgrimes		num = rootpid;
9041556Srgrimes		goto numvar;
9051556Srgrimes	case '?':
90617987Speter		num = oexitstatus;
9071556Srgrimes		goto numvar;
9081556Srgrimes	case '#':
9091556Srgrimes		num = shellparam.nparam;
9101556Srgrimes		goto numvar;
9111556Srgrimes	case '!':
912209600Sjilles		num = backgndpidval();
9131556Srgrimesnumvar:
91417987Speter		expdest = cvtnum(num, expdest);
9151556Srgrimes		break;
9161556Srgrimes	case '-':
9171556Srgrimes		for (i = 0 ; i < NOPTS ; i++) {
9181556Srgrimes			if (optlist[i].val)
9191556Srgrimes				STPUTC(optlist[i].letter, expdest);
9201556Srgrimes		}
9211556Srgrimes		break;
9221556Srgrimes	case '@':
923164081Sstefanf		if (flag & EXP_FULL && quoted) {
92438887Stegge			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
925216384Sjilles				strtodest(p, flag, subtype, quoted);
92638887Stegge				if (*ap)
92738887Stegge					STPUTC('\0', expdest);
92838887Stegge			}
92938887Stegge			break;
9301556Srgrimes		}
931102410Scharnier		/* FALLTHROUGH */
9321556Srgrimes	case '*':
933149825Srse		if (ifsset())
93438887Stegge			sep = ifsval()[0];
93538887Stegge		else
93638887Stegge			sep = ' ';
9371556Srgrimes		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
938216384Sjilles			strtodest(p, flag, subtype, quoted);
93938887Stegge			if (*ap && sep)
9401556Srgrimes				STPUTC(sep, expdest);
9411556Srgrimes		}
9421556Srgrimes		break;
9431556Srgrimes	case '0':
9441556Srgrimes		p = arg0;
945216384Sjilles		strtodest(p, flag, subtype, quoted);
9461556Srgrimes		break;
9471556Srgrimes	default:
94818202Speter		if (is_digit(*name)) {
94918202Speter			num = atoi(name);
95018202Speter			if (num > 0 && num <= shellparam.nparam) {
95118202Speter				p = shellparam.p[num - 1];
952216384Sjilles				strtodest(p, flag, subtype, quoted);
95318202Speter			}
9541556Srgrimes		}
9551556Srgrimes		break;
9561556Srgrimes	}
9571556Srgrimes}
9581556Srgrimes
9591556Srgrimes
9601556Srgrimes
9611556Srgrimes/*
962218909Sbrucec * Record the fact that we have to scan this region of the
9631556Srgrimes * string for IFS characters.
9641556Srgrimes */
9651556Srgrimes
966213811Sobrienstatic void
967194975Sjillesrecordregion(int start, int end, int inquotes)
96817987Speter{
96925233Ssteve	struct ifsregion *ifsp;
9701556Srgrimes
9711556Srgrimes	if (ifslastp == NULL) {
9721556Srgrimes		ifsp = &ifsfirst;
9731556Srgrimes	} else {
974194975Sjilles		if (ifslastp->endoff == start
975194975Sjilles		    && ifslastp->inquotes == inquotes) {
976194975Sjilles			/* extend previous area */
977194975Sjilles			ifslastp->endoff = end;
978194975Sjilles			return;
979194975Sjilles		}
9801556Srgrimes		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
9811556Srgrimes		ifslastp->next = ifsp;
9821556Srgrimes	}
9831556Srgrimes	ifslastp = ifsp;
9841556Srgrimes	ifslastp->next = NULL;
9851556Srgrimes	ifslastp->begoff = start;
9861556Srgrimes	ifslastp->endoff = end;
987194975Sjilles	ifslastp->inquotes = inquotes;
9881556Srgrimes}
9891556Srgrimes
9901556Srgrimes
9911556Srgrimes
9921556Srgrimes/*
9931556Srgrimes * Break the argument string into pieces based upon IFS and add the
9941556Srgrimes * strings to the argument list.  The regions of the string to be
9951556Srgrimes * searched for IFS characters have been stored by recordregion.
996212243Sjilles * CTLESC characters are preserved but have little effect in this pass
997212243Sjilles * other than escaping CTL* characters.  In particular, they do not escape
998212243Sjilles * IFS characters: that should be done with the ifsregion mechanism.
999212243Sjilles * CTLQUOTEMARK characters are used to preserve empty quoted strings.
1000212243Sjilles * This pass treats them as a regular character, making the string non-empty.
1001212243Sjilles * Later, they are removed along with the other CTL* characters.
10021556Srgrimes */
1003213811Sobrienstatic void
100490111Simpifsbreakup(char *string, struct arglist *arglist)
100590111Simp{
10061556Srgrimes	struct ifsregion *ifsp;
10071556Srgrimes	struct strlist *sp;
10081556Srgrimes	char *start;
100925233Ssteve	char *p;
10101556Srgrimes	char *q;
1011201053Sjilles	const char *ifs;
1012194975Sjilles	const char *ifsspc;
1013194975Sjilles	int had_param_ch = 0;
10141556Srgrimes
1015194975Sjilles	start = string;
101617987Speter
1017194975Sjilles	if (ifslastp == NULL) {
1018194975Sjilles		/* Return entire argument, IFS doesn't apply to any of it */
1019194975Sjilles		sp = (struct strlist *)stalloc(sizeof *sp);
1020194975Sjilles		sp->text = start;
1021194975Sjilles		*arglist->lastp = sp;
1022194975Sjilles		arglist->lastp = &sp->next;
1023194975Sjilles		return;
1024194975Sjilles	}
1025194975Sjilles
1026194975Sjilles	ifs = ifsset() ? ifsval() : " \t\n";
1027194975Sjilles
1028194975Sjilles	for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
1029194975Sjilles		p = string + ifsp->begoff;
1030194975Sjilles		while (p < string + ifsp->endoff) {
1031194975Sjilles			q = p;
1032194975Sjilles			if (*p == CTLESC)
1033194975Sjilles				p++;
1034194975Sjilles			if (ifsp->inquotes) {
1035194975Sjilles				/* Only NULs (should be from "$@") end args */
1036194977Sjilles				had_param_ch = 1;
1037194975Sjilles				if (*p != 0) {
10381556Srgrimes					p++;
1039194975Sjilles					continue;
1040194975Sjilles				}
1041194975Sjilles				ifsspc = NULL;
1042194975Sjilles			} else {
1043194975Sjilles				if (!strchr(ifs, *p)) {
1044194977Sjilles					had_param_ch = 1;
104538887Stegge					p++;
1046194975Sjilles					continue;
1047194975Sjilles				}
1048194975Sjilles				ifsspc = strchr(" \t\n", *p);
1049194975Sjilles
1050194975Sjilles				/* Ignore IFS whitespace at start */
1051194975Sjilles				if (q == start && ifsspc != NULL) {
1052194975Sjilles					p++;
10531556Srgrimes					start = p;
1054194975Sjilles					continue;
1055194975Sjilles				}
1056194977Sjilles				had_param_ch = 0;
10571556Srgrimes			}
1058194975Sjilles
1059194975Sjilles			/* Save this argument... */
1060194975Sjilles			*q = '\0';
10611556Srgrimes			sp = (struct strlist *)stalloc(sizeof *sp);
10621556Srgrimes			sp->text = start;
10631556Srgrimes			*arglist->lastp = sp;
10641556Srgrimes			arglist->lastp = &sp->next;
1065194975Sjilles			p++;
1066194975Sjilles
1067194975Sjilles			if (ifsspc != NULL) {
1068194975Sjilles				/* Ignore further trailing IFS whitespace */
1069194975Sjilles				for (; p < string + ifsp->endoff; p++) {
1070194975Sjilles					q = p;
1071194975Sjilles					if (*p == CTLESC)
1072194975Sjilles						p++;
1073194975Sjilles					if (strchr(ifs, *p) == NULL) {
1074194975Sjilles						p = q;
1075194975Sjilles						break;
1076194975Sjilles					}
1077194975Sjilles					if (strchr(" \t\n", *p) == NULL) {
1078194975Sjilles						p++;
1079194975Sjilles						break;
1080194975Sjilles					}
1081194975Sjilles				}
1082194975Sjilles			}
1083194975Sjilles			start = p;
10841556Srgrimes		}
1085194975Sjilles	}
1086194975Sjilles
1087194975Sjilles	/*
1088194975Sjilles	 * Save anything left as an argument.
1089194975Sjilles	 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
1090194975Sjilles	 * generating 2 arguments, the second of which is empty.
1091194975Sjilles	 * Some recent clarification of the Posix spec say that it
1092194975Sjilles	 * should only generate one....
1093194975Sjilles	 */
1094194975Sjilles	if (had_param_ch || *start != 0) {
10951556Srgrimes		sp = (struct strlist *)stalloc(sizeof *sp);
10961556Srgrimes		sp->text = start;
10971556Srgrimes		*arglist->lastp = sp;
10981556Srgrimes		arglist->lastp = &sp->next;
10991556Srgrimes	}
11001556Srgrimes}
11011556Srgrimes
11021556Srgrimes
1103213760Sobrienstatic char expdir[PATH_MAX];
1104212243Sjilles#define expdir_end (expdir + sizeof(expdir))
11051556Srgrimes
11061556Srgrimes/*
1107212243Sjilles * Perform pathname generation and remove control characters.
1108212243Sjilles * At this point, the only control characters should be CTLESC and CTLQUOTEMARK.
1109212243Sjilles * The results are stored in the list exparg.
11101556Srgrimes */
1111213811Sobrienstatic void
111290111Simpexpandmeta(struct strlist *str, int flag __unused)
111317987Speter{
11141556Srgrimes	char *p;
11151556Srgrimes	struct strlist **savelastp;
11161556Srgrimes	struct strlist *sp;
11171556Srgrimes	char c;
11181556Srgrimes	/* TODO - EXP_REDIR */
11191556Srgrimes
11201556Srgrimes	while (str) {
11211556Srgrimes		if (fflag)
11221556Srgrimes			goto nometa;
11231556Srgrimes		p = str->text;
11241556Srgrimes		for (;;) {			/* fast check for meta chars */
11251556Srgrimes			if ((c = *p++) == '\0')
11261556Srgrimes				goto nometa;
1127211646Sjilles			if (c == '*' || c == '?' || c == '[')
11281556Srgrimes				break;
11291556Srgrimes		}
11301556Srgrimes		savelastp = exparg.lastp;
11311556Srgrimes		INTOFF;
11321556Srgrimes		expmeta(expdir, str->text);
11331556Srgrimes		INTON;
11341556Srgrimes		if (exparg.lastp == savelastp) {
11358855Srgrimes			/*
11368855Srgrimes			 * no matches
11371556Srgrimes			 */
11381556Srgrimesnometa:
11391556Srgrimes			*exparg.lastp = str;
11401556Srgrimes			rmescapes(str->text);
11411556Srgrimes			exparg.lastp = &str->next;
11421556Srgrimes		} else {
11431556Srgrimes			*exparg.lastp = NULL;
11441556Srgrimes			*savelastp = sp = expsort(*savelastp);
11451556Srgrimes			while (sp->next != NULL)
11461556Srgrimes				sp = sp->next;
11471556Srgrimes			exparg.lastp = &sp->next;
11481556Srgrimes		}
11491556Srgrimes		str = str->next;
11501556Srgrimes	}
11511556Srgrimes}
11521556Srgrimes
11531556Srgrimes
11541556Srgrimes/*
11551556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion.
11561556Srgrimes */
11571556Srgrimes
1158213811Sobrienstatic void
115990111Simpexpmeta(char *enddir, char *name)
116090111Simp{
116125233Ssteve	char *p;
11621556Srgrimes	char *q;
11631556Srgrimes	char *start;
11641556Srgrimes	char *endname;
11651556Srgrimes	int metaflag;
11661556Srgrimes	struct stat statb;
11671556Srgrimes	DIR *dirp;
11681556Srgrimes	struct dirent *dp;
11691556Srgrimes	int atend;
11701556Srgrimes	int matchdot;
1171207944Sjilles	int esc;
11721556Srgrimes
11731556Srgrimes	metaflag = 0;
11741556Srgrimes	start = name;
1175207944Sjilles	for (p = name; esc = 0, *p; p += esc + 1) {
11761556Srgrimes		if (*p == '*' || *p == '?')
11771556Srgrimes			metaflag = 1;
11781556Srgrimes		else if (*p == '[') {
11791556Srgrimes			q = p + 1;
118026488Sache			if (*q == '!' || *q == '^')
11811556Srgrimes				q++;
11821556Srgrimes			for (;;) {
118338887Stegge				while (*q == CTLQUOTEMARK)
118438887Stegge					q++;
11851556Srgrimes				if (*q == CTLESC)
11861556Srgrimes					q++;
11871556Srgrimes				if (*q == '/' || *q == '\0')
11881556Srgrimes					break;
11891556Srgrimes				if (*++q == ']') {
11901556Srgrimes					metaflag = 1;
11911556Srgrimes					break;
11921556Srgrimes				}
11931556Srgrimes			}
11941556Srgrimes		} else if (*p == '\0')
11951556Srgrimes			break;
119638887Stegge		else if (*p == CTLQUOTEMARK)
119738887Stegge			continue;
1198207944Sjilles		else {
1199207944Sjilles			if (*p == CTLESC)
1200207944Sjilles				esc++;
1201207944Sjilles			if (p[esc] == '/') {
1202207944Sjilles				if (metaflag)
1203207944Sjilles					break;
1204207944Sjilles				start = p + esc + 1;
1205207944Sjilles			}
12061556Srgrimes		}
12071556Srgrimes	}
12081556Srgrimes	if (metaflag == 0) {	/* we've reached the end of the file name */
12091556Srgrimes		if (enddir != expdir)
12101556Srgrimes			metaflag++;
12111556Srgrimes		for (p = name ; ; p++) {
121238887Stegge			if (*p == CTLQUOTEMARK)
121338887Stegge				continue;
12141556Srgrimes			if (*p == CTLESC)
12151556Srgrimes				p++;
12161556Srgrimes			*enddir++ = *p;
12171556Srgrimes			if (*p == '\0')
12181556Srgrimes				break;
1219211155Sjilles			if (enddir == expdir_end)
1220211155Sjilles				return;
12211556Srgrimes		}
1222147812Sdelphij		if (metaflag == 0 || lstat(expdir, &statb) >= 0)
12231556Srgrimes			addfname(expdir);
12241556Srgrimes		return;
12251556Srgrimes	}
12261556Srgrimes	endname = p;
12271556Srgrimes	if (start != name) {
12281556Srgrimes		p = name;
12291556Srgrimes		while (p < start) {
123038887Stegge			while (*p == CTLQUOTEMARK)
123138887Stegge				p++;
12321556Srgrimes			if (*p == CTLESC)
12331556Srgrimes				p++;
12341556Srgrimes			*enddir++ = *p++;
1235211155Sjilles			if (enddir == expdir_end)
1236211155Sjilles				return;
12371556Srgrimes		}
12381556Srgrimes	}
12391556Srgrimes	if (enddir == expdir) {
12401556Srgrimes		p = ".";
12411556Srgrimes	} else if (enddir == expdir + 1 && *expdir == '/') {
12421556Srgrimes		p = "/";
12431556Srgrimes	} else {
12441556Srgrimes		p = expdir;
12451556Srgrimes		enddir[-1] = '\0';
12461556Srgrimes	}
12471556Srgrimes	if ((dirp = opendir(p)) == NULL)
12481556Srgrimes		return;
12491556Srgrimes	if (enddir != expdir)
12501556Srgrimes		enddir[-1] = '/';
12511556Srgrimes	if (*endname == 0) {
12521556Srgrimes		atend = 1;
12531556Srgrimes	} else {
12541556Srgrimes		atend = 0;
1255207944Sjilles		*endname = '\0';
1256207944Sjilles		endname += esc + 1;
12571556Srgrimes	}
12581556Srgrimes	matchdot = 0;
125938887Stegge	p = start;
126038887Stegge	while (*p == CTLQUOTEMARK)
126138887Stegge		p++;
126238887Stegge	if (*p == CTLESC)
126338887Stegge		p++;
126438887Stegge	if (*p == '.')
12651556Srgrimes		matchdot++;
12661556Srgrimes	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
12671556Srgrimes		if (dp->d_name[0] == '.' && ! matchdot)
12681556Srgrimes			continue;
126945514Stegge		if (patmatch(start, dp->d_name, 0)) {
1270211155Sjilles			if (enddir + dp->d_namlen + 1 > expdir_end)
1271211155Sjilles				continue;
1272211155Sjilles			memcpy(enddir, dp->d_name, dp->d_namlen + 1);
1273211155Sjilles			if (atend)
12741556Srgrimes				addfname(expdir);
1275211155Sjilles			else {
1276211155Sjilles				if (enddir + dp->d_namlen + 2 > expdir_end)
127717987Speter					continue;
1278211155Sjilles				enddir[dp->d_namlen] = '/';
1279211155Sjilles				enddir[dp->d_namlen + 1] = '\0';
1280211155Sjilles				expmeta(enddir + dp->d_namlen + 1, endname);
12811556Srgrimes			}
12821556Srgrimes		}
12831556Srgrimes	}
12841556Srgrimes	closedir(dirp);
12851556Srgrimes	if (! atend)
1286207944Sjilles		endname[-esc - 1] = esc ? CTLESC : '/';
12871556Srgrimes}
12881556Srgrimes
12891556Srgrimes
12901556Srgrimes/*
12911556Srgrimes * Add a file name to the list.
12921556Srgrimes */
12931556Srgrimes
1294213811Sobrienstatic void
129590111Simpaddfname(char *name)
129690111Simp{
12971556Srgrimes	char *p;
12981556Srgrimes	struct strlist *sp;
12991556Srgrimes
13001556Srgrimes	p = stalloc(strlen(name) + 1);
13011556Srgrimes	scopy(name, p);
13021556Srgrimes	sp = (struct strlist *)stalloc(sizeof *sp);
13031556Srgrimes	sp->text = p;
13041556Srgrimes	*exparg.lastp = sp;
13051556Srgrimes	exparg.lastp = &sp->next;
13061556Srgrimes}
13071556Srgrimes
13081556Srgrimes
13091556Srgrimes/*
13101556Srgrimes * Sort the results of file name expansion.  It calculates the number of
13111556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the
13121556Srgrimes * work.
13131556Srgrimes */
13141556Srgrimes
1315213811Sobrienstatic struct strlist *
131690111Simpexpsort(struct strlist *str)
131790111Simp{
13181556Srgrimes	int len;
13191556Srgrimes	struct strlist *sp;
13201556Srgrimes
13211556Srgrimes	len = 0;
13221556Srgrimes	for (sp = str ; sp ; sp = sp->next)
13231556Srgrimes		len++;
13241556Srgrimes	return msort(str, len);
13251556Srgrimes}
13261556Srgrimes
13271556Srgrimes
1328213811Sobrienstatic struct strlist *
132990111Simpmsort(struct strlist *list, int len)
133017987Speter{
133117987Speter	struct strlist *p, *q = NULL;
13321556Srgrimes	struct strlist **lpp;
13331556Srgrimes	int half;
13341556Srgrimes	int n;
13351556Srgrimes
13361556Srgrimes	if (len <= 1)
13371556Srgrimes		return list;
13388855Srgrimes	half = len >> 1;
13391556Srgrimes	p = list;
13401556Srgrimes	for (n = half ; --n >= 0 ; ) {
13411556Srgrimes		q = p;
13421556Srgrimes		p = p->next;
13431556Srgrimes	}
13441556Srgrimes	q->next = NULL;			/* terminate first half of list */
13451556Srgrimes	q = msort(list, half);		/* sort first half of list */
13461556Srgrimes	p = msort(p, len - half);		/* sort second half */
13471556Srgrimes	lpp = &list;
13481556Srgrimes	for (;;) {
13491556Srgrimes		if (strcmp(p->text, q->text) < 0) {
13501556Srgrimes			*lpp = p;
13511556Srgrimes			lpp = &p->next;
13521556Srgrimes			if ((p = *lpp) == NULL) {
13531556Srgrimes				*lpp = q;
13541556Srgrimes				break;
13551556Srgrimes			}
13561556Srgrimes		} else {
13571556Srgrimes			*lpp = q;
13581556Srgrimes			lpp = &q->next;
13591556Srgrimes			if ((q = *lpp) == NULL) {
13601556Srgrimes				*lpp = p;
13611556Srgrimes				break;
13621556Srgrimes			}
13631556Srgrimes		}
13641556Srgrimes	}
13651556Srgrimes	return list;
13661556Srgrimes}
13671556Srgrimes
13681556Srgrimes
13691556Srgrimes
13701556Srgrimes/*
13711556Srgrimes * Returns true if the pattern matches the string.
13721556Srgrimes */
13731556Srgrimes
13741556Srgrimesint
1375200956Sjillespatmatch(const char *pattern, const char *string, int squoted)
137690111Simp{
1377200956Sjilles	const char *p, *q;
137825233Ssteve	char c;
13791556Srgrimes
13801556Srgrimes	p = pattern;
13811556Srgrimes	q = string;
13821556Srgrimes	for (;;) {
13831556Srgrimes		switch (c = *p++) {
13841556Srgrimes		case '\0':
13851556Srgrimes			goto breakloop;
13861556Srgrimes		case CTLESC:
138745514Stegge			if (squoted && *q == CTLESC)
138845514Stegge				q++;
13891556Srgrimes			if (*q++ != *p++)
13901556Srgrimes				return 0;
13911556Srgrimes			break;
139238887Stegge		case CTLQUOTEMARK:
139338887Stegge			continue;
13941556Srgrimes		case '?':
139545514Stegge			if (squoted && *q == CTLESC)
139645514Stegge				q++;
13971556Srgrimes			if (*q++ == '\0')
13981556Srgrimes				return 0;
13991556Srgrimes			break;
14001556Srgrimes		case '*':
14011556Srgrimes			c = *p;
140238887Stegge			while (c == CTLQUOTEMARK || c == '*')
140338887Stegge				c = *++p;
140438887Stegge			if (c != CTLESC &&  c != CTLQUOTEMARK &&
140538887Stegge			    c != '?' && c != '*' && c != '[') {
14061556Srgrimes				while (*q != c) {
140745514Stegge					if (squoted && *q == CTLESC &&
140845514Stegge					    q[1] == c)
140945514Stegge						break;
14101556Srgrimes					if (*q == '\0')
14111556Srgrimes						return 0;
141245514Stegge					if (squoted && *q == CTLESC)
141345514Stegge						q++;
14141556Srgrimes					q++;
14151556Srgrimes				}
14161556Srgrimes			}
14171556Srgrimes			do {
1418211646Sjilles				if (patmatch(p, q, squoted))
14191556Srgrimes					return 1;
142045514Stegge				if (squoted && *q == CTLESC)
142145514Stegge					q++;
14221556Srgrimes			} while (*q++ != '\0');
14231556Srgrimes			return 0;
14241556Srgrimes		case '[': {
1425200956Sjilles			const char *endp;
14261556Srgrimes			int invert, found;
14271556Srgrimes			char chr;
14281556Srgrimes
14291556Srgrimes			endp = p;
143026488Sache			if (*endp == '!' || *endp == '^')
14311556Srgrimes				endp++;
14321556Srgrimes			for (;;) {
143338887Stegge				while (*endp == CTLQUOTEMARK)
143438887Stegge					endp++;
14351556Srgrimes				if (*endp == '\0')
14361556Srgrimes					goto dft;		/* no matching ] */
14371556Srgrimes				if (*endp == CTLESC)
14381556Srgrimes					endp++;
14391556Srgrimes				if (*++endp == ']')
14401556Srgrimes					break;
14411556Srgrimes			}
14421556Srgrimes			invert = 0;
144326488Sache			if (*p == '!' || *p == '^') {
14441556Srgrimes				invert++;
14451556Srgrimes				p++;
14461556Srgrimes			}
14471556Srgrimes			found = 0;
14481556Srgrimes			chr = *q++;
144945514Stegge			if (squoted && chr == CTLESC)
145045514Stegge				chr = *q++;
145117987Speter			if (chr == '\0')
145217987Speter				return 0;
14531556Srgrimes			c = *p++;
14541556Srgrimes			do {
145538887Stegge				if (c == CTLQUOTEMARK)
145638887Stegge					continue;
14571556Srgrimes				if (c == CTLESC)
14581556Srgrimes					c = *p++;
14591556Srgrimes				if (*p == '-' && p[1] != ']') {
14601556Srgrimes					p++;
146138887Stegge					while (*p == CTLQUOTEMARK)
146238887Stegge						p++;
14631556Srgrimes					if (*p == CTLESC)
14641556Srgrimes						p++;
146517557Sache					if (   collate_range_cmp(chr, c) >= 0
146617557Sache					    && collate_range_cmp(chr, *p) <= 0
146717525Sache					   )
14681556Srgrimes						found = 1;
14691556Srgrimes					p++;
14701556Srgrimes				} else {
14711556Srgrimes					if (chr == c)
14721556Srgrimes						found = 1;
14731556Srgrimes				}
14741556Srgrimes			} while ((c = *p++) != ']');
14751556Srgrimes			if (found == invert)
14761556Srgrimes				return 0;
14771556Srgrimes			break;
14781556Srgrimes		}
14791556Srgrimesdft:	        default:
148045514Stegge			if (squoted && *q == CTLESC)
148145514Stegge				q++;
14821556Srgrimes			if (*q++ != c)
14831556Srgrimes				return 0;
14841556Srgrimes			break;
14851556Srgrimes		}
14861556Srgrimes	}
14871556Srgrimesbreakloop:
14881556Srgrimes	if (*q != '\0')
14891556Srgrimes		return 0;
14901556Srgrimes	return 1;
14911556Srgrimes}
14921556Srgrimes
14931556Srgrimes
14941556Srgrimes
14951556Srgrimes/*
1496212243Sjilles * Remove any CTLESC and CTLQUOTEMARK characters from a string.
14971556Srgrimes */
14981556Srgrimes
14991556Srgrimesvoid
150090111Simprmescapes(char *str)
150138887Stegge{
150225233Ssteve	char *p, *q;
15031556Srgrimes
15041556Srgrimes	p = str;
1505214512Sjilles	while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) {
15061556Srgrimes		if (*p++ == '\0')
15071556Srgrimes			return;
15081556Srgrimes	}
15091556Srgrimes	q = p;
15101556Srgrimes	while (*p) {
1511214512Sjilles		if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) {
151238887Stegge			p++;
151338887Stegge			continue;
151438887Stegge		}
15151556Srgrimes		if (*p == CTLESC)
15161556Srgrimes			p++;
15171556Srgrimes		*q++ = *p++;
15181556Srgrimes	}
15191556Srgrimes	*q = '\0';
15201556Srgrimes}
15211556Srgrimes
15221556Srgrimes
15231556Srgrimes
15241556Srgrimes/*
15251556Srgrimes * See if a pattern matches in a case statement.
15261556Srgrimes */
15271556Srgrimes
15281556Srgrimesint
1529200956Sjillescasematch(union node *pattern, const char *val)
153090111Simp{
15311556Srgrimes	struct stackmark smark;
15321556Srgrimes	int result;
15331556Srgrimes	char *p;
15341556Srgrimes
15351556Srgrimes	setstackmark(&smark);
15361556Srgrimes	argbackq = pattern->narg.backquote;
15371556Srgrimes	STARTSTACKSTR(expdest);
15381556Srgrimes	ifslastp = NULL;
15391556Srgrimes	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
15401556Srgrimes	STPUTC('\0', expdest);
15411556Srgrimes	p = grabstackstr(expdest);
154245514Stegge	result = patmatch(p, val, 0);
15431556Srgrimes	popstackmark(&smark);
15441556Srgrimes	return result;
15451556Srgrimes}
154617987Speter
154717987Speter/*
154817987Speter * Our own itoa().
154917987Speter */
155017987Speter
1551213811Sobrienstatic char *
155290111Simpcvtnum(int num, char *buf)
155390111Simp{
155417987Speter	char temp[32];
155517987Speter	int neg = num < 0;
155617987Speter	char *p = temp + 31;
155717987Speter
155817987Speter	temp[31] = '\0';
155917987Speter
156017987Speter	do {
156117987Speter		*--p = num % 10 + '0';
156217987Speter	} while ((num /= 10) != 0);
156317987Speter
156417987Speter	if (neg)
156517987Speter		*--p = '-';
156617987Speter
1567215783Sjilles	STPUTS(p, buf);
156817987Speter	return buf;
156917987Speter}
1570108286Stjr
1571108286Stjr/*
1572216778Sjilles * Check statically if expanding a string may have side effects.
1573216778Sjilles */
1574216778Sjillesint
1575216778Sjillesexpandhassideeffects(const char *p)
1576216778Sjilles{
1577216778Sjilles	int c;
1578216778Sjilles	int arinest;
1579216778Sjilles
1580216778Sjilles	arinest = 0;
1581216778Sjilles	while ((c = *p++) != '\0') {
1582216778Sjilles		switch (c) {
1583216778Sjilles		case CTLESC:
1584216778Sjilles			p++;
1585216778Sjilles			break;
1586216778Sjilles		case CTLVAR:
1587216778Sjilles			c = *p++;
1588216778Sjilles			/* Expanding $! sets the job to remembered. */
1589216778Sjilles			if (*p == '!')
1590216778Sjilles				return 1;
1591216778Sjilles			if ((c & VSTYPE) == VSASSIGN)
1592216778Sjilles				return 1;
1593216778Sjilles			/*
1594216778Sjilles			 * If we are in arithmetic, the parameter may contain
1595216778Sjilles			 * '=' which may cause side effects. Exceptions are
1596216778Sjilles			 * the length of a parameter and $$, $# and $? which
1597216778Sjilles			 * are always numeric.
1598216778Sjilles			 */
1599216778Sjilles			if ((c & VSTYPE) == VSLENGTH) {
1600216778Sjilles				while (*p != '=')
1601216778Sjilles					p++;
1602216778Sjilles				p++;
1603216778Sjilles				break;
1604216778Sjilles			}
1605216778Sjilles			if ((*p == '$' || *p == '#' || *p == '?') &&
1606216778Sjilles			    p[1] == '=') {
1607216778Sjilles				p += 2;
1608216778Sjilles				break;
1609216778Sjilles			}
1610216778Sjilles			if (arinest > 0)
1611216778Sjilles				return 1;
1612216778Sjilles			break;
1613216778Sjilles		case CTLBACKQ:
1614216778Sjilles		case CTLBACKQ | CTLQUOTE:
1615216778Sjilles			if (arinest > 0)
1616216778Sjilles				return 1;
1617216778Sjilles			break;
1618216778Sjilles		case CTLARI:
1619216778Sjilles			arinest++;
1620216778Sjilles			break;
1621216778Sjilles		case CTLENDARI:
1622216778Sjilles			arinest--;
1623216778Sjilles			break;
1624216778Sjilles		case '=':
1625216778Sjilles			if (*p == '=') {
1626216778Sjilles				/* Allow '==' operator. */
1627216778Sjilles				p++;
1628216778Sjilles				continue;
1629216778Sjilles			}
1630216778Sjilles			if (arinest > 0)
1631216778Sjilles				return 1;
1632216778Sjilles			break;
1633216778Sjilles		case '!': case '<': case '>':
1634216778Sjilles			/* Allow '!=', '<=', '>=' operators. */
1635216778Sjilles			if (*p == '=')
1636216778Sjilles				p++;
1637216778Sjilles			break;
1638216778Sjilles		}
1639216778Sjilles	}
1640216778Sjilles	return 0;
1641216778Sjilles}
1642216778Sjilles
1643216778Sjilles/*
1644108286Stjr * Do most of the work for wordexp(3).
1645108286Stjr */
1646108286Stjr
1647108286Stjrint
1648108286Stjrwordexpcmd(int argc, char **argv)
1649108286Stjr{
1650108286Stjr	size_t len;
1651108286Stjr	int i;
1652108286Stjr
1653108286Stjr	out1fmt("%08x", argc - 1);
1654108286Stjr	for (i = 1, len = 0; i < argc; i++)
1655108286Stjr		len += strlen(argv[i]);
1656108286Stjr	out1fmt("%08x", (int)len);
1657215567Sjilles	for (i = 1; i < argc; i++)
1658215567Sjilles		outbin(argv[i], strlen(argv[i]) + 1, out1);
1659108286Stjr        return (0);
1660108286Stjr}
1661