expand.c revision 194975
1235723Sbapt/*-
2235723Sbapt * Copyright (c) 1991, 1993
3235723Sbapt *	The Regents of the University of California.  All rights reserved.
4235723Sbapt *
5235723Sbapt * This code is derived from software contributed to Berkeley by
6235723Sbapt * Kenneth Almquist.
7235723Sbapt *
8262960Sjmmv * Redistribution and use in source and binary forms, with or without
9235723Sbapt * modification, are permitted provided that the following conditions
10235723Sbapt * are met:
11235723Sbapt * 1. Redistributions of source code must retain the above copyright
12235723Sbapt *    notice, this list of conditions and the following disclaimer.
13235723Sbapt * 2. Redistributions in binary form must reproduce the above copyright
14235723Sbapt *    notice, this list of conditions and the following disclaimer in the
15235723Sbapt *    documentation and/or other materials provided with the distribution.
16235723Sbapt * 4. Neither the name of the University nor the names of its contributors
17235723Sbapt *    may be used to endorse or promote products derived from this software
18235723Sbapt *    without specific prior written permission.
19235723Sbapt *
20235723Sbapt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21235723Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22235723Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23235723Sbapt * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24235723Sbapt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25235723Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26235723Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27235723Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28235723Sbapt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29235723Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30235723Sbapt * SUCH DAMAGE.
31235723Sbapt */
32235723Sbapt
33235723Sbapt#ifndef lint
34235723Sbapt#if 0
35235723Sbaptstatic char sccsid[] = "@(#)expand.c	8.5 (Berkeley) 5/15/95";
36235723Sbapt#endif
37235723Sbapt#endif /* not lint */
38235723Sbapt#include <sys/cdefs.h>
39235723Sbapt__FBSDID("$FreeBSD: head/bin/sh/expand.c 194975 2009-06-25 17:10:51Z jilles $");
40235723Sbapt
41235723Sbapt#include <sys/types.h>
42235723Sbapt#include <sys/time.h>
43235723Sbapt#include <sys/stat.h>
44235723Sbapt#include <errno.h>
45235723Sbapt#include <dirent.h>
46235723Sbapt#include <unistd.h>
47235723Sbapt#include <pwd.h>
48235723Sbapt#include <stdlib.h>
49235723Sbapt#include <limits.h>
50235723Sbapt#include <stdio.h>
51235723Sbapt#include <string.h>
52235723Sbapt
53235723Sbapt/*
54235723Sbapt * Routines to expand arguments to commands.  We have to deal with
55235723Sbapt * backquotes, shell variables, and file metacharacters.
56235723Sbapt */
57235723Sbapt
58235723Sbapt#include "shell.h"
59235723Sbapt#include "main.h"
60235723Sbapt#include "nodes.h"
61235723Sbapt#include "eval.h"
62235723Sbapt#include "expand.h"
63235723Sbapt#include "syntax.h"
64235723Sbapt#include "parser.h"
65235723Sbapt#include "jobs.h"
66235723Sbapt#include "options.h"
67235723Sbapt#include "var.h"
68235723Sbapt#include "input.h"
69235723Sbapt#include "output.h"
70235723Sbapt#include "memalloc.h"
71235723Sbapt#include "error.h"
72235723Sbapt#include "mystring.h"
73235723Sbapt#include "arith.h"
74235723Sbapt#include "show.h"
75235723Sbapt
76235723Sbapt/*
77235723Sbapt * Structure specifying which parts of the string should be searched
78235723Sbapt * for IFS characters.
79235723Sbapt */
80235723Sbapt
81235723Sbaptstruct ifsregion {
82235723Sbapt	struct ifsregion *next;	/* next region in list */
83235723Sbapt	int begoff;		/* offset of start of region */
84235723Sbapt	int endoff;		/* offset of end of region */
85235723Sbapt	int inquotes;		/* search for nul bytes only */
86235723Sbapt};
87235723Sbapt
88235723Sbapt
89235723SbaptSTATIC char *expdest;			/* output of current string */
90235723SbaptSTATIC struct nodelist *argbackq;	/* list of back quote expressions */
91235723SbaptSTATIC struct ifsregion ifsfirst;	/* first struct in list of ifs regions */
92235723SbaptSTATIC struct ifsregion *ifslastp;	/* last struct in list */
93235723SbaptSTATIC struct arglist exparg;		/* holds expanded arg list */
94235723Sbapt
95235723SbaptSTATIC void argstr(char *, int);
96235723SbaptSTATIC char *exptilde(char *, int);
97235723SbaptSTATIC void expbackq(union node *, int, int);
98235723SbaptSTATIC int subevalvar(char *, char *, int, int, int, int);
99235723SbaptSTATIC char *evalvar(char *, int);
100235723SbaptSTATIC int varisset(char *, int);
101235723SbaptSTATIC void varvalue(char *, int, int, int);
102235723SbaptSTATIC void recordregion(int, int, int);
103235723SbaptSTATIC void removerecordregions(int);
104235723SbaptSTATIC void ifsbreakup(char *, struct arglist *);
105235723SbaptSTATIC void expandmeta(struct strlist *, int);
106235723SbaptSTATIC void expmeta(char *, char *);
107235723SbaptSTATIC void addfname(char *);
108235723SbaptSTATIC struct strlist *expsort(struct strlist *);
109235723SbaptSTATIC struct strlist *msort(struct strlist *, int);
110235723SbaptSTATIC int pmatch(char *, char *, int);
111235723SbaptSTATIC char *cvtnum(int, char *);
112235723SbaptSTATIC int collate_range_cmp(int, int);
113235723Sbapt
114235723SbaptSTATIC int
115235723Sbaptcollate_range_cmp(int c1, int c2)
116235723Sbapt{
117235723Sbapt	static char s1[2], s2[2];
118235723Sbapt
119235723Sbapt	s1[0] = c1;
120235723Sbapt	s2[0] = c2;
121235723Sbapt	return (strcoll(s1, s2));
122235723Sbapt}
123235723Sbapt
124235723Sbapt/*
125235723Sbapt * Expand shell variables and backquotes inside a here document.
126235723Sbapt *	union node *arg		the document
127235723Sbapt *	int fd;			where to write the expanded version
128235723Sbapt */
129235723Sbapt
130235723Sbaptvoid
131235723Sbaptexpandhere(union node *arg, int fd)
132235723Sbapt{
133235723Sbapt	herefd = fd;
134235723Sbapt	expandarg(arg, (struct arglist *)NULL, 0);
135235723Sbapt	xwrite(fd, stackblock(), expdest - stackblock());
136235723Sbapt}
137235723Sbapt
138235723Sbapt
139235723Sbapt/*
140235723Sbapt * Perform variable substitution and command substitution on an argument,
141235723Sbapt * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
142235723Sbapt * perform splitting and file name expansion.  When arglist is NULL, perform
143235723Sbapt * here document expansion.
144235723Sbapt */
145235723Sbapt
146235723Sbaptvoid
147235723Sbaptexpandarg(union node *arg, struct arglist *arglist, int flag)
148235723Sbapt{
149235723Sbapt	struct strlist *sp;
150235723Sbapt	char *p;
151235723Sbapt
152235723Sbapt	argbackq = arg->narg.backquote;
153235723Sbapt	STARTSTACKSTR(expdest);
154235723Sbapt	ifsfirst.next = NULL;
155235723Sbapt	ifslastp = NULL;
156235723Sbapt	argstr(arg->narg.text, flag);
157235723Sbapt	if (arglist == NULL) {
158235723Sbapt		return;			/* here document expanded */
159235723Sbapt	}
160235723Sbapt	STPUTC('\0', expdest);
161235723Sbapt	p = grabstackstr(expdest);
162235723Sbapt	exparg.lastp = &exparg.list;
163235723Sbapt	/*
164235723Sbapt	 * TODO - EXP_REDIR
165235723Sbapt	 */
166235723Sbapt	if (flag & EXP_FULL) {
167235723Sbapt		ifsbreakup(p, &exparg);
168235723Sbapt		*exparg.lastp = NULL;
169235723Sbapt		exparg.lastp = &exparg.list;
170235723Sbapt		expandmeta(exparg.list, flag);
171235723Sbapt	} else {
172235723Sbapt		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
173235723Sbapt			rmescapes(p);
174235723Sbapt		sp = (struct strlist *)stalloc(sizeof (struct strlist));
175235723Sbapt		sp->text = p;
176235723Sbapt		*exparg.lastp = sp;
177235723Sbapt		exparg.lastp = &sp->next;
178235723Sbapt	}
179235723Sbapt	while (ifsfirst.next != NULL) {
180235723Sbapt		struct ifsregion *ifsp;
181235723Sbapt		INTOFF;
182235723Sbapt		ifsp = ifsfirst.next->next;
183235723Sbapt		ckfree(ifsfirst.next);
184235723Sbapt		ifsfirst.next = ifsp;
185235723Sbapt		INTON;
186235723Sbapt	}
187235723Sbapt	*exparg.lastp = NULL;
188235723Sbapt	if (exparg.list) {
189235723Sbapt		*arglist->lastp = exparg.list;
190235723Sbapt		arglist->lastp = exparg.lastp;
191235723Sbapt	}
192235723Sbapt}
193235723Sbapt
194235723Sbapt
195235723Sbapt
196235723Sbapt/*
197235723Sbapt * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
198235723Sbapt * characters to allow for further processing.  Otherwise treat
199235723Sbapt * $@ like $* since no splitting will be performed.
200235723Sbapt */
201235723Sbapt
202235723SbaptSTATIC void
203235723Sbaptargstr(char *p, int flag)
204235723Sbapt{
205235723Sbapt	char c;
206235723Sbapt	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);	/* do CTLESC */
207235723Sbapt	int firsteq = 1;
208235723Sbapt
209235723Sbapt	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
210235723Sbapt		p = exptilde(p, flag);
211235723Sbapt	for (;;) {
212235723Sbapt		switch (c = *p++) {
213235723Sbapt		case '\0':
214235723Sbapt		case CTLENDVAR: /* ??? */
215235723Sbapt			goto breakloop;
216235723Sbapt		case CTLQUOTEMARK:
217235723Sbapt			/* "$@" syntax adherence hack */
218235723Sbapt			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
219235723Sbapt				break;
220235723Sbapt			if ((flag & EXP_FULL) != 0)
221235723Sbapt				STPUTC(c, expdest);
222235723Sbapt			break;
223235723Sbapt		case CTLESC:
224235723Sbapt			if (quotes)
225235723Sbapt				STPUTC(c, expdest);
226235723Sbapt			c = *p++;
227235723Sbapt			STPUTC(c, expdest);
228235723Sbapt			break;
229235723Sbapt		case CTLVAR:
230235723Sbapt			p = evalvar(p, flag);
231235723Sbapt			break;
232235723Sbapt		case CTLBACKQ:
233235723Sbapt		case CTLBACKQ|CTLQUOTE:
234235723Sbapt			expbackq(argbackq->n, c & CTLQUOTE, flag);
235235723Sbapt			argbackq = argbackq->next;
236235723Sbapt			break;
237235723Sbapt		case CTLENDARI:
238235723Sbapt			expari(flag);
239235723Sbapt			break;
240235723Sbapt		case ':':
241235723Sbapt		case '=':
242235723Sbapt			/*
243235723Sbapt			 * sort of a hack - expand tildes in variable
244235723Sbapt			 * assignments (after the first '=' and after ':'s).
245235723Sbapt			 */
246235723Sbapt			STPUTC(c, expdest);
247235723Sbapt			if (flag & EXP_VARTILDE && *p == '~') {
248235723Sbapt				if (c == '=') {
249235723Sbapt					if (firsteq)
250235723Sbapt						firsteq = 0;
251235723Sbapt					else
252235723Sbapt						break;
253235723Sbapt				}
254235723Sbapt				p = exptilde(p, flag);
255235723Sbapt			}
256235723Sbapt			break;
257235723Sbapt		default:
258235723Sbapt			STPUTC(c, expdest);
259235723Sbapt		}
260235723Sbapt	}
261235723Sbaptbreakloop:;
262235723Sbapt}
263235723Sbapt
264235723SbaptSTATIC char *
265235723Sbaptexptilde(char *p, int flag)
266235723Sbapt{
267235723Sbapt	char c, *startp = p;
268235723Sbapt	struct passwd *pw;
269235723Sbapt	char *home;
270235723Sbapt	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
271235723Sbapt
272235723Sbapt	while ((c = *p) != '\0') {
273235723Sbapt		switch(c) {
274235723Sbapt		case CTLESC:
275235723Sbapt			return (startp);
276235723Sbapt		case CTLQUOTEMARK:
277235723Sbapt			return (startp);
278235723Sbapt		case ':':
279235723Sbapt			if (flag & EXP_VARTILDE)
280235723Sbapt				goto done;
281235723Sbapt			break;
282235723Sbapt		case '/':
283235723Sbapt			goto done;
284235723Sbapt		}
285235723Sbapt		p++;
286235723Sbapt	}
287235723Sbaptdone:
288235723Sbapt	*p = '\0';
289235723Sbapt	if (*(startp+1) == '\0') {
290235723Sbapt		if ((home = lookupvar("HOME")) == NULL)
291235723Sbapt			goto lose;
292235723Sbapt	} else {
293235723Sbapt		if ((pw = getpwnam(startp+1)) == NULL)
294235723Sbapt			goto lose;
295235723Sbapt		home = pw->pw_dir;
296235723Sbapt	}
297235723Sbapt	if (*home == '\0')
298235723Sbapt		goto lose;
299235723Sbapt	*p = c;
300235723Sbapt	while ((c = *home++) != '\0') {
301235723Sbapt		if (quotes && SQSYNTAX[(int)c] == CCTL)
302235723Sbapt			STPUTC(CTLESC, expdest);
303235723Sbapt		STPUTC(c, expdest);
304235723Sbapt	}
305235723Sbapt	return (p);
306262960Sjmmvlose:
307235723Sbapt	*p = c;
308235723Sbapt	return (startp);
309235723Sbapt}
310235723Sbapt
311235723Sbapt
312235723SbaptSTATIC void
313235723Sbaptremoverecordregions(int endoff)
314235723Sbapt{
315235723Sbapt	if (ifslastp == NULL)
316235723Sbapt		return;
317235723Sbapt
318235723Sbapt	if (ifsfirst.endoff > endoff) {
319235723Sbapt		while (ifsfirst.next != NULL) {
320235723Sbapt			struct ifsregion *ifsp;
321235723Sbapt			INTOFF;
322235723Sbapt			ifsp = ifsfirst.next->next;
323235723Sbapt			ckfree(ifsfirst.next);
324235723Sbapt			ifsfirst.next = ifsp;
325235723Sbapt			INTON;
326235723Sbapt		}
327235723Sbapt		if (ifsfirst.begoff > endoff)
328235723Sbapt			ifslastp = NULL;
329235723Sbapt		else {
330235723Sbapt			ifslastp = &ifsfirst;
331235723Sbapt			ifsfirst.endoff = endoff;
332235723Sbapt		}
333235723Sbapt		return;
334235723Sbapt	}
335235723Sbapt
336235723Sbapt	ifslastp = &ifsfirst;
337235723Sbapt	while (ifslastp->next && ifslastp->next->begoff < endoff)
338235723Sbapt		ifslastp=ifslastp->next;
339235723Sbapt	while (ifslastp->next != NULL) {
340235723Sbapt		struct ifsregion *ifsp;
341235723Sbapt		INTOFF;
342235723Sbapt		ifsp = ifslastp->next->next;
343235723Sbapt		ckfree(ifslastp->next);
344235723Sbapt		ifslastp->next = ifsp;
345235723Sbapt		INTON;
346235723Sbapt	}
347235723Sbapt	if (ifslastp->endoff > endoff)
348235723Sbapt		ifslastp->endoff = endoff;
349235723Sbapt}
350235723Sbapt
351235723Sbapt/*
352235723Sbapt * Expand arithmetic expression.  Backup to start of expression,
353235723Sbapt * evaluate, place result in (backed up) result, adjust string position.
354235723Sbapt */
355235723Sbaptvoid
356235723Sbaptexpari(int flag)
357235723Sbapt{
358235723Sbapt	char *p, *start;
359235723Sbapt	arith_t result;
360235723Sbapt	int begoff;
361235723Sbapt	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
362235723Sbapt	int quoted;
363235723Sbapt
364235723Sbapt
365235723Sbapt	/*
366235723Sbapt	 * This routine is slightly over-complicated for
367235723Sbapt	 * efficiency.  First we make sure there is
368235723Sbapt	 * enough space for the result, which may be bigger
369235723Sbapt	 * than the expression if we add exponentiation.  Next we
370235723Sbapt	 * scan backwards looking for the start of arithmetic.  If the
371235723Sbapt	 * next previous character is a CTLESC character, then we
372235723Sbapt	 * have to rescan starting from the beginning since CTLESC
373235723Sbapt	 * characters have to be processed left to right.
374235723Sbapt	 */
375235723Sbapt	CHECKSTRSPACE(DIGITS(result) - 2, expdest);
376235723Sbapt	USTPUTC('\0', expdest);
377235723Sbapt	start = stackblock();
378235723Sbapt	p = expdest - 2;
379235723Sbapt	while (p >= start && *p != CTLARI)
380235723Sbapt		--p;
381235723Sbapt	if (p < start || *p != CTLARI)
382235723Sbapt		error("missing CTLARI (shouldn't happen)");
383235723Sbapt	if (p > start && *(p - 1) == CTLESC)
384235723Sbapt		for (p = start; *p != CTLARI; p++)
385235723Sbapt			if (*p == CTLESC)
386235723Sbapt				p++;
387235723Sbapt
388235723Sbapt	if (p[1] == '"')
389235723Sbapt		quoted=1;
390235723Sbapt	else
391235723Sbapt		quoted=0;
392235723Sbapt	begoff = p - start;
393235723Sbapt	removerecordregions(begoff);
394235723Sbapt	if (quotes)
395235723Sbapt		rmescapes(p+2);
396235723Sbapt	result = arith(p+2);
397235723Sbapt	fmtstr(p, DIGITS(result), ARITH_FORMAT_STR, result);
398235723Sbapt	while (*p++)
399235723Sbapt		;
400235723Sbapt	if (quoted == 0)
401235723Sbapt		recordregion(begoff, p - 1 - start, 0);
402235723Sbapt	result = expdest - p + 1;
403235723Sbapt	STADJUST(-result, expdest);
404235723Sbapt}
405235723Sbapt
406235723Sbapt
407235723Sbapt/*
408235723Sbapt * Expand stuff in backwards quotes.
409235723Sbapt */
410235723Sbapt
411235723SbaptSTATIC void
412235723Sbaptexpbackq(union node *cmd, int quoted, int flag)
413235723Sbapt{
414235723Sbapt	struct backcmd in;
415235723Sbapt	int i;
416235723Sbapt	char buf[128];
417235723Sbapt	char *p;
418235723Sbapt	char *dest = expdest;
419235723Sbapt	struct ifsregion saveifs, *savelastp;
420235723Sbapt	struct nodelist *saveargbackq;
421235723Sbapt	char lastc;
422235723Sbapt	int startloc = dest - stackblock();
423235723Sbapt	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
424235723Sbapt	int saveherefd;
425235723Sbapt	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
426235723Sbapt	int nnl;
427235723Sbapt
428235723Sbapt	INTOFF;
429235723Sbapt	saveifs = ifsfirst;
430235723Sbapt	savelastp = ifslastp;
431235723Sbapt	saveargbackq = argbackq;
432235723Sbapt	saveherefd = herefd;
433235723Sbapt	herefd = -1;
434235723Sbapt	p = grabstackstr(dest);
435235723Sbapt	evalbackcmd(cmd, &in);
436235723Sbapt	ungrabstackstr(p, dest);
437235723Sbapt	ifsfirst = saveifs;
438235723Sbapt	ifslastp = savelastp;
439235723Sbapt	argbackq = saveargbackq;
440235723Sbapt	herefd = saveherefd;
441235723Sbapt
442235723Sbapt	p = in.buf;
443235723Sbapt	lastc = '\0';
444235723Sbapt	nnl = 0;
445235723Sbapt	/* Don't copy trailing newlines */
446235723Sbapt	for (;;) {
447235723Sbapt		if (--in.nleft < 0) {
448235723Sbapt			if (in.fd < 0)
449235723Sbapt				break;
450235723Sbapt			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
451235723Sbapt			TRACE(("expbackq: read returns %d\n", i));
452235723Sbapt			if (i <= 0)
453235723Sbapt				break;
454235723Sbapt			p = buf;
455235723Sbapt			in.nleft = i - 1;
456235723Sbapt		}
457235723Sbapt		lastc = *p++;
458235723Sbapt		if (lastc != '\0') {
459235723Sbapt			if (quotes && syntax[(int)lastc] == CCTL)
460235723Sbapt				STPUTC(CTLESC, dest);
461235723Sbapt			if (lastc == '\n') {
462235723Sbapt				nnl++;
463235723Sbapt			} else {
464235723Sbapt				while (nnl > 0) {
465235723Sbapt					nnl--;
466235723Sbapt					STPUTC('\n', dest);
467235723Sbapt				}
468235723Sbapt				STPUTC(lastc, dest);
469235723Sbapt			}
470235723Sbapt		}
471235723Sbapt	}
472235723Sbapt
473235723Sbapt	if (in.fd >= 0)
474235723Sbapt		close(in.fd);
475235723Sbapt	if (in.buf)
476235723Sbapt		ckfree(in.buf);
477235723Sbapt	if (in.jp)
478235723Sbapt		exitstatus = waitforjob(in.jp, (int *)NULL);
479235723Sbapt	if (quoted == 0)
480235723Sbapt		recordregion(startloc, dest - stackblock(), 0);
481235723Sbapt	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
482235723Sbapt		(dest - stackblock()) - startloc,
483235723Sbapt		(dest - stackblock()) - startloc,
484235723Sbapt		stackblock() + startloc));
485235723Sbapt	expdest = dest;
486235723Sbapt	INTON;
487235723Sbapt}
488235723Sbapt
489235723Sbapt
490235723Sbapt
491235723SbaptSTATIC int
492235723Sbaptsubevalvar(char *p, char *str, int strloc, int subtype, int startloc,
493235723Sbapt  int varflags)
494235723Sbapt{
495235723Sbapt	char *startp;
496235723Sbapt	char *loc = NULL;
497235723Sbapt	char *q;
498235723Sbapt	int c = 0;
499235723Sbapt	int saveherefd = herefd;
500235723Sbapt	struct nodelist *saveargbackq = argbackq;
501235723Sbapt	int amount;
502235723Sbapt
503235723Sbapt	herefd = -1;
504235723Sbapt	argstr(p, 0);
505235723Sbapt	STACKSTRNUL(expdest);
506235723Sbapt	herefd = saveherefd;
507235723Sbapt	argbackq = saveargbackq;
508235723Sbapt	startp = stackblock() + startloc;
509235723Sbapt	if (str == NULL)
510235723Sbapt	    str = stackblock() + strloc;
511235723Sbapt
512235723Sbapt	switch (subtype) {
513235723Sbapt	case VSASSIGN:
514235723Sbapt		setvar(str, startp, 0);
515235723Sbapt		amount = startp - expdest;
516235723Sbapt		STADJUST(amount, expdest);
517235723Sbapt		varflags &= ~VSNUL;
518235723Sbapt		if (c != 0)
519235723Sbapt			*loc = c;
520235723Sbapt		return 1;
521235723Sbapt
522235723Sbapt	case VSQUESTION:
523235723Sbapt		if (*p != CTLENDVAR) {
524235723Sbapt			outfmt(&errout, "%s\n", startp);
525235723Sbapt			error((char *)NULL);
526235723Sbapt		}
527235723Sbapt		error("%.*s: parameter %snot set", (int)(p - str - 1),
528235723Sbapt		      str, (varflags & VSNUL) ? "null or "
529235723Sbapt					      : nullstr);
530235723Sbapt		return 0;
531235723Sbapt
532235723Sbapt	case VSTRIMLEFT:
533235723Sbapt		for (loc = startp; loc < str; loc++) {
534235723Sbapt			c = *loc;
535235723Sbapt			*loc = '\0';
536235723Sbapt			if (patmatch(str, startp, varflags & VSQUOTE)) {
537235723Sbapt				*loc = c;
538235723Sbapt				goto recordleft;
539235723Sbapt			}
540235723Sbapt			*loc = c;
541235723Sbapt			if ((varflags & VSQUOTE) && *loc == CTLESC)
542235723Sbapt				loc++;
543235723Sbapt		}
544235723Sbapt		return 0;
545235723Sbapt
546235723Sbapt	case VSTRIMLEFTMAX:
547235723Sbapt		for (loc = str - 1; loc >= startp;) {
548235723Sbapt			c = *loc;
549235723Sbapt			*loc = '\0';
550235723Sbapt			if (patmatch(str, startp, varflags & VSQUOTE)) {
551235723Sbapt				*loc = c;
552235723Sbapt				goto recordleft;
553235723Sbapt			}
554235723Sbapt			*loc = c;
555235723Sbapt			loc--;
556235723Sbapt			if ((varflags & VSQUOTE) && loc > startp &&
557235723Sbapt			    *(loc - 1) == CTLESC) {
558235723Sbapt				for (q = startp; q < loc; q++)
559235723Sbapt					if (*q == CTLESC)
560235723Sbapt						q++;
561235723Sbapt				if (q > loc)
562235723Sbapt					loc--;
563235723Sbapt			}
564235723Sbapt		}
565235723Sbapt		return 0;
566235723Sbapt
567235723Sbapt	case VSTRIMRIGHT:
568235723Sbapt		for (loc = str - 1; loc >= startp;) {
569235723Sbapt			if (patmatch(str, loc, varflags & VSQUOTE)) {
570235723Sbapt				amount = loc - expdest;
571235723Sbapt				STADJUST(amount, expdest);
572235723Sbapt				return 1;
573235723Sbapt			}
574235723Sbapt			loc--;
575235723Sbapt			if ((varflags & VSQUOTE) && loc > startp &&
576235723Sbapt			    *(loc - 1) == CTLESC) {
577235723Sbapt				for (q = startp; q < loc; q++)
578235723Sbapt					if (*q == CTLESC)
579235723Sbapt						q++;
580235723Sbapt				if (q > loc)
581235723Sbapt					loc--;
582235723Sbapt			}
583235723Sbapt		}
584235723Sbapt		return 0;
585235723Sbapt
586235723Sbapt	case VSTRIMRIGHTMAX:
587235723Sbapt		for (loc = startp; loc < str - 1; loc++) {
588235723Sbapt			if (patmatch(str, loc, varflags & VSQUOTE)) {
589235723Sbapt				amount = loc - expdest;
590235723Sbapt				STADJUST(amount, expdest);
591235723Sbapt				return 1;
592235723Sbapt			}
593235723Sbapt			if ((varflags & VSQUOTE) && *loc == CTLESC)
594235723Sbapt				loc++;
595235723Sbapt		}
596235723Sbapt		return 0;
597235723Sbapt
598235723Sbapt
599235723Sbapt	default:
600235723Sbapt		abort();
601235723Sbapt	}
602235723Sbapt
603235723Sbaptrecordleft:
604235723Sbapt	amount = ((str - 1) - (loc - startp)) - expdest;
605235723Sbapt	STADJUST(amount, expdest);
606235723Sbapt	while (loc != str - 1)
607235723Sbapt		*startp++ = *loc++;
608235723Sbapt	return 1;
609235723Sbapt}
610235723Sbapt
611235723Sbapt
612235723Sbapt/*
613235723Sbapt * Expand a variable, and return a pointer to the next character in the
614235723Sbapt * input string.
615235723Sbapt */
616235723Sbapt
617235723SbaptSTATIC char *
618235723Sbaptevalvar(char *p, int flag)
619235723Sbapt{
620235723Sbapt	int subtype;
621235723Sbapt	int varflags;
622235723Sbapt	char *var;
623235723Sbapt	char *val;
624235723Sbapt	int patloc;
625235723Sbapt	int c;
626235723Sbapt	int set;
627235723Sbapt	int special;
628235723Sbapt	int startloc;
629235723Sbapt	int varlen;
630235723Sbapt	int easy;
631235723Sbapt	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
632235723Sbapt
633235723Sbapt	varflags = (unsigned char)*p++;
634235723Sbapt	subtype = varflags & VSTYPE;
635235723Sbapt	var = p;
636235723Sbapt	special = 0;
637235723Sbapt	if (! is_name(*p))
638235723Sbapt		special = 1;
639235723Sbapt	p = strchr(p, '=') + 1;
640235723Sbaptagain: /* jump here after setting a variable with ${var=text} */
641235723Sbapt	if (varflags & VSLINENO) {
642235723Sbapt		set = 1;
643235723Sbapt		special = 0;
644235723Sbapt		val = var;
645235723Sbapt		p[-1] = '\0';	/* temporarily overwrite '=' to have \0
646235723Sbapt				   terminated string */
647235723Sbapt	} else if (special) {
648235723Sbapt		set = varisset(var, varflags & VSNUL);
649235723Sbapt		val = NULL;
650235723Sbapt	} else {
651235723Sbapt		val = bltinlookup(var, 1);
652235723Sbapt		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
653235723Sbapt			val = NULL;
654235723Sbapt			set = 0;
655235723Sbapt		} else
656235723Sbapt			set = 1;
657235723Sbapt	}
658235723Sbapt	varlen = 0;
659235723Sbapt	startloc = expdest - stackblock();
660235723Sbapt	if (!set && uflag) {
661235723Sbapt		switch (subtype) {
662235723Sbapt		case VSNORMAL:
663235723Sbapt		case VSTRIMLEFT:
664262960Sjmmv		case VSTRIMLEFTMAX:
665235723Sbapt		case VSTRIMRIGHT:
666235723Sbapt		case VSTRIMRIGHTMAX:
667235723Sbapt		case VSLENGTH:
668235723Sbapt			error("%.*s: parameter not set", (int)(p - var - 1),
669235723Sbapt			    var);
670235723Sbapt		}
671235723Sbapt	}
672235723Sbapt	if (set && subtype != VSPLUS) {
673235723Sbapt		/* insert the value of the variable */
674235723Sbapt		if (special) {
675235723Sbapt			varvalue(var, varflags & VSQUOTE, subtype, flag);
676235723Sbapt			if (subtype == VSLENGTH) {
677235723Sbapt				varlen = expdest - stackblock() - startloc;
678235723Sbapt				STADJUST(-varlen, expdest);
679235723Sbapt			}
680235723Sbapt		} else {
681262960Sjmmv			char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
682235723Sbapt								  : BASESYNTAX;
683235723Sbapt
684235723Sbapt			if (subtype == VSLENGTH) {
685235723Sbapt				for (;*val; val++)
686235723Sbapt					varlen++;
687235723Sbapt			}
688235723Sbapt			else {
689235723Sbapt				while (*val) {
690235723Sbapt					if (quotes &&
691235723Sbapt					    syntax[(int)*val] == CCTL)
692235723Sbapt						STPUTC(CTLESC, expdest);
693235723Sbapt					STPUTC(*val++, expdest);
694235723Sbapt				}
695235723Sbapt
696235723Sbapt			}
697235723Sbapt		}
698235723Sbapt	}
699235723Sbapt
700235723Sbapt	if (subtype == VSPLUS)
701235723Sbapt		set = ! set;
702235723Sbapt
703235723Sbapt	easy = ((varflags & VSQUOTE) == 0 ||
704235723Sbapt		(*var == '@' && shellparam.nparam != 1));
705235723Sbapt
706235723Sbapt
707235723Sbapt	switch (subtype) {
708235723Sbapt	case VSLENGTH:
709235723Sbapt		expdest = cvtnum(varlen, expdest);
710235723Sbapt		goto record;
711235723Sbapt
712235723Sbapt	case VSNORMAL:
713235723Sbapt		if (!easy)
714235723Sbapt			break;
715235723Sbaptrecord:
716235723Sbapt		recordregion(startloc, expdest - stackblock(),
717235723Sbapt			     varflags & VSQUOTE);
718235723Sbapt		break;
719235723Sbapt
720235723Sbapt	case VSPLUS:
721235723Sbapt	case VSMINUS:
722235723Sbapt		if (!set) {
723235723Sbapt			argstr(p, flag);
724235723Sbapt			break;
725235723Sbapt		}
726235723Sbapt		if (easy)
727235723Sbapt			goto record;
728235723Sbapt		break;
729235723Sbapt
730235723Sbapt	case VSTRIMLEFT:
731235723Sbapt	case VSTRIMLEFTMAX:
732235723Sbapt	case VSTRIMRIGHT:
733235723Sbapt	case VSTRIMRIGHTMAX:
734235723Sbapt		if (!set)
735235723Sbapt			break;
736235723Sbapt		/*
737235723Sbapt		 * Terminate the string and start recording the pattern
738235723Sbapt		 * right after it
739235723Sbapt		 */
740235723Sbapt		STPUTC('\0', expdest);
741235723Sbapt		patloc = expdest - stackblock();
742235723Sbapt		if (subevalvar(p, NULL, patloc, subtype,
743235723Sbapt			       startloc, varflags) == 0) {
744235723Sbapt			int amount = (expdest - stackblock() - patloc) + 1;
745235723Sbapt			STADJUST(-amount, expdest);
746235723Sbapt		}
747235723Sbapt		/* Remove any recorded regions beyond start of variable */
748235723Sbapt		removerecordregions(startloc);
749235723Sbapt		goto record;
750235723Sbapt
751235723Sbapt	case VSASSIGN:
752235723Sbapt	case VSQUESTION:
753235723Sbapt		if (!set) {
754235723Sbapt			if (subevalvar(p, var, 0, subtype, startloc, varflags)) {
755235723Sbapt				varflags &= ~VSNUL;
756235723Sbapt				/*
757235723Sbapt				 * Remove any recorded regions beyond
758235723Sbapt				 * start of variable
759235723Sbapt				 */
760235723Sbapt				removerecordregions(startloc);
761235723Sbapt				goto again;
762235723Sbapt			}
763235723Sbapt			break;
764235723Sbapt		}
765235723Sbapt		if (easy)
766235723Sbapt			goto record;
767235723Sbapt		break;
768235723Sbapt
769235723Sbapt	case VSERROR:
770235723Sbapt		c = p - var - 1;
771235723Sbapt		error("${%.*s%s}: Bad substitution", c, var,
772235723Sbapt		    (c > 0 && *p != CTLENDVAR) ? "..." : "");
773235723Sbapt
774235723Sbapt	default:
775235723Sbapt		abort();
776235723Sbapt	}
777235723Sbapt	p[-1] = '=';	/* recover overwritten '=' */
778235723Sbapt
779235723Sbapt	if (subtype != VSNORMAL) {	/* skip to end of alternative */
780235723Sbapt		int nesting = 1;
781235723Sbapt		for (;;) {
782235723Sbapt			if ((c = *p++) == CTLESC)
783235723Sbapt				p++;
784235723Sbapt			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
785235723Sbapt				if (set)
786235723Sbapt					argbackq = argbackq->next;
787235723Sbapt			} else if (c == CTLVAR) {
788235723Sbapt				if ((*p++ & VSTYPE) != VSNORMAL)
789235723Sbapt					nesting++;
790235723Sbapt			} else if (c == CTLENDVAR) {
791235723Sbapt				if (--nesting == 0)
792235723Sbapt					break;
793235723Sbapt			}
794235723Sbapt		}
795235723Sbapt	}
796235723Sbapt	return p;
797235723Sbapt}
798235723Sbapt
799235723Sbapt
800235723Sbapt
801235723Sbapt/*
802235723Sbapt * Test whether a specialized variable is set.
803235723Sbapt */
804235723Sbapt
805235723SbaptSTATIC int
806235723Sbaptvarisset(char *name, int nulok)
807235723Sbapt{
808235723Sbapt
809235723Sbapt	if (*name == '!')
810235723Sbapt		return backgndpid != -1;
811235723Sbapt	else if (*name == '@' || *name == '*') {
812235723Sbapt		if (*shellparam.p == NULL)
813235723Sbapt			return 0;
814235723Sbapt
815235723Sbapt		if (nulok) {
816235723Sbapt			char **av;
817235723Sbapt
818235723Sbapt			for (av = shellparam.p; *av; av++)
819262960Sjmmv				if (**av != '\0')
820262960Sjmmv					return 1;
821235723Sbapt			return 0;
822235723Sbapt		}
823235723Sbapt	} else if (is_digit(*name)) {
824262960Sjmmv		char *ap;
825235723Sbapt		int num = atoi(name);
826235723Sbapt
827235723Sbapt		if (num > shellparam.nparam)
828235723Sbapt			return 0;
829235723Sbapt
830235723Sbapt		if (num == 0)
831235723Sbapt			ap = arg0;
832235723Sbapt		else
833235723Sbapt			ap = shellparam.p[num - 1];
834235723Sbapt
835235723Sbapt		if (nulok && (ap == NULL || *ap == '\0'))
836235723Sbapt			return 0;
837235723Sbapt	}
838235723Sbapt	return 1;
839235723Sbapt}
840235723Sbapt
841235723Sbapt
842235723Sbapt
843235723Sbapt/*
844235723Sbapt * Add the value of a specialized variable to the stack string.
845235723Sbapt */
846235723Sbapt
847235723SbaptSTATIC void
848235723Sbaptvarvalue(char *name, int quoted, int subtype, int flag)
849235723Sbapt{
850235723Sbapt	int num;
851235723Sbapt	char *p;
852235723Sbapt	int i;
853249582Sgabor	extern int oexitstatus;
854235723Sbapt	char sep;
855235723Sbapt	char **ap;
856235723Sbapt	char const *syntax;
857235723Sbapt
858235723Sbapt#define STRTODEST(p) \
859235723Sbapt	do {\
860235723Sbapt	if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) { \
861235723Sbapt		syntax = quoted? DQSYNTAX : BASESYNTAX; \
862235723Sbapt		while (*p) { \
863235723Sbapt			if (syntax[(int)*p] == CCTL) \
864235723Sbapt				STPUTC(CTLESC, expdest); \
865235723Sbapt			STPUTC(*p++, expdest); \
866235723Sbapt		} \
867235723Sbapt	} else \
868235723Sbapt		while (*p) \
869235723Sbapt			STPUTC(*p++, expdest); \
870235723Sbapt	} while (0)
871235723Sbapt
872235723Sbapt
873235723Sbapt	switch (*name) {
874235723Sbapt	case '$':
875235723Sbapt		num = rootpid;
876235723Sbapt		goto numvar;
877235723Sbapt	case '?':
878235723Sbapt		num = oexitstatus;
879235723Sbapt		goto numvar;
880235723Sbapt	case '#':
881235723Sbapt		num = shellparam.nparam;
882235723Sbapt		goto numvar;
883235723Sbapt	case '!':
884235723Sbapt		num = backgndpid;
885235723Sbaptnumvar:
886235723Sbapt		expdest = cvtnum(num, expdest);
887235723Sbapt		break;
888235723Sbapt	case '-':
889235723Sbapt		for (i = 0 ; i < NOPTS ; i++) {
890235723Sbapt			if (optlist[i].val)
891235723Sbapt				STPUTC(optlist[i].letter, expdest);
892235723Sbapt		}
893235723Sbapt		break;
894235723Sbapt	case '@':
895235723Sbapt		if (flag & EXP_FULL && quoted) {
896235723Sbapt			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
897235723Sbapt				STRTODEST(p);
898235723Sbapt				if (*ap)
899235723Sbapt					STPUTC('\0', expdest);
900235723Sbapt			}
901235723Sbapt			break;
902235723Sbapt		}
903235723Sbapt		/* FALLTHROUGH */
904235723Sbapt	case '*':
905235723Sbapt		if (ifsset())
906235723Sbapt			sep = ifsval()[0];
907235723Sbapt		else
908235723Sbapt			sep = ' ';
909235723Sbapt		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
910235723Sbapt			STRTODEST(p);
911235723Sbapt			if (*ap && sep)
912235723Sbapt				STPUTC(sep, expdest);
913235723Sbapt		}
914235723Sbapt		break;
915235723Sbapt	case '0':
916235723Sbapt		p = arg0;
917235723Sbapt		STRTODEST(p);
918235723Sbapt		break;
919235723Sbapt	default:
920235723Sbapt		if (is_digit(*name)) {
921235723Sbapt			num = atoi(name);
922235723Sbapt			if (num > 0 && num <= shellparam.nparam) {
923235723Sbapt				p = shellparam.p[num - 1];
924235723Sbapt				STRTODEST(p);
925235723Sbapt			}
926235723Sbapt		}
927235723Sbapt		break;
928235723Sbapt	}
929235723Sbapt}
930235723Sbapt
931235723Sbapt
932235723Sbapt
933235723Sbapt/*
934235723Sbapt * Record the the fact that we have to scan this region of the
935235723Sbapt * string for IFS characters.
936235723Sbapt */
937235723Sbapt
938235723SbaptSTATIC void
939235723Sbaptrecordregion(int start, int end, int inquotes)
940235723Sbapt{
941235723Sbapt	struct ifsregion *ifsp;
942235723Sbapt
943235723Sbapt	if (ifslastp == NULL) {
944235723Sbapt		ifsp = &ifsfirst;
945235723Sbapt	} else {
946235723Sbapt		if (ifslastp->endoff == start
947235723Sbapt		    && ifslastp->inquotes == inquotes) {
948235723Sbapt			/* extend previous area */
949235723Sbapt			ifslastp->endoff = end;
950235723Sbapt			return;
951235723Sbapt		}
952235723Sbapt		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
953235723Sbapt		ifslastp->next = ifsp;
954235723Sbapt	}
955235723Sbapt	ifslastp = ifsp;
956235723Sbapt	ifslastp->next = NULL;
957235723Sbapt	ifslastp->begoff = start;
958235723Sbapt	ifslastp->endoff = end;
959235723Sbapt	ifslastp->inquotes = inquotes;
960235723Sbapt}
961235723Sbapt
962235723Sbapt
963235723Sbapt
964235723Sbapt/*
965235723Sbapt * Break the argument string into pieces based upon IFS and add the
966235723Sbapt * strings to the argument list.  The regions of the string to be
967235723Sbapt * searched for IFS characters have been stored by recordregion.
968235723Sbapt */
969235723SbaptSTATIC void
970235723Sbaptifsbreakup(char *string, struct arglist *arglist)
971235723Sbapt{
972235723Sbapt	struct ifsregion *ifsp;
973235723Sbapt	struct strlist *sp;
974235723Sbapt	char *start;
975235723Sbapt	char *p;
976235723Sbapt	char *q;
977235723Sbapt	char *ifs;
978235723Sbapt	const char *ifsspc;
979235723Sbapt	int had_param_ch = 0;
980235723Sbapt
981235723Sbapt	start = string;
982235723Sbapt
983235723Sbapt	if (ifslastp == NULL) {
984235723Sbapt		/* Return entire argument, IFS doesn't apply to any of it */
985235723Sbapt		sp = (struct strlist *)stalloc(sizeof *sp);
986235723Sbapt		sp->text = start;
987235723Sbapt		*arglist->lastp = sp;
988235723Sbapt		arglist->lastp = &sp->next;
989235723Sbapt		return;
990235723Sbapt	}
991235723Sbapt
992235723Sbapt	ifs = ifsset() ? ifsval() : " \t\n";
993235723Sbapt
994235723Sbapt	for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
995235723Sbapt		p = string + ifsp->begoff;
996235723Sbapt		while (p < string + ifsp->endoff) {
997235723Sbapt			had_param_ch = 1;
998235723Sbapt			q = p;
999235723Sbapt			if (*p == CTLESC)
1000235723Sbapt				p++;
1001235723Sbapt			if (ifsp->inquotes) {
1002235723Sbapt				/* Only NULs (should be from "$@") end args */
1003235723Sbapt				if (*p != 0) {
1004235723Sbapt					p++;
1005235723Sbapt					continue;
1006235723Sbapt				}
1007262960Sjmmv				ifsspc = NULL;
1008235723Sbapt			} else {
1009235723Sbapt				if (!strchr(ifs, *p)) {
1010235723Sbapt					p++;
1011235723Sbapt					continue;
1012235723Sbapt				}
1013235723Sbapt				had_param_ch = 0;
1014235723Sbapt				ifsspc = strchr(" \t\n", *p);
1015235723Sbapt
1016235723Sbapt				/* Ignore IFS whitespace at start */
1017235723Sbapt				if (q == start && ifsspc != NULL) {
1018235723Sbapt					p++;
1019235723Sbapt					start = p;
1020235723Sbapt					continue;
1021235723Sbapt				}
1022235723Sbapt			}
1023235723Sbapt
1024235723Sbapt			/* Save this argument... */
1025235723Sbapt			*q = '\0';
1026235723Sbapt			sp = (struct strlist *)stalloc(sizeof *sp);
1027235723Sbapt			sp->text = start;
1028235723Sbapt			*arglist->lastp = sp;
1029235723Sbapt			arglist->lastp = &sp->next;
1030235723Sbapt			p++;
1031262960Sjmmv
1032235723Sbapt			if (ifsspc != NULL) {
1033235723Sbapt				/* Ignore further trailing IFS whitespace */
1034235723Sbapt				for (; p < string + ifsp->endoff; p++) {
1035235723Sbapt					q = p;
1036235723Sbapt					if (*p == CTLESC)
1037235723Sbapt						p++;
1038235723Sbapt					if (strchr(ifs, *p) == NULL) {
1039235723Sbapt						p = q;
1040235723Sbapt						break;
1041235723Sbapt					}
1042235723Sbapt					if (strchr(" \t\n", *p) == NULL) {
1043235723Sbapt						p++;
1044235723Sbapt						break;
1045235723Sbapt					}
1046235723Sbapt				}
1047235723Sbapt			}
1048235723Sbapt			start = p;
1049235723Sbapt		}
1050235723Sbapt	}
1051235723Sbapt
1052235723Sbapt	/*
1053235723Sbapt	 * Save anything left as an argument.
1054235723Sbapt	 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
1055235723Sbapt	 * generating 2 arguments, the second of which is empty.
1056235723Sbapt	 * Some recent clarification of the Posix spec say that it
1057235723Sbapt	 * should only generate one....
1058235723Sbapt	 */
1059235723Sbapt	if (had_param_ch || *start != 0) {
1060235723Sbapt		sp = (struct strlist *)stalloc(sizeof *sp);
1061235723Sbapt		sp->text = start;
1062235723Sbapt		*arglist->lastp = sp;
1063235723Sbapt		arglist->lastp = &sp->next;
1064235723Sbapt	}
1065235723Sbapt}
1066235723Sbapt
1067235723Sbapt
1068235723Sbapt
1069235723Sbapt/*
1070235723Sbapt * Expand shell metacharacters.  At this point, the only control characters
1071235723Sbapt * should be escapes.  The results are stored in the list exparg.
1072235723Sbapt */
1073235723Sbapt
1074235723SbaptSTATIC char *expdir;
1075235723Sbapt
1076235723Sbapt
1077235723SbaptSTATIC void
1078235723Sbaptexpandmeta(struct strlist *str, int flag __unused)
1079235723Sbapt{
1080235723Sbapt	char *p;
1081235723Sbapt	struct strlist **savelastp;
1082235723Sbapt	struct strlist *sp;
1083235723Sbapt	char c;
1084235723Sbapt	/* TODO - EXP_REDIR */
1085235723Sbapt
1086235723Sbapt	while (str) {
1087235723Sbapt		if (fflag)
1088235723Sbapt			goto nometa;
1089235723Sbapt		p = str->text;
1090235723Sbapt		for (;;) {			/* fast check for meta chars */
1091235723Sbapt			if ((c = *p++) == '\0')
1092235723Sbapt				goto nometa;
1093235723Sbapt			if (c == '*' || c == '?' || c == '[' || c == '!')
1094235723Sbapt				break;
1095235723Sbapt		}
1096235723Sbapt		savelastp = exparg.lastp;
1097235723Sbapt		INTOFF;
1098235723Sbapt		if (expdir == NULL) {
1099235723Sbapt			int i = strlen(str->text);
1100235723Sbapt			expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
1101235723Sbapt		}
1102235723Sbapt
1103235723Sbapt		expmeta(expdir, str->text);
1104235723Sbapt		ckfree(expdir);
1105262960Sjmmv		expdir = NULL;
1106235723Sbapt		INTON;
1107235723Sbapt		if (exparg.lastp == savelastp) {
1108235723Sbapt			/*
1109235723Sbapt			 * no matches
1110235723Sbapt			 */
1111235723Sbaptnometa:
1112235723Sbapt			*exparg.lastp = str;
1113235723Sbapt			rmescapes(str->text);
1114235723Sbapt			exparg.lastp = &str->next;
1115235723Sbapt		} else {
1116235723Sbapt			*exparg.lastp = NULL;
1117235723Sbapt			*savelastp = sp = expsort(*savelastp);
1118235723Sbapt			while (sp->next != NULL)
1119235723Sbapt				sp = sp->next;
1120235723Sbapt			exparg.lastp = &sp->next;
1121235723Sbapt		}
1122235723Sbapt		str = str->next;
1123235723Sbapt	}
1124235723Sbapt}
1125235723Sbapt
1126235723Sbapt
1127235723Sbapt/*
1128235723Sbapt * Do metacharacter (i.e. *, ?, [...]) expansion.
1129235723Sbapt */
1130235723Sbapt
1131235723SbaptSTATIC void
1132235723Sbaptexpmeta(char *enddir, char *name)
1133235723Sbapt{
1134235723Sbapt	char *p;
1135235723Sbapt	char *q;
1136235723Sbapt	char *start;
1137235723Sbapt	char *endname;
1138235723Sbapt	int metaflag;
1139235723Sbapt	struct stat statb;
1140235723Sbapt	DIR *dirp;
1141235723Sbapt	struct dirent *dp;
1142235723Sbapt	int atend;
1143235723Sbapt	int matchdot;
1144235723Sbapt
1145235723Sbapt	metaflag = 0;
1146235723Sbapt	start = name;
1147235723Sbapt	for (p = name ; ; p++) {
1148235723Sbapt		if (*p == '*' || *p == '?')
1149235723Sbapt			metaflag = 1;
1150235723Sbapt		else if (*p == '[') {
1151235723Sbapt			q = p + 1;
1152235723Sbapt			if (*q == '!' || *q == '^')
1153235723Sbapt				q++;
1154235723Sbapt			for (;;) {
1155235723Sbapt				while (*q == CTLQUOTEMARK)
1156235723Sbapt					q++;
1157235723Sbapt				if (*q == CTLESC)
1158235723Sbapt					q++;
1159235723Sbapt				if (*q == '/' || *q == '\0')
1160235723Sbapt					break;
1161235723Sbapt				if (*++q == ']') {
1162235723Sbapt					metaflag = 1;
1163235723Sbapt					break;
1164235723Sbapt				}
1165235723Sbapt			}
1166235723Sbapt		} else if (*p == '!' && p[1] == '!'	&& (p == name || p[-1] == '/')) {
1167235723Sbapt			metaflag = 1;
1168235723Sbapt		} else if (*p == '\0')
1169235723Sbapt			break;
1170235723Sbapt		else if (*p == CTLQUOTEMARK)
1171235723Sbapt			continue;
1172235723Sbapt		else if (*p == CTLESC)
1173235723Sbapt			p++;
1174235723Sbapt		if (*p == '/') {
1175235723Sbapt			if (metaflag)
1176235723Sbapt				break;
1177235723Sbapt			start = p + 1;
1178235723Sbapt		}
1179235723Sbapt	}
1180235723Sbapt	if (metaflag == 0) {	/* we've reached the end of the file name */
1181235723Sbapt		if (enddir != expdir)
1182235723Sbapt			metaflag++;
1183235723Sbapt		for (p = name ; ; p++) {
1184235723Sbapt			if (*p == CTLQUOTEMARK)
1185235723Sbapt				continue;
1186235723Sbapt			if (*p == CTLESC)
1187262960Sjmmv				p++;
1188235723Sbapt			*enddir++ = *p;
1189235723Sbapt			if (*p == '\0')
1190235723Sbapt				break;
1191235723Sbapt		}
1192235723Sbapt		if (metaflag == 0 || lstat(expdir, &statb) >= 0)
1193235723Sbapt			addfname(expdir);
1194235723Sbapt		return;
1195235723Sbapt	}
1196235723Sbapt	endname = p;
1197235723Sbapt	if (start != name) {
1198235723Sbapt		p = name;
1199235723Sbapt		while (p < start) {
1200235723Sbapt			while (*p == CTLQUOTEMARK)
1201235723Sbapt				p++;
1202235723Sbapt			if (*p == CTLESC)
1203235723Sbapt				p++;
1204235723Sbapt			*enddir++ = *p++;
1205235723Sbapt		}
1206235723Sbapt	}
1207235723Sbapt	if (enddir == expdir) {
1208235723Sbapt		p = ".";
1209235723Sbapt	} else if (enddir == expdir + 1 && *expdir == '/') {
1210235723Sbapt		p = "/";
1211235723Sbapt	} else {
1212235723Sbapt		p = expdir;
1213235723Sbapt		enddir[-1] = '\0';
1214235723Sbapt	}
1215235723Sbapt	if ((dirp = opendir(p)) == NULL)
1216235723Sbapt		return;
1217235723Sbapt	if (enddir != expdir)
1218235723Sbapt		enddir[-1] = '/';
1219235723Sbapt	if (*endname == 0) {
1220235723Sbapt		atend = 1;
1221235723Sbapt	} else {
1222235723Sbapt		atend = 0;
1223235723Sbapt		*endname++ = '\0';
1224235723Sbapt	}
1225235723Sbapt	matchdot = 0;
1226235723Sbapt	p = start;
1227235723Sbapt	while (*p == CTLQUOTEMARK)
1228235723Sbapt		p++;
1229235723Sbapt	if (*p == CTLESC)
1230235723Sbapt		p++;
1231235723Sbapt	if (*p == '.')
1232235723Sbapt		matchdot++;
1233235723Sbapt	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
1234235723Sbapt		if (dp->d_name[0] == '.' && ! matchdot)
1235235723Sbapt			continue;
1236235723Sbapt		if (patmatch(start, dp->d_name, 0)) {
1237235723Sbapt			if (atend) {
1238235723Sbapt				scopy(dp->d_name, enddir);
1239235723Sbapt				addfname(expdir);
1240235723Sbapt			} else {
1241235723Sbapt				for (p = enddir, q = dp->d_name;
1242235723Sbapt				     (*p++ = *q++) != '\0';)
1243235723Sbapt					continue;
1244235723Sbapt				p[-1] = '/';
1245235723Sbapt				expmeta(p, endname);
1246235723Sbapt			}
1247235723Sbapt		}
1248235723Sbapt	}
1249235723Sbapt	closedir(dirp);
1250235723Sbapt	if (! atend)
1251235723Sbapt		endname[-1] = '/';
1252235723Sbapt}
1253235723Sbapt
1254235723Sbapt
1255235723Sbapt/*
1256235723Sbapt * Add a file name to the list.
1257235723Sbapt */
1258235723Sbapt
1259235723SbaptSTATIC void
1260235723Sbaptaddfname(char *name)
1261235723Sbapt{
1262235723Sbapt	char *p;
1263235723Sbapt	struct strlist *sp;
1264235723Sbapt
1265235723Sbapt	p = stalloc(strlen(name) + 1);
1266235723Sbapt	scopy(name, p);
1267235723Sbapt	sp = (struct strlist *)stalloc(sizeof *sp);
1268235723Sbapt	sp->text = p;
1269235723Sbapt	*exparg.lastp = sp;
1270235723Sbapt	exparg.lastp = &sp->next;
1271235723Sbapt}
1272235723Sbapt
1273235723Sbapt
1274235723Sbapt/*
1275235723Sbapt * Sort the results of file name expansion.  It calculates the number of
1276235723Sbapt * strings to sort and then calls msort (short for merge sort) to do the
1277235723Sbapt * work.
1278235723Sbapt */
1279235723Sbapt
1280235723SbaptSTATIC struct strlist *
1281235723Sbaptexpsort(struct strlist *str)
1282235723Sbapt{
1283235723Sbapt	int len;
1284235723Sbapt	struct strlist *sp;
1285235723Sbapt
1286235723Sbapt	len = 0;
1287235723Sbapt	for (sp = str ; sp ; sp = sp->next)
1288235723Sbapt		len++;
1289235723Sbapt	return msort(str, len);
1290235723Sbapt}
1291235723Sbapt
1292235723Sbapt
1293235723SbaptSTATIC struct strlist *
1294235723Sbaptmsort(struct strlist *list, int len)
1295235723Sbapt{
1296235723Sbapt	struct strlist *p, *q = NULL;
1297235723Sbapt	struct strlist **lpp;
1298235723Sbapt	int half;
1299235723Sbapt	int n;
1300235723Sbapt
1301235723Sbapt	if (len <= 1)
1302235723Sbapt		return list;
1303235723Sbapt	half = len >> 1;
1304235723Sbapt	p = list;
1305235723Sbapt	for (n = half ; --n >= 0 ; ) {
1306235723Sbapt		q = p;
1307235723Sbapt		p = p->next;
1308235723Sbapt	}
1309235723Sbapt	q->next = NULL;			/* terminate first half of list */
1310235723Sbapt	q = msort(list, half);		/* sort first half of list */
1311235723Sbapt	p = msort(p, len - half);		/* sort second half */
1312235723Sbapt	lpp = &list;
1313235723Sbapt	for (;;) {
1314235723Sbapt		if (strcmp(p->text, q->text) < 0) {
1315235723Sbapt			*lpp = p;
1316235723Sbapt			lpp = &p->next;
1317235723Sbapt			if ((p = *lpp) == NULL) {
1318235723Sbapt				*lpp = q;
1319235723Sbapt				break;
1320235723Sbapt			}
1321235723Sbapt		} else {
1322235723Sbapt			*lpp = q;
1323235723Sbapt			lpp = &q->next;
1324235723Sbapt			if ((q = *lpp) == NULL) {
1325235723Sbapt				*lpp = p;
1326235723Sbapt				break;
1327235723Sbapt			}
1328235723Sbapt		}
1329235723Sbapt	}
1330235723Sbapt	return list;
1331235723Sbapt}
1332235723Sbapt
1333235723Sbapt
1334235723Sbapt
1335235723Sbapt/*
1336235723Sbapt * Returns true if the pattern matches the string.
1337235723Sbapt */
1338235723Sbapt
1339235723Sbaptint
1340235723Sbaptpatmatch(char *pattern, char *string, int squoted)
1341235723Sbapt{
1342235723Sbapt#ifdef notdef
1343235723Sbapt	if (pattern[0] == '!' && pattern[1] == '!')
1344235723Sbapt		return 1 - pmatch(pattern + 2, string);
1345235723Sbapt	else
1346235723Sbapt#endif
1347235723Sbapt		return pmatch(pattern, string, squoted);
1348235723Sbapt}
1349235723Sbapt
1350235723Sbapt
1351235723SbaptSTATIC int
1352235723Sbaptpmatch(char *pattern, char *string, int squoted)
1353235723Sbapt{
1354235723Sbapt	char *p, *q;
1355235723Sbapt	char c;
1356235723Sbapt
1357235723Sbapt	p = pattern;
1358235723Sbapt	q = string;
1359235723Sbapt	for (;;) {
1360235723Sbapt		switch (c = *p++) {
1361235723Sbapt		case '\0':
1362235723Sbapt			goto breakloop;
1363235723Sbapt		case CTLESC:
1364235723Sbapt			if (squoted && *q == CTLESC)
1365235723Sbapt				q++;
1366235723Sbapt			if (*q++ != *p++)
1367235723Sbapt				return 0;
1368235723Sbapt			break;
1369235723Sbapt		case CTLQUOTEMARK:
1370235723Sbapt			continue;
1371235723Sbapt		case '?':
1372235723Sbapt			if (squoted && *q == CTLESC)
1373235723Sbapt				q++;
1374235723Sbapt			if (*q++ == '\0')
1375235723Sbapt				return 0;
1376235723Sbapt			break;
1377235723Sbapt		case '*':
1378235723Sbapt			c = *p;
1379235723Sbapt			while (c == CTLQUOTEMARK || c == '*')
1380235723Sbapt				c = *++p;
1381235723Sbapt			if (c != CTLESC &&  c != CTLQUOTEMARK &&
1382235723Sbapt			    c != '?' && c != '*' && c != '[') {
1383235723Sbapt				while (*q != c) {
1384235723Sbapt					if (squoted && *q == CTLESC &&
1385235723Sbapt					    q[1] == c)
1386235723Sbapt						break;
1387235723Sbapt					if (*q == '\0')
1388235723Sbapt						return 0;
1389235723Sbapt					if (squoted && *q == CTLESC)
1390235723Sbapt						q++;
1391235723Sbapt					q++;
1392235723Sbapt				}
1393235723Sbapt			}
1394235723Sbapt			do {
1395235723Sbapt				if (pmatch(p, q, squoted))
1396235723Sbapt					return 1;
1397235723Sbapt				if (squoted && *q == CTLESC)
1398235723Sbapt					q++;
1399235723Sbapt			} while (*q++ != '\0');
1400235723Sbapt			return 0;
1401235723Sbapt		case '[': {
1402235723Sbapt			char *endp;
1403235723Sbapt			int invert, found;
1404235723Sbapt			char chr;
1405235723Sbapt
1406235723Sbapt			endp = p;
1407235723Sbapt			if (*endp == '!' || *endp == '^')
1408235723Sbapt				endp++;
1409235723Sbapt			for (;;) {
1410235723Sbapt				while (*endp == CTLQUOTEMARK)
1411235723Sbapt					endp++;
1412235723Sbapt				if (*endp == '\0')
1413235723Sbapt					goto dft;		/* no matching ] */
1414235723Sbapt				if (*endp == CTLESC)
1415235723Sbapt					endp++;
1416235723Sbapt				if (*++endp == ']')
1417235723Sbapt					break;
1418235723Sbapt			}
1419235723Sbapt			invert = 0;
1420235723Sbapt			if (*p == '!' || *p == '^') {
1421235723Sbapt				invert++;
1422235723Sbapt				p++;
1423235723Sbapt			}
1424235723Sbapt			found = 0;
1425235723Sbapt			chr = *q++;
1426235723Sbapt			if (squoted && chr == CTLESC)
1427235723Sbapt				chr = *q++;
1428235723Sbapt			if (chr == '\0')
1429235723Sbapt				return 0;
1430235723Sbapt			c = *p++;
1431235723Sbapt			do {
1432235723Sbapt				if (c == CTLQUOTEMARK)
1433235723Sbapt					continue;
1434235723Sbapt				if (c == CTLESC)
1435235723Sbapt					c = *p++;
1436235723Sbapt				if (*p == '-' && p[1] != ']') {
1437235723Sbapt					p++;
1438235723Sbapt					while (*p == CTLQUOTEMARK)
1439235723Sbapt						p++;
1440235723Sbapt					if (*p == CTLESC)
1441235723Sbapt						p++;
1442235723Sbapt					if (   collate_range_cmp(chr, c) >= 0
1443235723Sbapt					    && collate_range_cmp(chr, *p) <= 0
1444235723Sbapt					   )
1445235723Sbapt						found = 1;
1446235723Sbapt					p++;
1447235723Sbapt				} else {
1448235723Sbapt					if (chr == c)
1449235723Sbapt						found = 1;
1450235723Sbapt				}
1451235723Sbapt			} while ((c = *p++) != ']');
1452235723Sbapt			if (found == invert)
1453235723Sbapt				return 0;
1454235723Sbapt			break;
1455235723Sbapt		}
1456235723Sbaptdft:	        default:
1457235723Sbapt			if (squoted && *q == CTLESC)
1458235723Sbapt				q++;
1459235723Sbapt			if (*q++ != c)
1460235723Sbapt				return 0;
1461235723Sbapt			break;
1462235723Sbapt		}
1463235723Sbapt	}
1464235723Sbaptbreakloop:
1465235723Sbapt	if (*q != '\0')
1466235723Sbapt		return 0;
1467235723Sbapt	return 1;
1468235723Sbapt}
1469235723Sbapt
1470235723Sbapt
1471235723Sbapt
1472235723Sbapt/*
1473235723Sbapt * Remove any CTLESC characters from a string.
1474235723Sbapt */
1475235723Sbapt
1476235723Sbaptvoid
1477235723Sbaptrmescapes(char *str)
1478235723Sbapt{
1479235723Sbapt	char *p, *q;
1480235723Sbapt
1481235723Sbapt	p = str;
1482235723Sbapt	while (*p != CTLESC && *p != CTLQUOTEMARK) {
1483235723Sbapt		if (*p++ == '\0')
1484235723Sbapt			return;
1485235723Sbapt	}
1486235723Sbapt	q = p;
1487235723Sbapt	while (*p) {
1488235723Sbapt		if (*p == CTLQUOTEMARK) {
1489235723Sbapt			p++;
1490235723Sbapt			continue;
1491235723Sbapt		}
1492235723Sbapt		if (*p == CTLESC)
1493235723Sbapt			p++;
1494235723Sbapt		*q++ = *p++;
1495235723Sbapt	}
1496235723Sbapt	*q = '\0';
1497235723Sbapt}
1498235723Sbapt
1499235723Sbapt
1500235723Sbapt
1501235723Sbapt/*
1502235723Sbapt * See if a pattern matches in a case statement.
1503235723Sbapt */
1504235723Sbapt
1505235723Sbaptint
1506235723Sbaptcasematch(union node *pattern, char *val)
1507235723Sbapt{
1508235723Sbapt	struct stackmark smark;
1509235723Sbapt	int result;
1510235723Sbapt	char *p;
1511235723Sbapt
1512235723Sbapt	setstackmark(&smark);
1513235723Sbapt	argbackq = pattern->narg.backquote;
1514235723Sbapt	STARTSTACKSTR(expdest);
1515235723Sbapt	ifslastp = NULL;
1516235723Sbapt	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
1517235723Sbapt	STPUTC('\0', expdest);
1518235723Sbapt	p = grabstackstr(expdest);
1519235723Sbapt	result = patmatch(p, val, 0);
1520235723Sbapt	popstackmark(&smark);
1521235723Sbapt	return result;
1522235723Sbapt}
1523235723Sbapt
1524235723Sbapt/*
1525235723Sbapt * Our own itoa().
1526235723Sbapt */
1527235723Sbapt
1528235723SbaptSTATIC char *
1529235723Sbaptcvtnum(int num, char *buf)
1530235723Sbapt{
1531235723Sbapt	char temp[32];
1532235723Sbapt	int neg = num < 0;
1533235723Sbapt	char *p = temp + 31;
1534235723Sbapt
1535235723Sbapt	temp[31] = '\0';
1536235723Sbapt
1537235723Sbapt	do {
1538235723Sbapt		*--p = num % 10 + '0';
1539235723Sbapt	} while ((num /= 10) != 0);
1540235723Sbapt
1541235723Sbapt	if (neg)
1542235723Sbapt		*--p = '-';
1543235723Sbapt
1544235723Sbapt	while (*p)
1545235723Sbapt		STPUTC(*p++, buf);
1546235723Sbapt	return buf;
1547235723Sbapt}
1548235723Sbapt
1549235723Sbapt/*
1550235723Sbapt * Do most of the work for wordexp(3).
1551235723Sbapt */
1552235723Sbapt
1553235723Sbaptint
1554235723Sbaptwordexpcmd(int argc, char **argv)
1555235723Sbapt{
1556235723Sbapt	size_t len;
1557235723Sbapt	int i;
1558235723Sbapt
1559235723Sbapt	out1fmt("%08x", argc - 1);
1560235723Sbapt	for (i = 1, len = 0; i < argc; i++)
1561235723Sbapt		len += strlen(argv[i]);
1562235723Sbapt	out1fmt("%08x", (int)len);
1563235723Sbapt	for (i = 1; i < argc; i++) {
1564235723Sbapt		out1str(argv[i]);
1565235723Sbapt		out1c('\0');
1566235723Sbapt	}
1567235723Sbapt        return (0);
1568235723Sbapt}
1569235723Sbapt