expand.c revision 211646
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 211646 2010-08-22 21:18:21Z jilles $");
421556Srgrimes
4317987Speter#include <sys/types.h>
4417987Speter#include <sys/time.h>
4517987Speter#include <sys/stat.h>
4617987Speter#include <errno.h>
4717987Speter#include <dirent.h>
4817987Speter#include <unistd.h>
4917987Speter#include <pwd.h>
5017987Speter#include <stdlib.h>
5119281Sache#include <limits.h>
5238887Stegge#include <stdio.h>
53108286Stjr#include <string.h>
5417987Speter
551556Srgrimes/*
561556Srgrimes * Routines to expand arguments to commands.  We have to deal with
571556Srgrimes * backquotes, shell variables, and file metacharacters.
581556Srgrimes */
591556Srgrimes
601556Srgrimes#include "shell.h"
611556Srgrimes#include "main.h"
621556Srgrimes#include "nodes.h"
631556Srgrimes#include "eval.h"
641556Srgrimes#include "expand.h"
651556Srgrimes#include "syntax.h"
661556Srgrimes#include "parser.h"
671556Srgrimes#include "jobs.h"
681556Srgrimes#include "options.h"
691556Srgrimes#include "var.h"
701556Srgrimes#include "input.h"
711556Srgrimes#include "output.h"
721556Srgrimes#include "memalloc.h"
731556Srgrimes#include "error.h"
741556Srgrimes#include "mystring.h"
7517987Speter#include "arith.h"
7617987Speter#include "show.h"
771556Srgrimes
781556Srgrimes/*
791556Srgrimes * Structure specifying which parts of the string should be searched
801556Srgrimes * for IFS characters.
811556Srgrimes */
821556Srgrimes
831556Srgrimesstruct ifsregion {
841556Srgrimes	struct ifsregion *next;	/* next region in list */
851556Srgrimes	int begoff;		/* offset of start of region */
861556Srgrimes	int endoff;		/* offset of end of region */
87194975Sjilles	int inquotes;		/* search for nul bytes only */
881556Srgrimes};
891556Srgrimes
901556Srgrimes
91117261SddsSTATIC char *expdest;			/* output of current string */
92117261SddsSTATIC struct nodelist *argbackq;	/* list of back quote expressions */
93117261SddsSTATIC struct ifsregion ifsfirst;	/* first struct in list of ifs regions */
94117261SddsSTATIC struct ifsregion *ifslastp;	/* last struct in list */
95117261SddsSTATIC struct arglist exparg;		/* holds expanded arg list */
961556Srgrimes
9790111SimpSTATIC void argstr(char *, int);
9890111SimpSTATIC char *exptilde(char *, int);
9990111SimpSTATIC void expbackq(union node *, int, int);
10090111SimpSTATIC int subevalvar(char *, char *, int, int, int, int);
10190111SimpSTATIC char *evalvar(char *, int);
10290111SimpSTATIC int varisset(char *, int);
103164081SstefanfSTATIC void varvalue(char *, int, int, int);
10490111SimpSTATIC void recordregion(int, int, int);
105155301SschweikhSTATIC void removerecordregions(int);
10690111SimpSTATIC void ifsbreakup(char *, struct arglist *);
10790111SimpSTATIC void expandmeta(struct strlist *, int);
10890111SimpSTATIC void expmeta(char *, char *);
10990111SimpSTATIC void addfname(char *);
11090111SimpSTATIC struct strlist *expsort(struct strlist *);
11190111SimpSTATIC struct strlist *msort(struct strlist *, int);
11290111SimpSTATIC char *cvtnum(int, char *);
11390111SimpSTATIC int collate_range_cmp(int, int);
1141556Srgrimes
11590111SimpSTATIC int
116118374Sachecollate_range_cmp(int c1, int c2)
11719281Sache{
11819281Sache	static char s1[2], s2[2];
11919281Sache
12019281Sache	s1[0] = c1;
12119281Sache	s2[0] = c2;
122118374Sache	return (strcoll(s1, s2));
12319281Sache}
12419281Sache
1251556Srgrimes/*
1261556Srgrimes * Expand shell variables and backquotes inside a here document.
12790111Simp *	union node *arg		the document
12890111Simp *	int fd;			where to write the expanded version
1291556Srgrimes */
1301556Srgrimes
1311556Srgrimesvoid
13290111Simpexpandhere(union node *arg, int fd)
13390111Simp{
1341556Srgrimes	herefd = fd;
1351556Srgrimes	expandarg(arg, (struct arglist *)NULL, 0);
13639137Stegge	xwrite(fd, stackblock(), expdest - stackblock());
1371556Srgrimes}
1381556Srgrimes
1391556Srgrimes
1401556Srgrimes/*
1411556Srgrimes * Perform variable substitution and command substitution on an argument,
1421556Srgrimes * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
1431556Srgrimes * perform splitting and file name expansion.  When arglist is NULL, perform
1441556Srgrimes * here document expansion.
1451556Srgrimes */
1461556Srgrimes
1471556Srgrimesvoid
14890111Simpexpandarg(union node *arg, struct arglist *arglist, int flag)
14917987Speter{
1501556Srgrimes	struct strlist *sp;
1511556Srgrimes	char *p;
1521556Srgrimes
1531556Srgrimes	argbackq = arg->narg.backquote;
1541556Srgrimes	STARTSTACKSTR(expdest);
1551556Srgrimes	ifsfirst.next = NULL;
1561556Srgrimes	ifslastp = NULL;
1571556Srgrimes	argstr(arg->narg.text, flag);
1581556Srgrimes	if (arglist == NULL) {
1591556Srgrimes		return;			/* here document expanded */
1601556Srgrimes	}
1611556Srgrimes	STPUTC('\0', expdest);
1621556Srgrimes	p = grabstackstr(expdest);
1631556Srgrimes	exparg.lastp = &exparg.list;
1641556Srgrimes	/*
1651556Srgrimes	 * TODO - EXP_REDIR
1661556Srgrimes	 */
1671556Srgrimes	if (flag & EXP_FULL) {
1681556Srgrimes		ifsbreakup(p, &exparg);
1691556Srgrimes		*exparg.lastp = NULL;
1701556Srgrimes		exparg.lastp = &exparg.list;
1711556Srgrimes		expandmeta(exparg.list, flag);
1721556Srgrimes	} else {
1731556Srgrimes		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
1741556Srgrimes			rmescapes(p);
1751556Srgrimes		sp = (struct strlist *)stalloc(sizeof (struct strlist));
1761556Srgrimes		sp->text = p;
1771556Srgrimes		*exparg.lastp = sp;
1781556Srgrimes		exparg.lastp = &sp->next;
1791556Srgrimes	}
1801556Srgrimes	while (ifsfirst.next != NULL) {
1811556Srgrimes		struct ifsregion *ifsp;
1821556Srgrimes		INTOFF;
1831556Srgrimes		ifsp = ifsfirst.next->next;
1841556Srgrimes		ckfree(ifsfirst.next);
1851556Srgrimes		ifsfirst.next = ifsp;
1861556Srgrimes		INTON;
1871556Srgrimes	}
1881556Srgrimes	*exparg.lastp = NULL;
1891556Srgrimes	if (exparg.list) {
1901556Srgrimes		*arglist->lastp = exparg.list;
1911556Srgrimes		arglist->lastp = exparg.lastp;
1921556Srgrimes	}
1931556Srgrimes}
1941556Srgrimes
1951556Srgrimes
1961556Srgrimes
1971556Srgrimes/*
1981556Srgrimes * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
1991556Srgrimes * characters to allow for further processing.  Otherwise treat
2001556Srgrimes * $@ like $* since no splitting will be performed.
2011556Srgrimes */
2021556Srgrimes
2031556SrgrimesSTATIC void
20490111Simpargstr(char *p, int flag)
20517987Speter{
20625233Ssteve	char c;
207104672Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);	/* do CTLESC */
2081556Srgrimes	int firsteq = 1;
2091556Srgrimes
2101556Srgrimes	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
2111556Srgrimes		p = exptilde(p, flag);
2121556Srgrimes	for (;;) {
2131556Srgrimes		switch (c = *p++) {
2141556Srgrimes		case '\0':
2151556Srgrimes		case CTLENDVAR: /* ??? */
2161556Srgrimes			goto breakloop;
21738887Stegge		case CTLQUOTEMARK:
21838887Stegge			/* "$@" syntax adherence hack */
21938887Stegge			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
22038887Stegge				break;
22139137Stegge			if ((flag & EXP_FULL) != 0)
22239137Stegge				STPUTC(c, expdest);
22338887Stegge			break;
2241556Srgrimes		case CTLESC:
2251556Srgrimes			if (quotes)
2261556Srgrimes				STPUTC(c, expdest);
2271556Srgrimes			c = *p++;
2281556Srgrimes			STPUTC(c, expdest);
2291556Srgrimes			break;
2301556Srgrimes		case CTLVAR:
2311556Srgrimes			p = evalvar(p, flag);
2321556Srgrimes			break;
2331556Srgrimes		case CTLBACKQ:
2341556Srgrimes		case CTLBACKQ|CTLQUOTE:
2351556Srgrimes			expbackq(argbackq->n, c & CTLQUOTE, flag);
2361556Srgrimes			argbackq = argbackq->next;
2371556Srgrimes			break;
2381556Srgrimes		case CTLENDARI:
2391556Srgrimes			expari(flag);
2401556Srgrimes			break;
2411556Srgrimes		case ':':
2421556Srgrimes		case '=':
2431556Srgrimes			/*
2441556Srgrimes			 * sort of a hack - expand tildes in variable
2451556Srgrimes			 * assignments (after the first '=' and after ':'s).
2461556Srgrimes			 */
2471556Srgrimes			STPUTC(c, expdest);
2481556Srgrimes			if (flag & EXP_VARTILDE && *p == '~') {
2491556Srgrimes				if (c == '=') {
2501556Srgrimes					if (firsteq)
2511556Srgrimes						firsteq = 0;
2521556Srgrimes					else
2531556Srgrimes						break;
2541556Srgrimes				}
2551556Srgrimes				p = exptilde(p, flag);
2561556Srgrimes			}
2571556Srgrimes			break;
2581556Srgrimes		default:
2591556Srgrimes			STPUTC(c, expdest);
2601556Srgrimes		}
2611556Srgrimes	}
2621556Srgrimesbreakloop:;
2631556Srgrimes}
2641556Srgrimes
2651556SrgrimesSTATIC char *
26690111Simpexptilde(char *p, int flag)
26717987Speter{
2681556Srgrimes	char c, *startp = p;
2691556Srgrimes	struct passwd *pw;
2701556Srgrimes	char *home;
271108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
2721556Srgrimes
27317987Speter	while ((c = *p) != '\0') {
2741556Srgrimes		switch(c) {
275200988Sjilles		case CTLESC: /* This means CTL* are always considered quoted. */
276200988Sjilles		case CTLVAR:
277200988Sjilles		case CTLBACKQ:
278200988Sjilles		case CTLBACKQ | CTLQUOTE:
279200988Sjilles		case CTLARI:
280200988Sjilles		case CTLENDARI:
28139137Stegge		case CTLQUOTEMARK:
28239137Stegge			return (startp);
2831556Srgrimes		case ':':
2841556Srgrimes			if (flag & EXP_VARTILDE)
2851556Srgrimes				goto done;
2861556Srgrimes			break;
2871556Srgrimes		case '/':
288206150Sjilles		case CTLENDVAR:
2891556Srgrimes			goto done;
2901556Srgrimes		}
2911556Srgrimes		p++;
2921556Srgrimes	}
2931556Srgrimesdone:
2941556Srgrimes	*p = '\0';
2951556Srgrimes	if (*(startp+1) == '\0') {
2961556Srgrimes		if ((home = lookupvar("HOME")) == NULL)
2971556Srgrimes			goto lose;
2981556Srgrimes	} else {
2991556Srgrimes		if ((pw = getpwnam(startp+1)) == NULL)
3001556Srgrimes			goto lose;
3011556Srgrimes		home = pw->pw_dir;
3021556Srgrimes	}
3031556Srgrimes	if (*home == '\0')
3041556Srgrimes		goto lose;
3051556Srgrimes	*p = c;
30617987Speter	while ((c = *home++) != '\0') {
30783675Stegge		if (quotes && SQSYNTAX[(int)c] == CCTL)
3081556Srgrimes			STPUTC(CTLESC, expdest);
3091556Srgrimes		STPUTC(c, expdest);
3101556Srgrimes	}
3111556Srgrimes	return (p);
3121556Srgrimeslose:
3131556Srgrimes	*p = c;
3141556Srgrimes	return (startp);
3151556Srgrimes}
3161556Srgrimes
3171556Srgrimes
318155301SschweikhSTATIC void
31990111Simpremoverecordregions(int endoff)
32038887Stegge{
32138887Stegge	if (ifslastp == NULL)
32238887Stegge		return;
32338887Stegge
32438887Stegge	if (ifsfirst.endoff > endoff) {
32538887Stegge		while (ifsfirst.next != NULL) {
32638887Stegge			struct ifsregion *ifsp;
32738887Stegge			INTOFF;
32838887Stegge			ifsp = ifsfirst.next->next;
32938887Stegge			ckfree(ifsfirst.next);
33038887Stegge			ifsfirst.next = ifsp;
33138887Stegge			INTON;
33238887Stegge		}
33338887Stegge		if (ifsfirst.begoff > endoff)
33438887Stegge			ifslastp = NULL;
33538887Stegge		else {
33638887Stegge			ifslastp = &ifsfirst;
33738887Stegge			ifsfirst.endoff = endoff;
33838887Stegge		}
33938887Stegge		return;
34038887Stegge	}
341155301Sschweikh
34238887Stegge	ifslastp = &ifsfirst;
34338887Stegge	while (ifslastp->next && ifslastp->next->begoff < endoff)
34438887Stegge		ifslastp=ifslastp->next;
34538887Stegge	while (ifslastp->next != NULL) {
34638887Stegge		struct ifsregion *ifsp;
34738887Stegge		INTOFF;
34838887Stegge		ifsp = ifslastp->next->next;
34938887Stegge		ckfree(ifslastp->next);
35038887Stegge		ifslastp->next = ifsp;
35138887Stegge		INTON;
35238887Stegge	}
35338887Stegge	if (ifslastp->endoff > endoff)
35438887Stegge		ifslastp->endoff = endoff;
35538887Stegge}
35638887Stegge
3571556Srgrimes/*
3581556Srgrimes * Expand arithmetic expression.  Backup to start of expression,
3591556Srgrimes * evaluate, place result in (backed up) result, adjust string position.
3601556Srgrimes */
3611556Srgrimesvoid
36290111Simpexpari(int flag)
36317987Speter{
364207206Sjilles	char *p, *q, *start;
365178631Sstefanf	arith_t result;
36638887Stegge	int begoff;
367108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
36838887Stegge	int quoted;
3691556Srgrimes
37025233Ssteve
3711556Srgrimes	/*
37246684Skris	 * This routine is slightly over-complicated for
3731556Srgrimes	 * efficiency.  First we make sure there is
3741556Srgrimes	 * enough space for the result, which may be bigger
37546684Skris	 * than the expression if we add exponentiation.  Next we
3761556Srgrimes	 * scan backwards looking for the start of arithmetic.  If the
3771556Srgrimes	 * next previous character is a CTLESC character, then we
3781556Srgrimes	 * have to rescan starting from the beginning since CTLESC
3798855Srgrimes	 * characters have to be processed left to right.
3801556Srgrimes	 */
381178631Sstefanf	CHECKSTRSPACE(DIGITS(result) - 2, expdest);
3828855Srgrimes	USTPUTC('\0', expdest);
3831556Srgrimes	start = stackblock();
38483676Stegge	p = expdest - 2;
38583676Stegge	while (p >= start && *p != CTLARI)
3861556Srgrimes		--p;
38783676Stegge	if (p < start || *p != CTLARI)
3881556Srgrimes		error("missing CTLARI (shouldn't happen)");
38983676Stegge	if (p > start && *(p - 1) == CTLESC)
3901556Srgrimes		for (p = start; *p != CTLARI; p++)
3911556Srgrimes			if (*p == CTLESC)
3921556Srgrimes				p++;
39338887Stegge
39438887Stegge	if (p[1] == '"')
39538887Stegge		quoted=1;
39638887Stegge	else
39738887Stegge		quoted=0;
39838887Stegge	begoff = p - start;
39938887Stegge	removerecordregions(begoff);
4001556Srgrimes	if (quotes)
40138887Stegge		rmescapes(p+2);
402207206Sjilles	q = grabstackstr(expdest);
40338887Stegge	result = arith(p+2);
404207206Sjilles	ungrabstackstr(q, expdest);
405178631Sstefanf	fmtstr(p, DIGITS(result), ARITH_FORMAT_STR, result);
4061556Srgrimes	while (*p++)
4071556Srgrimes		;
40838887Stegge	if (quoted == 0)
40938887Stegge		recordregion(begoff, p - 1 - start, 0);
4101556Srgrimes	result = expdest - p + 1;
4111556Srgrimes	STADJUST(-result, expdest);
4121556Srgrimes}
4131556Srgrimes
4141556Srgrimes
4151556Srgrimes/*
4161556Srgrimes * Expand stuff in backwards quotes.
4171556Srgrimes */
4181556Srgrimes
4191556SrgrimesSTATIC void
42090111Simpexpbackq(union node *cmd, int quoted, int flag)
42117987Speter{
4221556Srgrimes	struct backcmd in;
4231556Srgrimes	int i;
4241556Srgrimes	char buf[128];
4251556Srgrimes	char *p;
4261556Srgrimes	char *dest = expdest;
4271556Srgrimes	struct ifsregion saveifs, *savelastp;
4281556Srgrimes	struct nodelist *saveargbackq;
4291556Srgrimes	char lastc;
4301556Srgrimes	int startloc = dest - stackblock();
4311556Srgrimes	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
4321556Srgrimes	int saveherefd;
433108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
434115424Sfenner	int nnl;
4351556Srgrimes
4361556Srgrimes	INTOFF;
4371556Srgrimes	saveifs = ifsfirst;
4381556Srgrimes	savelastp = ifslastp;
4391556Srgrimes	saveargbackq = argbackq;
4408855Srgrimes	saveherefd = herefd;
4411556Srgrimes	herefd = -1;
4421556Srgrimes	p = grabstackstr(dest);
4431556Srgrimes	evalbackcmd(cmd, &in);
4441556Srgrimes	ungrabstackstr(p, dest);
4451556Srgrimes	ifsfirst = saveifs;
4461556Srgrimes	ifslastp = savelastp;
4471556Srgrimes	argbackq = saveargbackq;
4481556Srgrimes	herefd = saveherefd;
4491556Srgrimes
4501556Srgrimes	p = in.buf;
4511556Srgrimes	lastc = '\0';
452115424Sfenner	nnl = 0;
453115424Sfenner	/* Don't copy trailing newlines */
4541556Srgrimes	for (;;) {
4551556Srgrimes		if (--in.nleft < 0) {
4561556Srgrimes			if (in.fd < 0)
4571556Srgrimes				break;
4581556Srgrimes			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
4591556Srgrimes			TRACE(("expbackq: read returns %d\n", i));
4601556Srgrimes			if (i <= 0)
4611556Srgrimes				break;
4621556Srgrimes			p = buf;
4631556Srgrimes			in.nleft = i - 1;
4641556Srgrimes		}
4651556Srgrimes		lastc = *p++;
4661556Srgrimes		if (lastc != '\0') {
46783675Stegge			if (quotes && syntax[(int)lastc] == CCTL)
4681556Srgrimes				STPUTC(CTLESC, dest);
469115424Sfenner			if (lastc == '\n') {
470115424Sfenner				nnl++;
471115424Sfenner			} else {
472115424Sfenner				while (nnl > 0) {
473115424Sfenner					nnl--;
474115424Sfenner					STPUTC('\n', dest);
475115424Sfenner				}
476115424Sfenner				STPUTC(lastc, dest);
477115424Sfenner			}
4781556Srgrimes		}
4791556Srgrimes	}
48017987Speter
4811556Srgrimes	if (in.fd >= 0)
4821556Srgrimes		close(in.fd);
4831556Srgrimes	if (in.buf)
4841556Srgrimes		ckfree(in.buf);
4851556Srgrimes	if (in.jp)
48645916Scracauer		exitstatus = waitforjob(in.jp, (int *)NULL);
4871556Srgrimes	if (quoted == 0)
4881556Srgrimes		recordregion(startloc, dest - stackblock(), 0);
4891556Srgrimes	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
4901556Srgrimes		(dest - stackblock()) - startloc,
4911556Srgrimes		(dest - stackblock()) - startloc,
4921556Srgrimes		stackblock() + startloc));
4931556Srgrimes	expdest = dest;
4941556Srgrimes	INTON;
4951556Srgrimes}
4961556Srgrimes
4971556Srgrimes
4981556Srgrimes
49917987SpeterSTATIC int
50090111Simpsubevalvar(char *p, char *str, int strloc, int subtype, int startloc,
50190111Simp  int varflags)
50217987Speter{
50317987Speter	char *startp;
50417987Speter	char *loc = NULL;
50545514Stegge	char *q;
50617987Speter	int c = 0;
50717987Speter	int saveherefd = herefd;
50817987Speter	struct nodelist *saveargbackq = argbackq;
50920425Ssteve	int amount;
51020425Ssteve
51117987Speter	herefd = -1;
512206150Sjilles	argstr(p, (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX ||
513206147Sjilles	    subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX ?
514206150Sjilles	    EXP_CASE : 0) | EXP_TILDE);
51517987Speter	STACKSTRNUL(expdest);
51617987Speter	herefd = saveherefd;
51717987Speter	argbackq = saveargbackq;
51817987Speter	startp = stackblock() + startloc;
51920425Ssteve	if (str == NULL)
52020425Ssteve	    str = stackblock() + strloc;
52117987Speter
52217987Speter	switch (subtype) {
52317987Speter	case VSASSIGN:
52417987Speter		setvar(str, startp, 0);
52520425Ssteve		amount = startp - expdest;
52620425Ssteve		STADJUST(amount, expdest);
52717987Speter		varflags &= ~VSNUL;
52817987Speter		if (c != 0)
52917987Speter			*loc = c;
53017987Speter		return 1;
53117987Speter
53217987Speter	case VSQUESTION:
53317987Speter		if (*p != CTLENDVAR) {
534201366Sjilles			outfmt(out2, "%s\n", startp);
53517987Speter			error((char *)NULL);
53617987Speter		}
537112254Sru		error("%.*s: parameter %snot set", (int)(p - str - 1),
53817987Speter		      str, (varflags & VSNUL) ? "null or "
53917987Speter					      : nullstr);
54017987Speter		return 0;
54117987Speter
54217987Speter	case VSTRIMLEFT:
54325233Ssteve		for (loc = startp; loc < str; loc++) {
54417987Speter			c = *loc;
54517987Speter			*loc = '\0';
54645514Stegge			if (patmatch(str, startp, varflags & VSQUOTE)) {
54717987Speter				*loc = c;
54817987Speter				goto recordleft;
54917987Speter			}
55017987Speter			*loc = c;
55145514Stegge			if ((varflags & VSQUOTE) && *loc == CTLESC)
55245514Stegge				loc++;
55317987Speter		}
55417987Speter		return 0;
55517987Speter
55617987Speter	case VSTRIMLEFTMAX:
55745514Stegge		for (loc = str - 1; loc >= startp;) {
55817987Speter			c = *loc;
55917987Speter			*loc = '\0';
56045514Stegge			if (patmatch(str, startp, varflags & VSQUOTE)) {
56117987Speter				*loc = c;
56217987Speter				goto recordleft;
56317987Speter			}
56417987Speter			*loc = c;
56545514Stegge			loc--;
56645514Stegge			if ((varflags & VSQUOTE) && loc > startp &&
56745514Stegge			    *(loc - 1) == CTLESC) {
56845514Stegge				for (q = startp; q < loc; q++)
56945514Stegge					if (*q == CTLESC)
57045514Stegge						q++;
57145514Stegge				if (q > loc)
57245514Stegge					loc--;
57345514Stegge			}
57417987Speter		}
57517987Speter		return 0;
57617987Speter
57717987Speter	case VSTRIMRIGHT:
57845514Stegge		for (loc = str - 1; loc >= startp;) {
57945514Stegge			if (patmatch(str, loc, varflags & VSQUOTE)) {
58020425Ssteve				amount = loc - expdest;
58120425Ssteve				STADJUST(amount, expdest);
58217987Speter				return 1;
58317987Speter			}
58445514Stegge			loc--;
58545514Stegge			if ((varflags & VSQUOTE) && loc > startp &&
586155301Sschweikh			    *(loc - 1) == CTLESC) {
58745514Stegge				for (q = startp; q < loc; q++)
58845514Stegge					if (*q == CTLESC)
58945514Stegge						q++;
59045514Stegge				if (q > loc)
59145514Stegge					loc--;
59245514Stegge			}
59317987Speter		}
59417987Speter		return 0;
59517987Speter
59617987Speter	case VSTRIMRIGHTMAX:
59717987Speter		for (loc = startp; loc < str - 1; loc++) {
59845514Stegge			if (patmatch(str, loc, varflags & VSQUOTE)) {
59920425Ssteve				amount = loc - expdest;
60020425Ssteve				STADJUST(amount, expdest);
60117987Speter				return 1;
60217987Speter			}
60345514Stegge			if ((varflags & VSQUOTE) && *loc == CTLESC)
60445514Stegge				loc++;
60517987Speter		}
60617987Speter		return 0;
60717987Speter
60817987Speter
60917987Speter	default:
61017987Speter		abort();
61117987Speter	}
61217987Speter
61317987Speterrecordleft:
61420425Ssteve	amount = ((str - 1) - (loc - startp)) - expdest;
61520425Ssteve	STADJUST(amount, expdest);
61617987Speter	while (loc != str - 1)
61717987Speter		*startp++ = *loc++;
61817987Speter	return 1;
61917987Speter}
62017987Speter
62117987Speter
6221556Srgrimes/*
6231556Srgrimes * Expand a variable, and return a pointer to the next character in the
6241556Srgrimes * input string.
6251556Srgrimes */
6261556Srgrimes
6271556SrgrimesSTATIC char *
62890111Simpevalvar(char *p, int flag)
62917987Speter{
6301556Srgrimes	int subtype;
6311556Srgrimes	int varflags;
6321556Srgrimes	char *var;
6331556Srgrimes	char *val;
63445644Stegge	int patloc;
6351556Srgrimes	int c;
6361556Srgrimes	int set;
6371556Srgrimes	int special;
6381556Srgrimes	int startloc;
63917987Speter	int varlen;
64017987Speter	int easy;
641108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
6421556Srgrimes
643164081Sstefanf	varflags = (unsigned char)*p++;
6441556Srgrimes	subtype = varflags & VSTYPE;
6451556Srgrimes	var = p;
6461556Srgrimes	special = 0;
6471556Srgrimes	if (! is_name(*p))
6481556Srgrimes		special = 1;
6491556Srgrimes	p = strchr(p, '=') + 1;
6501556Srgrimesagain: /* jump here after setting a variable with ${var=text} */
651179022Sstefanf	if (varflags & VSLINENO) {
652179022Sstefanf		set = 1;
653179022Sstefanf		special = 0;
654179022Sstefanf		val = var;
655179022Sstefanf		p[-1] = '\0';	/* temporarily overwrite '=' to have \0
656179022Sstefanf				   terminated string */
657179022Sstefanf	} else if (special) {
65825233Ssteve		set = varisset(var, varflags & VSNUL);
6591556Srgrimes		val = NULL;
6601556Srgrimes	} else {
66160592Scracauer		val = bltinlookup(var, 1);
66217987Speter		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
6631556Srgrimes			val = NULL;
6641556Srgrimes			set = 0;
6651556Srgrimes		} else
6661556Srgrimes			set = 1;
6671556Srgrimes	}
66817987Speter	varlen = 0;
6691556Srgrimes	startloc = expdest - stackblock();
670198454Sjilles	if (!set && uflag && *var != '@' && *var != '*') {
67196939Stjr		switch (subtype) {
67296939Stjr		case VSNORMAL:
67396939Stjr		case VSTRIMLEFT:
67496939Stjr		case VSTRIMLEFTMAX:
67596939Stjr		case VSTRIMRIGHT:
67696939Stjr		case VSTRIMRIGHTMAX:
67796939Stjr		case VSLENGTH:
678112254Sru			error("%.*s: parameter not set", (int)(p - var - 1),
679112254Sru			    var);
68096939Stjr		}
68196939Stjr	}
6821556Srgrimes	if (set && subtype != VSPLUS) {
6831556Srgrimes		/* insert the value of the variable */
6841556Srgrimes		if (special) {
685164081Sstefanf			varvalue(var, varflags & VSQUOTE, subtype, flag);
68617987Speter			if (subtype == VSLENGTH) {
68725233Ssteve				varlen = expdest - stackblock() - startloc;
68825233Ssteve				STADJUST(-varlen, expdest);
68917987Speter			}
6901556Srgrimes		} else {
69120425Ssteve			char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
69217987Speter								  : BASESYNTAX;
6931556Srgrimes
69417987Speter			if (subtype == VSLENGTH) {
69517987Speter				for (;*val; val++)
69617987Speter					varlen++;
6971556Srgrimes			}
69817987Speter			else {
69917987Speter				while (*val) {
70083675Stegge					if (quotes &&
70154132Scracauer					    syntax[(int)*val] == CCTL)
70217987Speter						STPUTC(CTLESC, expdest);
70317987Speter					STPUTC(*val++, expdest);
70417987Speter				}
70517987Speter
70617987Speter			}
7071556Srgrimes		}
7081556Srgrimes	}
70920425Ssteve
7101556Srgrimes	if (subtype == VSPLUS)
7111556Srgrimes		set = ! set;
71217987Speter
71320425Ssteve	easy = ((varflags & VSQUOTE) == 0 ||
71417987Speter		(*var == '@' && shellparam.nparam != 1));
71517987Speter
71617987Speter
71717987Speter	switch (subtype) {
71817987Speter	case VSLENGTH:
71917987Speter		expdest = cvtnum(varlen, expdest);
72017987Speter		goto record;
72117987Speter
72217987Speter	case VSNORMAL:
72317987Speter		if (!easy)
72417987Speter			break;
72517987Speterrecord:
72620425Ssteve		recordregion(startloc, expdest - stackblock(),
72717987Speter			     varflags & VSQUOTE);
72817987Speter		break;
72917987Speter
73017987Speter	case VSPLUS:
73117987Speter	case VSMINUS:
73217987Speter		if (!set) {
7331556Srgrimes			argstr(p, flag);
73417987Speter			break;
73517987Speter		}
73617987Speter		if (easy)
73717987Speter			goto record;
73817987Speter		break;
73917987Speter
74017987Speter	case VSTRIMLEFT:
74117987Speter	case VSTRIMLEFTMAX:
74217987Speter	case VSTRIMRIGHT:
74317987Speter	case VSTRIMRIGHTMAX:
74417987Speter		if (!set)
74517987Speter			break;
74617987Speter		/*
74717987Speter		 * Terminate the string and start recording the pattern
74817987Speter		 * right after it
74917987Speter		 */
75017987Speter		STPUTC('\0', expdest);
75145644Stegge		patloc = expdest - stackblock();
75245644Stegge		if (subevalvar(p, NULL, patloc, subtype,
75338887Stegge			       startloc, varflags) == 0) {
75445644Stegge			int amount = (expdest - stackblock() - patloc) + 1;
75525233Ssteve			STADJUST(-amount, expdest);
75625233Ssteve		}
75738887Stegge		/* Remove any recorded regions beyond start of variable */
75838887Stegge		removerecordregions(startloc);
75938887Stegge		goto record;
76017987Speter
76117987Speter	case VSASSIGN:
76217987Speter	case VSQUESTION:
76317987Speter		if (!set) {
76420425Ssteve			if (subevalvar(p, var, 0, subtype, startloc, varflags)) {
76520425Ssteve				varflags &= ~VSNUL;
766155301Sschweikh				/*
767155301Sschweikh				 * Remove any recorded regions beyond
768155301Sschweikh				 * start of variable
76938887Stegge				 */
77038887Stegge				removerecordregions(startloc);
7711556Srgrimes				goto again;
77220425Ssteve			}
77317987Speter			break;
7741556Srgrimes		}
77517987Speter		if (easy)
77617987Speter			goto record;
77717987Speter		break;
77817987Speter
779164003Sstefanf	case VSERROR:
780164003Sstefanf		c = p - var - 1;
781164003Sstefanf		error("${%.*s%s}: Bad substitution", c, var,
782164003Sstefanf		    (c > 0 && *p != CTLENDVAR) ? "..." : "");
783164003Sstefanf
78417987Speter	default:
78517987Speter		abort();
7861556Srgrimes	}
787179022Sstefanf	p[-1] = '=';	/* recover overwritten '=' */
78817987Speter
7891556Srgrimes	if (subtype != VSNORMAL) {	/* skip to end of alternative */
7901556Srgrimes		int nesting = 1;
7911556Srgrimes		for (;;) {
7921556Srgrimes			if ((c = *p++) == CTLESC)
7931556Srgrimes				p++;
7941556Srgrimes			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
7951556Srgrimes				if (set)
7961556Srgrimes					argbackq = argbackq->next;
7971556Srgrimes			} else if (c == CTLVAR) {
7981556Srgrimes				if ((*p++ & VSTYPE) != VSNORMAL)
7991556Srgrimes					nesting++;
8001556Srgrimes			} else if (c == CTLENDVAR) {
8011556Srgrimes				if (--nesting == 0)
8021556Srgrimes					break;
8031556Srgrimes			}
8041556Srgrimes		}
8051556Srgrimes	}
8061556Srgrimes	return p;
8071556Srgrimes}
8081556Srgrimes
8091556Srgrimes
8101556Srgrimes
8111556Srgrimes/*
8121556Srgrimes * Test whether a specialized variable is set.
8131556Srgrimes */
8141556Srgrimes
8151556SrgrimesSTATIC int
81690111Simpvarisset(char *name, int nulok)
81725233Ssteve{
8181556Srgrimes
81925233Ssteve	if (*name == '!')
820209600Sjilles		return backgndpidset();
82125233Ssteve	else if (*name == '@' || *name == '*') {
8221556Srgrimes		if (*shellparam.p == NULL)
8231556Srgrimes			return 0;
82425233Ssteve
82525233Ssteve		if (nulok) {
82625233Ssteve			char **av;
82725233Ssteve
82825233Ssteve			for (av = shellparam.p; *av; av++)
82925233Ssteve				if (**av != '\0')
83025233Ssteve					return 1;
83125233Ssteve			return 0;
83225233Ssteve		}
83318202Speter	} else if (is_digit(*name)) {
83425233Ssteve		char *ap;
83520425Ssteve		int num = atoi(name);
83625233Ssteve
83725233Ssteve		if (num > shellparam.nparam)
83825233Ssteve			return 0;
83925233Ssteve
84025233Ssteve		if (num == 0)
84125233Ssteve			ap = arg0;
84225233Ssteve		else
84325233Ssteve			ap = shellparam.p[num - 1];
84425233Ssteve
84525233Ssteve		if (nulok && (ap == NULL || *ap == '\0'))
84625233Ssteve			return 0;
8471556Srgrimes	}
8481556Srgrimes	return 1;
8491556Srgrimes}
8501556Srgrimes
8511556Srgrimes
8521556Srgrimes
8531556Srgrimes/*
8541556Srgrimes * Add the value of a specialized variable to the stack string.
8551556Srgrimes */
8561556Srgrimes
8571556SrgrimesSTATIC void
858164081Sstefanfvarvalue(char *name, int quoted, int subtype, int flag)
85917987Speter{
8601556Srgrimes	int num;
8611556Srgrimes	char *p;
8621556Srgrimes	int i;
8631556Srgrimes	char sep;
8641556Srgrimes	char **ap;
8651556Srgrimes	char const *syntax;
8661556Srgrimes
8671556Srgrimes#define STRTODEST(p) \
8681556Srgrimes	do {\
869164081Sstefanf	if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) { \
8701556Srgrimes		syntax = quoted? DQSYNTAX : BASESYNTAX; \
8711556Srgrimes		while (*p) { \
87283675Stegge			if (syntax[(int)*p] == CCTL) \
8731556Srgrimes				STPUTC(CTLESC, expdest); \
8741556Srgrimes			STPUTC(*p++, expdest); \
8751556Srgrimes		} \
8761556Srgrimes	} else \
8771556Srgrimes		while (*p) \
8781556Srgrimes			STPUTC(*p++, expdest); \
8791556Srgrimes	} while (0)
8801556Srgrimes
8811556Srgrimes
88218202Speter	switch (*name) {
8831556Srgrimes	case '$':
8841556Srgrimes		num = rootpid;
8851556Srgrimes		goto numvar;
8861556Srgrimes	case '?':
88717987Speter		num = oexitstatus;
8881556Srgrimes		goto numvar;
8891556Srgrimes	case '#':
8901556Srgrimes		num = shellparam.nparam;
8911556Srgrimes		goto numvar;
8921556Srgrimes	case '!':
893209600Sjilles		num = backgndpidval();
8941556Srgrimesnumvar:
89517987Speter		expdest = cvtnum(num, expdest);
8961556Srgrimes		break;
8971556Srgrimes	case '-':
8981556Srgrimes		for (i = 0 ; i < NOPTS ; i++) {
8991556Srgrimes			if (optlist[i].val)
9001556Srgrimes				STPUTC(optlist[i].letter, expdest);
9011556Srgrimes		}
9021556Srgrimes		break;
9031556Srgrimes	case '@':
904164081Sstefanf		if (flag & EXP_FULL && quoted) {
90538887Stegge			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
90638887Stegge				STRTODEST(p);
90738887Stegge				if (*ap)
90838887Stegge					STPUTC('\0', expdest);
90938887Stegge			}
91038887Stegge			break;
9111556Srgrimes		}
912102410Scharnier		/* FALLTHROUGH */
9131556Srgrimes	case '*':
914149825Srse		if (ifsset())
91538887Stegge			sep = ifsval()[0];
91638887Stegge		else
91738887Stegge			sep = ' ';
9181556Srgrimes		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
9191556Srgrimes			STRTODEST(p);
92038887Stegge			if (*ap && sep)
9211556Srgrimes				STPUTC(sep, expdest);
9221556Srgrimes		}
9231556Srgrimes		break;
9241556Srgrimes	case '0':
9251556Srgrimes		p = arg0;
9261556Srgrimes		STRTODEST(p);
9271556Srgrimes		break;
9281556Srgrimes	default:
92918202Speter		if (is_digit(*name)) {
93018202Speter			num = atoi(name);
93118202Speter			if (num > 0 && num <= shellparam.nparam) {
93218202Speter				p = shellparam.p[num - 1];
93318202Speter				STRTODEST(p);
93418202Speter			}
9351556Srgrimes		}
9361556Srgrimes		break;
9371556Srgrimes	}
9381556Srgrimes}
9391556Srgrimes
9401556Srgrimes
9411556Srgrimes
9421556Srgrimes/*
9431556Srgrimes * Record the the fact that we have to scan this region of the
9441556Srgrimes * string for IFS characters.
9451556Srgrimes */
9461556Srgrimes
9471556SrgrimesSTATIC void
948194975Sjillesrecordregion(int start, int end, int inquotes)
94917987Speter{
95025233Ssteve	struct ifsregion *ifsp;
9511556Srgrimes
9521556Srgrimes	if (ifslastp == NULL) {
9531556Srgrimes		ifsp = &ifsfirst;
9541556Srgrimes	} else {
955194975Sjilles		if (ifslastp->endoff == start
956194975Sjilles		    && ifslastp->inquotes == inquotes) {
957194975Sjilles			/* extend previous area */
958194975Sjilles			ifslastp->endoff = end;
959194975Sjilles			return;
960194975Sjilles		}
9611556Srgrimes		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
9621556Srgrimes		ifslastp->next = ifsp;
9631556Srgrimes	}
9641556Srgrimes	ifslastp = ifsp;
9651556Srgrimes	ifslastp->next = NULL;
9661556Srgrimes	ifslastp->begoff = start;
9671556Srgrimes	ifslastp->endoff = end;
968194975Sjilles	ifslastp->inquotes = inquotes;
9691556Srgrimes}
9701556Srgrimes
9711556Srgrimes
9721556Srgrimes
9731556Srgrimes/*
9741556Srgrimes * Break the argument string into pieces based upon IFS and add the
9751556Srgrimes * strings to the argument list.  The regions of the string to be
9761556Srgrimes * searched for IFS characters have been stored by recordregion.
9771556Srgrimes */
9781556SrgrimesSTATIC void
97990111Simpifsbreakup(char *string, struct arglist *arglist)
98090111Simp{
9811556Srgrimes	struct ifsregion *ifsp;
9821556Srgrimes	struct strlist *sp;
9831556Srgrimes	char *start;
98425233Ssteve	char *p;
9851556Srgrimes	char *q;
986201053Sjilles	const char *ifs;
987194975Sjilles	const char *ifsspc;
988194975Sjilles	int had_param_ch = 0;
9891556Srgrimes
990194975Sjilles	start = string;
99117987Speter
992194975Sjilles	if (ifslastp == NULL) {
993194975Sjilles		/* Return entire argument, IFS doesn't apply to any of it */
994194975Sjilles		sp = (struct strlist *)stalloc(sizeof *sp);
995194975Sjilles		sp->text = start;
996194975Sjilles		*arglist->lastp = sp;
997194975Sjilles		arglist->lastp = &sp->next;
998194975Sjilles		return;
999194975Sjilles	}
1000194975Sjilles
1001194975Sjilles	ifs = ifsset() ? ifsval() : " \t\n";
1002194975Sjilles
1003194975Sjilles	for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
1004194975Sjilles		p = string + ifsp->begoff;
1005194975Sjilles		while (p < string + ifsp->endoff) {
1006194975Sjilles			q = p;
1007194975Sjilles			if (*p == CTLESC)
1008194975Sjilles				p++;
1009194975Sjilles			if (ifsp->inquotes) {
1010194975Sjilles				/* Only NULs (should be from "$@") end args */
1011194977Sjilles				had_param_ch = 1;
1012194975Sjilles				if (*p != 0) {
10131556Srgrimes					p++;
1014194975Sjilles					continue;
1015194975Sjilles				}
1016194975Sjilles				ifsspc = NULL;
1017194975Sjilles			} else {
1018194975Sjilles				if (!strchr(ifs, *p)) {
1019194977Sjilles					had_param_ch = 1;
102038887Stegge					p++;
1021194975Sjilles					continue;
1022194975Sjilles				}
1023194975Sjilles				ifsspc = strchr(" \t\n", *p);
1024194975Sjilles
1025194975Sjilles				/* Ignore IFS whitespace at start */
1026194975Sjilles				if (q == start && ifsspc != NULL) {
1027194975Sjilles					p++;
10281556Srgrimes					start = p;
1029194975Sjilles					continue;
1030194975Sjilles				}
1031194977Sjilles				had_param_ch = 0;
10321556Srgrimes			}
1033194975Sjilles
1034194975Sjilles			/* Save this argument... */
1035194975Sjilles			*q = '\0';
10361556Srgrimes			sp = (struct strlist *)stalloc(sizeof *sp);
10371556Srgrimes			sp->text = start;
10381556Srgrimes			*arglist->lastp = sp;
10391556Srgrimes			arglist->lastp = &sp->next;
1040194975Sjilles			p++;
1041194975Sjilles
1042194975Sjilles			if (ifsspc != NULL) {
1043194975Sjilles				/* Ignore further trailing IFS whitespace */
1044194975Sjilles				for (; p < string + ifsp->endoff; p++) {
1045194975Sjilles					q = p;
1046194975Sjilles					if (*p == CTLESC)
1047194975Sjilles						p++;
1048194975Sjilles					if (strchr(ifs, *p) == NULL) {
1049194975Sjilles						p = q;
1050194975Sjilles						break;
1051194975Sjilles					}
1052194975Sjilles					if (strchr(" \t\n", *p) == NULL) {
1053194975Sjilles						p++;
1054194975Sjilles						break;
1055194975Sjilles					}
1056194975Sjilles				}
1057194975Sjilles			}
1058194975Sjilles			start = p;
10591556Srgrimes		}
1060194975Sjilles	}
1061194975Sjilles
1062194975Sjilles	/*
1063194975Sjilles	 * Save anything left as an argument.
1064194975Sjilles	 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
1065194975Sjilles	 * generating 2 arguments, the second of which is empty.
1066194975Sjilles	 * Some recent clarification of the Posix spec say that it
1067194975Sjilles	 * should only generate one....
1068194975Sjilles	 */
1069194975Sjilles	if (had_param_ch || *start != 0) {
10701556Srgrimes		sp = (struct strlist *)stalloc(sizeof *sp);
10711556Srgrimes		sp->text = start;
10721556Srgrimes		*arglist->lastp = sp;
10731556Srgrimes		arglist->lastp = &sp->next;
10741556Srgrimes	}
10751556Srgrimes}
10761556Srgrimes
10771556Srgrimes
10781556Srgrimes
10791556Srgrimes/*
10801556Srgrimes * Expand shell metacharacters.  At this point, the only control characters
10811556Srgrimes * should be escapes.  The results are stored in the list exparg.
10821556Srgrimes */
10831556Srgrimes
1084211155SjillesSTATIC char expdir[PATH_MAX];
1085211155Sjilles#define expdir_end (expdir + sizeof(expdir))
10861556Srgrimes
10871556SrgrimesSTATIC void
108890111Simpexpandmeta(struct strlist *str, int flag __unused)
108917987Speter{
10901556Srgrimes	char *p;
10911556Srgrimes	struct strlist **savelastp;
10921556Srgrimes	struct strlist *sp;
10931556Srgrimes	char c;
10941556Srgrimes	/* TODO - EXP_REDIR */
10951556Srgrimes
10961556Srgrimes	while (str) {
10971556Srgrimes		if (fflag)
10981556Srgrimes			goto nometa;
10991556Srgrimes		p = str->text;
11001556Srgrimes		for (;;) {			/* fast check for meta chars */
11011556Srgrimes			if ((c = *p++) == '\0')
11021556Srgrimes				goto nometa;
1103211646Sjilles			if (c == '*' || c == '?' || c == '[')
11041556Srgrimes				break;
11051556Srgrimes		}
11061556Srgrimes		savelastp = exparg.lastp;
11071556Srgrimes		INTOFF;
11081556Srgrimes		expmeta(expdir, str->text);
11091556Srgrimes		INTON;
11101556Srgrimes		if (exparg.lastp == savelastp) {
11118855Srgrimes			/*
11128855Srgrimes			 * no matches
11131556Srgrimes			 */
11141556Srgrimesnometa:
11151556Srgrimes			*exparg.lastp = str;
11161556Srgrimes			rmescapes(str->text);
11171556Srgrimes			exparg.lastp = &str->next;
11181556Srgrimes		} else {
11191556Srgrimes			*exparg.lastp = NULL;
11201556Srgrimes			*savelastp = sp = expsort(*savelastp);
11211556Srgrimes			while (sp->next != NULL)
11221556Srgrimes				sp = sp->next;
11231556Srgrimes			exparg.lastp = &sp->next;
11241556Srgrimes		}
11251556Srgrimes		str = str->next;
11261556Srgrimes	}
11271556Srgrimes}
11281556Srgrimes
11291556Srgrimes
11301556Srgrimes/*
11311556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion.
11321556Srgrimes */
11331556Srgrimes
11341556SrgrimesSTATIC void
113590111Simpexpmeta(char *enddir, char *name)
113690111Simp{
113725233Ssteve	char *p;
11381556Srgrimes	char *q;
11391556Srgrimes	char *start;
11401556Srgrimes	char *endname;
11411556Srgrimes	int metaflag;
11421556Srgrimes	struct stat statb;
11431556Srgrimes	DIR *dirp;
11441556Srgrimes	struct dirent *dp;
11451556Srgrimes	int atend;
11461556Srgrimes	int matchdot;
1147207944Sjilles	int esc;
11481556Srgrimes
11491556Srgrimes	metaflag = 0;
11501556Srgrimes	start = name;
1151207944Sjilles	for (p = name; esc = 0, *p; p += esc + 1) {
11521556Srgrimes		if (*p == '*' || *p == '?')
11531556Srgrimes			metaflag = 1;
11541556Srgrimes		else if (*p == '[') {
11551556Srgrimes			q = p + 1;
115626488Sache			if (*q == '!' || *q == '^')
11571556Srgrimes				q++;
11581556Srgrimes			for (;;) {
115938887Stegge				while (*q == CTLQUOTEMARK)
116038887Stegge					q++;
11611556Srgrimes				if (*q == CTLESC)
11621556Srgrimes					q++;
11631556Srgrimes				if (*q == '/' || *q == '\0')
11641556Srgrimes					break;
11651556Srgrimes				if (*++q == ']') {
11661556Srgrimes					metaflag = 1;
11671556Srgrimes					break;
11681556Srgrimes				}
11691556Srgrimes			}
11701556Srgrimes		} else if (*p == '\0')
11711556Srgrimes			break;
117238887Stegge		else if (*p == CTLQUOTEMARK)
117338887Stegge			continue;
1174207944Sjilles		else {
1175207944Sjilles			if (*p == CTLESC)
1176207944Sjilles				esc++;
1177207944Sjilles			if (p[esc] == '/') {
1178207944Sjilles				if (metaflag)
1179207944Sjilles					break;
1180207944Sjilles				start = p + esc + 1;
1181207944Sjilles			}
11821556Srgrimes		}
11831556Srgrimes	}
11841556Srgrimes	if (metaflag == 0) {	/* we've reached the end of the file name */
11851556Srgrimes		if (enddir != expdir)
11861556Srgrimes			metaflag++;
11871556Srgrimes		for (p = name ; ; p++) {
118838887Stegge			if (*p == CTLQUOTEMARK)
118938887Stegge				continue;
11901556Srgrimes			if (*p == CTLESC)
11911556Srgrimes				p++;
11921556Srgrimes			*enddir++ = *p;
11931556Srgrimes			if (*p == '\0')
11941556Srgrimes				break;
1195211155Sjilles			if (enddir == expdir_end)
1196211155Sjilles				return;
11971556Srgrimes		}
1198147812Sdelphij		if (metaflag == 0 || lstat(expdir, &statb) >= 0)
11991556Srgrimes			addfname(expdir);
12001556Srgrimes		return;
12011556Srgrimes	}
12021556Srgrimes	endname = p;
12031556Srgrimes	if (start != name) {
12041556Srgrimes		p = name;
12051556Srgrimes		while (p < start) {
120638887Stegge			while (*p == CTLQUOTEMARK)
120738887Stegge				p++;
12081556Srgrimes			if (*p == CTLESC)
12091556Srgrimes				p++;
12101556Srgrimes			*enddir++ = *p++;
1211211155Sjilles			if (enddir == expdir_end)
1212211155Sjilles				return;
12131556Srgrimes		}
12141556Srgrimes	}
12151556Srgrimes	if (enddir == expdir) {
12161556Srgrimes		p = ".";
12171556Srgrimes	} else if (enddir == expdir + 1 && *expdir == '/') {
12181556Srgrimes		p = "/";
12191556Srgrimes	} else {
12201556Srgrimes		p = expdir;
12211556Srgrimes		enddir[-1] = '\0';
12221556Srgrimes	}
12231556Srgrimes	if ((dirp = opendir(p)) == NULL)
12241556Srgrimes		return;
12251556Srgrimes	if (enddir != expdir)
12261556Srgrimes		enddir[-1] = '/';
12271556Srgrimes	if (*endname == 0) {
12281556Srgrimes		atend = 1;
12291556Srgrimes	} else {
12301556Srgrimes		atend = 0;
1231207944Sjilles		*endname = '\0';
1232207944Sjilles		endname += esc + 1;
12331556Srgrimes	}
12341556Srgrimes	matchdot = 0;
123538887Stegge	p = start;
123638887Stegge	while (*p == CTLQUOTEMARK)
123738887Stegge		p++;
123838887Stegge	if (*p == CTLESC)
123938887Stegge		p++;
124038887Stegge	if (*p == '.')
12411556Srgrimes		matchdot++;
12421556Srgrimes	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
12431556Srgrimes		if (dp->d_name[0] == '.' && ! matchdot)
12441556Srgrimes			continue;
124545514Stegge		if (patmatch(start, dp->d_name, 0)) {
1246211155Sjilles			if (enddir + dp->d_namlen + 1 > expdir_end)
1247211155Sjilles				continue;
1248211155Sjilles			memcpy(enddir, dp->d_name, dp->d_namlen + 1);
1249211155Sjilles			if (atend)
12501556Srgrimes				addfname(expdir);
1251211155Sjilles			else {
1252211155Sjilles				if (enddir + dp->d_namlen + 2 > expdir_end)
125317987Speter					continue;
1254211155Sjilles				enddir[dp->d_namlen] = '/';
1255211155Sjilles				enddir[dp->d_namlen + 1] = '\0';
1256211155Sjilles				expmeta(enddir + dp->d_namlen + 1, endname);
12571556Srgrimes			}
12581556Srgrimes		}
12591556Srgrimes	}
12601556Srgrimes	closedir(dirp);
12611556Srgrimes	if (! atend)
1262207944Sjilles		endname[-esc - 1] = esc ? CTLESC : '/';
12631556Srgrimes}
12641556Srgrimes
12651556Srgrimes
12661556Srgrimes/*
12671556Srgrimes * Add a file name to the list.
12681556Srgrimes */
12691556Srgrimes
12701556SrgrimesSTATIC void
127190111Simpaddfname(char *name)
127290111Simp{
12731556Srgrimes	char *p;
12741556Srgrimes	struct strlist *sp;
12751556Srgrimes
12761556Srgrimes	p = stalloc(strlen(name) + 1);
12771556Srgrimes	scopy(name, p);
12781556Srgrimes	sp = (struct strlist *)stalloc(sizeof *sp);
12791556Srgrimes	sp->text = p;
12801556Srgrimes	*exparg.lastp = sp;
12811556Srgrimes	exparg.lastp = &sp->next;
12821556Srgrimes}
12831556Srgrimes
12841556Srgrimes
12851556Srgrimes/*
12861556Srgrimes * Sort the results of file name expansion.  It calculates the number of
12871556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the
12881556Srgrimes * work.
12891556Srgrimes */
12901556Srgrimes
12911556SrgrimesSTATIC struct strlist *
129290111Simpexpsort(struct strlist *str)
129390111Simp{
12941556Srgrimes	int len;
12951556Srgrimes	struct strlist *sp;
12961556Srgrimes
12971556Srgrimes	len = 0;
12981556Srgrimes	for (sp = str ; sp ; sp = sp->next)
12991556Srgrimes		len++;
13001556Srgrimes	return msort(str, len);
13011556Srgrimes}
13021556Srgrimes
13031556Srgrimes
13041556SrgrimesSTATIC struct strlist *
130590111Simpmsort(struct strlist *list, int len)
130617987Speter{
130717987Speter	struct strlist *p, *q = NULL;
13081556Srgrimes	struct strlist **lpp;
13091556Srgrimes	int half;
13101556Srgrimes	int n;
13111556Srgrimes
13121556Srgrimes	if (len <= 1)
13131556Srgrimes		return list;
13148855Srgrimes	half = len >> 1;
13151556Srgrimes	p = list;
13161556Srgrimes	for (n = half ; --n >= 0 ; ) {
13171556Srgrimes		q = p;
13181556Srgrimes		p = p->next;
13191556Srgrimes	}
13201556Srgrimes	q->next = NULL;			/* terminate first half of list */
13211556Srgrimes	q = msort(list, half);		/* sort first half of list */
13221556Srgrimes	p = msort(p, len - half);		/* sort second half */
13231556Srgrimes	lpp = &list;
13241556Srgrimes	for (;;) {
13251556Srgrimes		if (strcmp(p->text, q->text) < 0) {
13261556Srgrimes			*lpp = p;
13271556Srgrimes			lpp = &p->next;
13281556Srgrimes			if ((p = *lpp) == NULL) {
13291556Srgrimes				*lpp = q;
13301556Srgrimes				break;
13311556Srgrimes			}
13321556Srgrimes		} else {
13331556Srgrimes			*lpp = q;
13341556Srgrimes			lpp = &q->next;
13351556Srgrimes			if ((q = *lpp) == NULL) {
13361556Srgrimes				*lpp = p;
13371556Srgrimes				break;
13381556Srgrimes			}
13391556Srgrimes		}
13401556Srgrimes	}
13411556Srgrimes	return list;
13421556Srgrimes}
13431556Srgrimes
13441556Srgrimes
13451556Srgrimes
13461556Srgrimes/*
13471556Srgrimes * Returns true if the pattern matches the string.
13481556Srgrimes */
13491556Srgrimes
13501556Srgrimesint
1351200956Sjillespatmatch(const char *pattern, const char *string, int squoted)
135290111Simp{
1353200956Sjilles	const char *p, *q;
135425233Ssteve	char c;
13551556Srgrimes
13561556Srgrimes	p = pattern;
13571556Srgrimes	q = string;
13581556Srgrimes	for (;;) {
13591556Srgrimes		switch (c = *p++) {
13601556Srgrimes		case '\0':
13611556Srgrimes			goto breakloop;
13621556Srgrimes		case CTLESC:
136345514Stegge			if (squoted && *q == CTLESC)
136445514Stegge				q++;
13651556Srgrimes			if (*q++ != *p++)
13661556Srgrimes				return 0;
13671556Srgrimes			break;
136838887Stegge		case CTLQUOTEMARK:
136938887Stegge			continue;
13701556Srgrimes		case '?':
137145514Stegge			if (squoted && *q == CTLESC)
137245514Stegge				q++;
13731556Srgrimes			if (*q++ == '\0')
13741556Srgrimes				return 0;
13751556Srgrimes			break;
13761556Srgrimes		case '*':
13771556Srgrimes			c = *p;
137838887Stegge			while (c == CTLQUOTEMARK || c == '*')
137938887Stegge				c = *++p;
138038887Stegge			if (c != CTLESC &&  c != CTLQUOTEMARK &&
138138887Stegge			    c != '?' && c != '*' && c != '[') {
13821556Srgrimes				while (*q != c) {
138345514Stegge					if (squoted && *q == CTLESC &&
138445514Stegge					    q[1] == c)
138545514Stegge						break;
13861556Srgrimes					if (*q == '\0')
13871556Srgrimes						return 0;
138845514Stegge					if (squoted && *q == CTLESC)
138945514Stegge						q++;
13901556Srgrimes					q++;
13911556Srgrimes				}
13921556Srgrimes			}
13931556Srgrimes			do {
1394211646Sjilles				if (patmatch(p, q, squoted))
13951556Srgrimes					return 1;
139645514Stegge				if (squoted && *q == CTLESC)
139745514Stegge					q++;
13981556Srgrimes			} while (*q++ != '\0');
13991556Srgrimes			return 0;
14001556Srgrimes		case '[': {
1401200956Sjilles			const char *endp;
14021556Srgrimes			int invert, found;
14031556Srgrimes			char chr;
14041556Srgrimes
14051556Srgrimes			endp = p;
140626488Sache			if (*endp == '!' || *endp == '^')
14071556Srgrimes				endp++;
14081556Srgrimes			for (;;) {
140938887Stegge				while (*endp == CTLQUOTEMARK)
141038887Stegge					endp++;
14111556Srgrimes				if (*endp == '\0')
14121556Srgrimes					goto dft;		/* no matching ] */
14131556Srgrimes				if (*endp == CTLESC)
14141556Srgrimes					endp++;
14151556Srgrimes				if (*++endp == ']')
14161556Srgrimes					break;
14171556Srgrimes			}
14181556Srgrimes			invert = 0;
141926488Sache			if (*p == '!' || *p == '^') {
14201556Srgrimes				invert++;
14211556Srgrimes				p++;
14221556Srgrimes			}
14231556Srgrimes			found = 0;
14241556Srgrimes			chr = *q++;
142545514Stegge			if (squoted && chr == CTLESC)
142645514Stegge				chr = *q++;
142717987Speter			if (chr == '\0')
142817987Speter				return 0;
14291556Srgrimes			c = *p++;
14301556Srgrimes			do {
143138887Stegge				if (c == CTLQUOTEMARK)
143238887Stegge					continue;
14331556Srgrimes				if (c == CTLESC)
14341556Srgrimes					c = *p++;
14351556Srgrimes				if (*p == '-' && p[1] != ']') {
14361556Srgrimes					p++;
143738887Stegge					while (*p == CTLQUOTEMARK)
143838887Stegge						p++;
14391556Srgrimes					if (*p == CTLESC)
14401556Srgrimes						p++;
144117557Sache					if (   collate_range_cmp(chr, c) >= 0
144217557Sache					    && collate_range_cmp(chr, *p) <= 0
144317525Sache					   )
14441556Srgrimes						found = 1;
14451556Srgrimes					p++;
14461556Srgrimes				} else {
14471556Srgrimes					if (chr == c)
14481556Srgrimes						found = 1;
14491556Srgrimes				}
14501556Srgrimes			} while ((c = *p++) != ']');
14511556Srgrimes			if (found == invert)
14521556Srgrimes				return 0;
14531556Srgrimes			break;
14541556Srgrimes		}
14551556Srgrimesdft:	        default:
145645514Stegge			if (squoted && *q == CTLESC)
145745514Stegge				q++;
14581556Srgrimes			if (*q++ != c)
14591556Srgrimes				return 0;
14601556Srgrimes			break;
14611556Srgrimes		}
14621556Srgrimes	}
14631556Srgrimesbreakloop:
14641556Srgrimes	if (*q != '\0')
14651556Srgrimes		return 0;
14661556Srgrimes	return 1;
14671556Srgrimes}
14681556Srgrimes
14691556Srgrimes
14701556Srgrimes
14711556Srgrimes/*
14721556Srgrimes * Remove any CTLESC characters from a string.
14731556Srgrimes */
14741556Srgrimes
14751556Srgrimesvoid
147690111Simprmescapes(char *str)
147738887Stegge{
147825233Ssteve	char *p, *q;
14791556Srgrimes
14801556Srgrimes	p = str;
148138887Stegge	while (*p != CTLESC && *p != CTLQUOTEMARK) {
14821556Srgrimes		if (*p++ == '\0')
14831556Srgrimes			return;
14841556Srgrimes	}
14851556Srgrimes	q = p;
14861556Srgrimes	while (*p) {
148738887Stegge		if (*p == CTLQUOTEMARK) {
148838887Stegge			p++;
148938887Stegge			continue;
149038887Stegge		}
14911556Srgrimes		if (*p == CTLESC)
14921556Srgrimes			p++;
14931556Srgrimes		*q++ = *p++;
14941556Srgrimes	}
14951556Srgrimes	*q = '\0';
14961556Srgrimes}
14971556Srgrimes
14981556Srgrimes
14991556Srgrimes
15001556Srgrimes/*
15011556Srgrimes * See if a pattern matches in a case statement.
15021556Srgrimes */
15031556Srgrimes
15041556Srgrimesint
1505200956Sjillescasematch(union node *pattern, const char *val)
150690111Simp{
15071556Srgrimes	struct stackmark smark;
15081556Srgrimes	int result;
15091556Srgrimes	char *p;
15101556Srgrimes
15111556Srgrimes	setstackmark(&smark);
15121556Srgrimes	argbackq = pattern->narg.backquote;
15131556Srgrimes	STARTSTACKSTR(expdest);
15141556Srgrimes	ifslastp = NULL;
15151556Srgrimes	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
15161556Srgrimes	STPUTC('\0', expdest);
15171556Srgrimes	p = grabstackstr(expdest);
151845514Stegge	result = patmatch(p, val, 0);
15191556Srgrimes	popstackmark(&smark);
15201556Srgrimes	return result;
15211556Srgrimes}
152217987Speter
152317987Speter/*
152417987Speter * Our own itoa().
152517987Speter */
152617987Speter
152717987SpeterSTATIC char *
152890111Simpcvtnum(int num, char *buf)
152990111Simp{
153017987Speter	char temp[32];
153117987Speter	int neg = num < 0;
153217987Speter	char *p = temp + 31;
153317987Speter
153417987Speter	temp[31] = '\0';
153517987Speter
153617987Speter	do {
153717987Speter		*--p = num % 10 + '0';
153817987Speter	} while ((num /= 10) != 0);
153917987Speter
154017987Speter	if (neg)
154117987Speter		*--p = '-';
154217987Speter
154317987Speter	while (*p)
154417987Speter		STPUTC(*p++, buf);
154517987Speter	return buf;
154617987Speter}
1547108286Stjr
1548108286Stjr/*
1549108286Stjr * Do most of the work for wordexp(3).
1550108286Stjr */
1551108286Stjr
1552108286Stjrint
1553108286Stjrwordexpcmd(int argc, char **argv)
1554108286Stjr{
1555108286Stjr	size_t len;
1556108286Stjr	int i;
1557108286Stjr
1558108286Stjr	out1fmt("%08x", argc - 1);
1559108286Stjr	for (i = 1, len = 0; i < argc; i++)
1560108286Stjr		len += strlen(argv[i]);
1561108286Stjr	out1fmt("%08x", (int)len);
1562108286Stjr	for (i = 1; i < argc; i++) {
1563108286Stjr		out1str(argv[i]);
1564108286Stjr		out1c('\0');
1565108286Stjr	}
1566108286Stjr        return (0);
1567108286Stjr}
1568