expand.c revision 194975
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Kenneth Almquist.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 4. Neither the name of the University nor the names of its contributors
171556Srgrimes *    may be used to endorse or promote products derived from this software
181556Srgrimes *    without specific prior written permission.
191556Srgrimes *
201556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301556Srgrimes * SUCH DAMAGE.
311556Srgrimes */
321556Srgrimes
331556Srgrimes#ifndef lint
3436150Scharnier#if 0
3536150Scharnierstatic char sccsid[] = "@(#)expand.c	8.5 (Berkeley) 5/15/95";
3636150Scharnier#endif
371556Srgrimes#endif /* not lint */
3899110Sobrien#include <sys/cdefs.h>
3999110Sobrien__FBSDID("$FreeBSD: head/bin/sh/expand.c 194975 2009-06-25 17:10:51Z jilles $");
401556Srgrimes
4117987Speter#include <sys/types.h>
4217987Speter#include <sys/time.h>
4317987Speter#include <sys/stat.h>
4417987Speter#include <errno.h>
4517987Speter#include <dirent.h>
4617987Speter#include <unistd.h>
4717987Speter#include <pwd.h>
4817987Speter#include <stdlib.h>
4919281Sache#include <limits.h>
5038887Stegge#include <stdio.h>
51108286Stjr#include <string.h>
5217987Speter
531556Srgrimes/*
541556Srgrimes * Routines to expand arguments to commands.  We have to deal with
551556Srgrimes * backquotes, shell variables, and file metacharacters.
561556Srgrimes */
571556Srgrimes
581556Srgrimes#include "shell.h"
591556Srgrimes#include "main.h"
601556Srgrimes#include "nodes.h"
611556Srgrimes#include "eval.h"
621556Srgrimes#include "expand.h"
631556Srgrimes#include "syntax.h"
641556Srgrimes#include "parser.h"
651556Srgrimes#include "jobs.h"
661556Srgrimes#include "options.h"
671556Srgrimes#include "var.h"
681556Srgrimes#include "input.h"
691556Srgrimes#include "output.h"
701556Srgrimes#include "memalloc.h"
711556Srgrimes#include "error.h"
721556Srgrimes#include "mystring.h"
7317987Speter#include "arith.h"
7417987Speter#include "show.h"
751556Srgrimes
761556Srgrimes/*
771556Srgrimes * Structure specifying which parts of the string should be searched
781556Srgrimes * for IFS characters.
791556Srgrimes */
801556Srgrimes
811556Srgrimesstruct ifsregion {
821556Srgrimes	struct ifsregion *next;	/* next region in list */
831556Srgrimes	int begoff;		/* offset of start of region */
841556Srgrimes	int endoff;		/* offset of end of region */
85194975Sjilles	int inquotes;		/* search for nul bytes only */
861556Srgrimes};
871556Srgrimes
881556Srgrimes
89117261SddsSTATIC char *expdest;			/* output of current string */
90117261SddsSTATIC struct nodelist *argbackq;	/* list of back quote expressions */
91117261SddsSTATIC struct ifsregion ifsfirst;	/* first struct in list of ifs regions */
92117261SddsSTATIC struct ifsregion *ifslastp;	/* last struct in list */
93117261SddsSTATIC struct arglist exparg;		/* holds expanded arg list */
941556Srgrimes
9590111SimpSTATIC void argstr(char *, int);
9690111SimpSTATIC char *exptilde(char *, int);
9790111SimpSTATIC void expbackq(union node *, int, int);
9890111SimpSTATIC int subevalvar(char *, char *, int, int, int, int);
9990111SimpSTATIC char *evalvar(char *, int);
10090111SimpSTATIC int varisset(char *, int);
101164081SstefanfSTATIC void varvalue(char *, int, int, int);
10290111SimpSTATIC void recordregion(int, int, int);
103155301SschweikhSTATIC void removerecordregions(int);
10490111SimpSTATIC void ifsbreakup(char *, struct arglist *);
10590111SimpSTATIC void expandmeta(struct strlist *, int);
10690111SimpSTATIC void expmeta(char *, char *);
10790111SimpSTATIC void addfname(char *);
10890111SimpSTATIC struct strlist *expsort(struct strlist *);
10990111SimpSTATIC struct strlist *msort(struct strlist *, int);
11090111SimpSTATIC int pmatch(char *, char *, int);
11190111SimpSTATIC char *cvtnum(int, char *);
11290111SimpSTATIC int collate_range_cmp(int, int);
1131556Srgrimes
11490111SimpSTATIC int
115118374Sachecollate_range_cmp(int c1, int c2)
11619281Sache{
11719281Sache	static char s1[2], s2[2];
11819281Sache
11919281Sache	s1[0] = c1;
12019281Sache	s2[0] = c2;
121118374Sache	return (strcoll(s1, s2));
12219281Sache}
12319281Sache
1241556Srgrimes/*
1251556Srgrimes * Expand shell variables and backquotes inside a here document.
12690111Simp *	union node *arg		the document
12790111Simp *	int fd;			where to write the expanded version
1281556Srgrimes */
1291556Srgrimes
1301556Srgrimesvoid
13190111Simpexpandhere(union node *arg, int fd)
13290111Simp{
1331556Srgrimes	herefd = fd;
1341556Srgrimes	expandarg(arg, (struct arglist *)NULL, 0);
13539137Stegge	xwrite(fd, stackblock(), expdest - stackblock());
1361556Srgrimes}
1371556Srgrimes
1381556Srgrimes
1391556Srgrimes/*
1401556Srgrimes * Perform variable substitution and command substitution on an argument,
1411556Srgrimes * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
1421556Srgrimes * perform splitting and file name expansion.  When arglist is NULL, perform
1431556Srgrimes * here document expansion.
1441556Srgrimes */
1451556Srgrimes
1461556Srgrimesvoid
14790111Simpexpandarg(union node *arg, struct arglist *arglist, int flag)
14817987Speter{
1491556Srgrimes	struct strlist *sp;
1501556Srgrimes	char *p;
1511556Srgrimes
1521556Srgrimes	argbackq = arg->narg.backquote;
1531556Srgrimes	STARTSTACKSTR(expdest);
1541556Srgrimes	ifsfirst.next = NULL;
1551556Srgrimes	ifslastp = NULL;
1561556Srgrimes	argstr(arg->narg.text, flag);
1571556Srgrimes	if (arglist == NULL) {
1581556Srgrimes		return;			/* here document expanded */
1591556Srgrimes	}
1601556Srgrimes	STPUTC('\0', expdest);
1611556Srgrimes	p = grabstackstr(expdest);
1621556Srgrimes	exparg.lastp = &exparg.list;
1631556Srgrimes	/*
1641556Srgrimes	 * TODO - EXP_REDIR
1651556Srgrimes	 */
1661556Srgrimes	if (flag & EXP_FULL) {
1671556Srgrimes		ifsbreakup(p, &exparg);
1681556Srgrimes		*exparg.lastp = NULL;
1691556Srgrimes		exparg.lastp = &exparg.list;
1701556Srgrimes		expandmeta(exparg.list, flag);
1711556Srgrimes	} else {
1721556Srgrimes		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
1731556Srgrimes			rmescapes(p);
1741556Srgrimes		sp = (struct strlist *)stalloc(sizeof (struct strlist));
1751556Srgrimes		sp->text = p;
1761556Srgrimes		*exparg.lastp = sp;
1771556Srgrimes		exparg.lastp = &sp->next;
1781556Srgrimes	}
1791556Srgrimes	while (ifsfirst.next != NULL) {
1801556Srgrimes		struct ifsregion *ifsp;
1811556Srgrimes		INTOFF;
1821556Srgrimes		ifsp = ifsfirst.next->next;
1831556Srgrimes		ckfree(ifsfirst.next);
1841556Srgrimes		ifsfirst.next = ifsp;
1851556Srgrimes		INTON;
1861556Srgrimes	}
1871556Srgrimes	*exparg.lastp = NULL;
1881556Srgrimes	if (exparg.list) {
1891556Srgrimes		*arglist->lastp = exparg.list;
1901556Srgrimes		arglist->lastp = exparg.lastp;
1911556Srgrimes	}
1921556Srgrimes}
1931556Srgrimes
1941556Srgrimes
1951556Srgrimes
1961556Srgrimes/*
1971556Srgrimes * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
1981556Srgrimes * characters to allow for further processing.  Otherwise treat
1991556Srgrimes * $@ like $* since no splitting will be performed.
2001556Srgrimes */
2011556Srgrimes
2021556SrgrimesSTATIC void
20390111Simpargstr(char *p, int flag)
20417987Speter{
20525233Ssteve	char c;
206104672Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);	/* do CTLESC */
2071556Srgrimes	int firsteq = 1;
2081556Srgrimes
2091556Srgrimes	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
2101556Srgrimes		p = exptilde(p, flag);
2111556Srgrimes	for (;;) {
2121556Srgrimes		switch (c = *p++) {
2131556Srgrimes		case '\0':
2141556Srgrimes		case CTLENDVAR: /* ??? */
2151556Srgrimes			goto breakloop;
21638887Stegge		case CTLQUOTEMARK:
21738887Stegge			/* "$@" syntax adherence hack */
21838887Stegge			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
21938887Stegge				break;
22039137Stegge			if ((flag & EXP_FULL) != 0)
22139137Stegge				STPUTC(c, expdest);
22238887Stegge			break;
2231556Srgrimes		case CTLESC:
2241556Srgrimes			if (quotes)
2251556Srgrimes				STPUTC(c, expdest);
2261556Srgrimes			c = *p++;
2271556Srgrimes			STPUTC(c, expdest);
2281556Srgrimes			break;
2291556Srgrimes		case CTLVAR:
2301556Srgrimes			p = evalvar(p, flag);
2311556Srgrimes			break;
2321556Srgrimes		case CTLBACKQ:
2331556Srgrimes		case CTLBACKQ|CTLQUOTE:
2341556Srgrimes			expbackq(argbackq->n, c & CTLQUOTE, flag);
2351556Srgrimes			argbackq = argbackq->next;
2361556Srgrimes			break;
2371556Srgrimes		case CTLENDARI:
2381556Srgrimes			expari(flag);
2391556Srgrimes			break;
2401556Srgrimes		case ':':
2411556Srgrimes		case '=':
2421556Srgrimes			/*
2431556Srgrimes			 * sort of a hack - expand tildes in variable
2441556Srgrimes			 * assignments (after the first '=' and after ':'s).
2451556Srgrimes			 */
2461556Srgrimes			STPUTC(c, expdest);
2471556Srgrimes			if (flag & EXP_VARTILDE && *p == '~') {
2481556Srgrimes				if (c == '=') {
2491556Srgrimes					if (firsteq)
2501556Srgrimes						firsteq = 0;
2511556Srgrimes					else
2521556Srgrimes						break;
2531556Srgrimes				}
2541556Srgrimes				p = exptilde(p, flag);
2551556Srgrimes			}
2561556Srgrimes			break;
2571556Srgrimes		default:
2581556Srgrimes			STPUTC(c, expdest);
2591556Srgrimes		}
2601556Srgrimes	}
2611556Srgrimesbreakloop:;
2621556Srgrimes}
2631556Srgrimes
2641556SrgrimesSTATIC char *
26590111Simpexptilde(char *p, int flag)
26617987Speter{
2671556Srgrimes	char c, *startp = p;
2681556Srgrimes	struct passwd *pw;
2691556Srgrimes	char *home;
270108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
2711556Srgrimes
27217987Speter	while ((c = *p) != '\0') {
2731556Srgrimes		switch(c) {
2741556Srgrimes		case CTLESC:
2751556Srgrimes			return (startp);
27639137Stegge		case CTLQUOTEMARK:
27739137Stegge			return (startp);
2781556Srgrimes		case ':':
2791556Srgrimes			if (flag & EXP_VARTILDE)
2801556Srgrimes				goto done;
2811556Srgrimes			break;
2821556Srgrimes		case '/':
2831556Srgrimes			goto done;
2841556Srgrimes		}
2851556Srgrimes		p++;
2861556Srgrimes	}
2871556Srgrimesdone:
2881556Srgrimes	*p = '\0';
2891556Srgrimes	if (*(startp+1) == '\0') {
2901556Srgrimes		if ((home = lookupvar("HOME")) == NULL)
2911556Srgrimes			goto lose;
2921556Srgrimes	} else {
2931556Srgrimes		if ((pw = getpwnam(startp+1)) == NULL)
2941556Srgrimes			goto lose;
2951556Srgrimes		home = pw->pw_dir;
2961556Srgrimes	}
2971556Srgrimes	if (*home == '\0')
2981556Srgrimes		goto lose;
2991556Srgrimes	*p = c;
30017987Speter	while ((c = *home++) != '\0') {
30183675Stegge		if (quotes && SQSYNTAX[(int)c] == CCTL)
3021556Srgrimes			STPUTC(CTLESC, expdest);
3031556Srgrimes		STPUTC(c, expdest);
3041556Srgrimes	}
3051556Srgrimes	return (p);
3061556Srgrimeslose:
3071556Srgrimes	*p = c;
3081556Srgrimes	return (startp);
3091556Srgrimes}
3101556Srgrimes
3111556Srgrimes
312155301SschweikhSTATIC void
31390111Simpremoverecordregions(int endoff)
31438887Stegge{
31538887Stegge	if (ifslastp == NULL)
31638887Stegge		return;
31738887Stegge
31838887Stegge	if (ifsfirst.endoff > endoff) {
31938887Stegge		while (ifsfirst.next != NULL) {
32038887Stegge			struct ifsregion *ifsp;
32138887Stegge			INTOFF;
32238887Stegge			ifsp = ifsfirst.next->next;
32338887Stegge			ckfree(ifsfirst.next);
32438887Stegge			ifsfirst.next = ifsp;
32538887Stegge			INTON;
32638887Stegge		}
32738887Stegge		if (ifsfirst.begoff > endoff)
32838887Stegge			ifslastp = NULL;
32938887Stegge		else {
33038887Stegge			ifslastp = &ifsfirst;
33138887Stegge			ifsfirst.endoff = endoff;
33238887Stegge		}
33338887Stegge		return;
33438887Stegge	}
335155301Sschweikh
33638887Stegge	ifslastp = &ifsfirst;
33738887Stegge	while (ifslastp->next && ifslastp->next->begoff < endoff)
33838887Stegge		ifslastp=ifslastp->next;
33938887Stegge	while (ifslastp->next != NULL) {
34038887Stegge		struct ifsregion *ifsp;
34138887Stegge		INTOFF;
34238887Stegge		ifsp = ifslastp->next->next;
34338887Stegge		ckfree(ifslastp->next);
34438887Stegge		ifslastp->next = ifsp;
34538887Stegge		INTON;
34638887Stegge	}
34738887Stegge	if (ifslastp->endoff > endoff)
34838887Stegge		ifslastp->endoff = endoff;
34938887Stegge}
35038887Stegge
3511556Srgrimes/*
3521556Srgrimes * Expand arithmetic expression.  Backup to start of expression,
3531556Srgrimes * evaluate, place result in (backed up) result, adjust string position.
3541556Srgrimes */
3551556Srgrimesvoid
35690111Simpexpari(int flag)
35717987Speter{
3581556Srgrimes	char *p, *start;
359178631Sstefanf	arith_t result;
36038887Stegge	int begoff;
361108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
36238887Stegge	int quoted;
3631556Srgrimes
36425233Ssteve
3651556Srgrimes	/*
36646684Skris	 * This routine is slightly over-complicated for
3671556Srgrimes	 * efficiency.  First we make sure there is
3681556Srgrimes	 * enough space for the result, which may be bigger
36946684Skris	 * than the expression if we add exponentiation.  Next we
3701556Srgrimes	 * scan backwards looking for the start of arithmetic.  If the
3711556Srgrimes	 * next previous character is a CTLESC character, then we
3721556Srgrimes	 * have to rescan starting from the beginning since CTLESC
3738855Srgrimes	 * characters have to be processed left to right.
3741556Srgrimes	 */
375178631Sstefanf	CHECKSTRSPACE(DIGITS(result) - 2, expdest);
3768855Srgrimes	USTPUTC('\0', expdest);
3771556Srgrimes	start = stackblock();
37883676Stegge	p = expdest - 2;
37983676Stegge	while (p >= start && *p != CTLARI)
3801556Srgrimes		--p;
38183676Stegge	if (p < start || *p != CTLARI)
3821556Srgrimes		error("missing CTLARI (shouldn't happen)");
38383676Stegge	if (p > start && *(p - 1) == CTLESC)
3841556Srgrimes		for (p = start; *p != CTLARI; p++)
3851556Srgrimes			if (*p == CTLESC)
3861556Srgrimes				p++;
38738887Stegge
38838887Stegge	if (p[1] == '"')
38938887Stegge		quoted=1;
39038887Stegge	else
39138887Stegge		quoted=0;
39238887Stegge	begoff = p - start;
39338887Stegge	removerecordregions(begoff);
3941556Srgrimes	if (quotes)
39538887Stegge		rmescapes(p+2);
39638887Stegge	result = arith(p+2);
397178631Sstefanf	fmtstr(p, DIGITS(result), ARITH_FORMAT_STR, result);
3981556Srgrimes	while (*p++)
3991556Srgrimes		;
40038887Stegge	if (quoted == 0)
40138887Stegge		recordregion(begoff, p - 1 - start, 0);
4021556Srgrimes	result = expdest - p + 1;
4031556Srgrimes	STADJUST(-result, expdest);
4041556Srgrimes}
4051556Srgrimes
4061556Srgrimes
4071556Srgrimes/*
4081556Srgrimes * Expand stuff in backwards quotes.
4091556Srgrimes */
4101556Srgrimes
4111556SrgrimesSTATIC void
41290111Simpexpbackq(union node *cmd, int quoted, int flag)
41317987Speter{
4141556Srgrimes	struct backcmd in;
4151556Srgrimes	int i;
4161556Srgrimes	char buf[128];
4171556Srgrimes	char *p;
4181556Srgrimes	char *dest = expdest;
4191556Srgrimes	struct ifsregion saveifs, *savelastp;
4201556Srgrimes	struct nodelist *saveargbackq;
4211556Srgrimes	char lastc;
4221556Srgrimes	int startloc = dest - stackblock();
4231556Srgrimes	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
4241556Srgrimes	int saveherefd;
425108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
426115424Sfenner	int nnl;
4271556Srgrimes
4281556Srgrimes	INTOFF;
4291556Srgrimes	saveifs = ifsfirst;
4301556Srgrimes	savelastp = ifslastp;
4311556Srgrimes	saveargbackq = argbackq;
4328855Srgrimes	saveherefd = herefd;
4331556Srgrimes	herefd = -1;
4341556Srgrimes	p = grabstackstr(dest);
4351556Srgrimes	evalbackcmd(cmd, &in);
4361556Srgrimes	ungrabstackstr(p, dest);
4371556Srgrimes	ifsfirst = saveifs;
4381556Srgrimes	ifslastp = savelastp;
4391556Srgrimes	argbackq = saveargbackq;
4401556Srgrimes	herefd = saveherefd;
4411556Srgrimes
4421556Srgrimes	p = in.buf;
4431556Srgrimes	lastc = '\0';
444115424Sfenner	nnl = 0;
445115424Sfenner	/* Don't copy trailing newlines */
4461556Srgrimes	for (;;) {
4471556Srgrimes		if (--in.nleft < 0) {
4481556Srgrimes			if (in.fd < 0)
4491556Srgrimes				break;
4501556Srgrimes			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
4511556Srgrimes			TRACE(("expbackq: read returns %d\n", i));
4521556Srgrimes			if (i <= 0)
4531556Srgrimes				break;
4541556Srgrimes			p = buf;
4551556Srgrimes			in.nleft = i - 1;
4561556Srgrimes		}
4571556Srgrimes		lastc = *p++;
4581556Srgrimes		if (lastc != '\0') {
45983675Stegge			if (quotes && syntax[(int)lastc] == CCTL)
4601556Srgrimes				STPUTC(CTLESC, dest);
461115424Sfenner			if (lastc == '\n') {
462115424Sfenner				nnl++;
463115424Sfenner			} else {
464115424Sfenner				while (nnl > 0) {
465115424Sfenner					nnl--;
466115424Sfenner					STPUTC('\n', dest);
467115424Sfenner				}
468115424Sfenner				STPUTC(lastc, dest);
469115424Sfenner			}
4701556Srgrimes		}
4711556Srgrimes	}
47217987Speter
4731556Srgrimes	if (in.fd >= 0)
4741556Srgrimes		close(in.fd);
4751556Srgrimes	if (in.buf)
4761556Srgrimes		ckfree(in.buf);
4771556Srgrimes	if (in.jp)
47845916Scracauer		exitstatus = waitforjob(in.jp, (int *)NULL);
4791556Srgrimes	if (quoted == 0)
4801556Srgrimes		recordregion(startloc, dest - stackblock(), 0);
4811556Srgrimes	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
4821556Srgrimes		(dest - stackblock()) - startloc,
4831556Srgrimes		(dest - stackblock()) - startloc,
4841556Srgrimes		stackblock() + startloc));
4851556Srgrimes	expdest = dest;
4861556Srgrimes	INTON;
4871556Srgrimes}
4881556Srgrimes
4891556Srgrimes
4901556Srgrimes
49117987SpeterSTATIC int
49290111Simpsubevalvar(char *p, char *str, int strloc, int subtype, int startloc,
49390111Simp  int varflags)
49417987Speter{
49517987Speter	char *startp;
49617987Speter	char *loc = NULL;
49745514Stegge	char *q;
49817987Speter	int c = 0;
49917987Speter	int saveherefd = herefd;
50017987Speter	struct nodelist *saveargbackq = argbackq;
50120425Ssteve	int amount;
50220425Ssteve
50317987Speter	herefd = -1;
50417987Speter	argstr(p, 0);
50517987Speter	STACKSTRNUL(expdest);
50617987Speter	herefd = saveherefd;
50717987Speter	argbackq = saveargbackq;
50817987Speter	startp = stackblock() + startloc;
50920425Ssteve	if (str == NULL)
51020425Ssteve	    str = stackblock() + strloc;
51117987Speter
51217987Speter	switch (subtype) {
51317987Speter	case VSASSIGN:
51417987Speter		setvar(str, startp, 0);
51520425Ssteve		amount = startp - expdest;
51620425Ssteve		STADJUST(amount, expdest);
51717987Speter		varflags &= ~VSNUL;
51817987Speter		if (c != 0)
51917987Speter			*loc = c;
52017987Speter		return 1;
52117987Speter
52217987Speter	case VSQUESTION:
52317987Speter		if (*p != CTLENDVAR) {
52417987Speter			outfmt(&errout, "%s\n", startp);
52517987Speter			error((char *)NULL);
52617987Speter		}
527112254Sru		error("%.*s: parameter %snot set", (int)(p - str - 1),
52817987Speter		      str, (varflags & VSNUL) ? "null or "
52917987Speter					      : nullstr);
53017987Speter		return 0;
53117987Speter
53217987Speter	case VSTRIMLEFT:
53325233Ssteve		for (loc = startp; loc < str; loc++) {
53417987Speter			c = *loc;
53517987Speter			*loc = '\0';
53645514Stegge			if (patmatch(str, startp, varflags & VSQUOTE)) {
53717987Speter				*loc = c;
53817987Speter				goto recordleft;
53917987Speter			}
54017987Speter			*loc = c;
54145514Stegge			if ((varflags & VSQUOTE) && *loc == CTLESC)
54245514Stegge				loc++;
54317987Speter		}
54417987Speter		return 0;
54517987Speter
54617987Speter	case VSTRIMLEFTMAX:
54745514Stegge		for (loc = str - 1; loc >= startp;) {
54817987Speter			c = *loc;
54917987Speter			*loc = '\0';
55045514Stegge			if (patmatch(str, startp, varflags & VSQUOTE)) {
55117987Speter				*loc = c;
55217987Speter				goto recordleft;
55317987Speter			}
55417987Speter			*loc = c;
55545514Stegge			loc--;
55645514Stegge			if ((varflags & VSQUOTE) && loc > startp &&
55745514Stegge			    *(loc - 1) == CTLESC) {
55845514Stegge				for (q = startp; q < loc; q++)
55945514Stegge					if (*q == CTLESC)
56045514Stegge						q++;
56145514Stegge				if (q > loc)
56245514Stegge					loc--;
56345514Stegge			}
56417987Speter		}
56517987Speter		return 0;
56617987Speter
56717987Speter	case VSTRIMRIGHT:
56845514Stegge		for (loc = str - 1; loc >= startp;) {
56945514Stegge			if (patmatch(str, loc, varflags & VSQUOTE)) {
57020425Ssteve				amount = loc - expdest;
57120425Ssteve				STADJUST(amount, expdest);
57217987Speter				return 1;
57317987Speter			}
57445514Stegge			loc--;
57545514Stegge			if ((varflags & VSQUOTE) && loc > startp &&
576155301Sschweikh			    *(loc - 1) == CTLESC) {
57745514Stegge				for (q = startp; q < loc; q++)
57845514Stegge					if (*q == CTLESC)
57945514Stegge						q++;
58045514Stegge				if (q > loc)
58145514Stegge					loc--;
58245514Stegge			}
58317987Speter		}
58417987Speter		return 0;
58517987Speter
58617987Speter	case VSTRIMRIGHTMAX:
58717987Speter		for (loc = startp; loc < str - 1; loc++) {
58845514Stegge			if (patmatch(str, loc, varflags & VSQUOTE)) {
58920425Ssteve				amount = loc - expdest;
59020425Ssteve				STADJUST(amount, expdest);
59117987Speter				return 1;
59217987Speter			}
59345514Stegge			if ((varflags & VSQUOTE) && *loc == CTLESC)
59445514Stegge				loc++;
59517987Speter		}
59617987Speter		return 0;
59717987Speter
59817987Speter
59917987Speter	default:
60017987Speter		abort();
60117987Speter	}
60217987Speter
60317987Speterrecordleft:
60420425Ssteve	amount = ((str - 1) - (loc - startp)) - expdest;
60520425Ssteve	STADJUST(amount, expdest);
60617987Speter	while (loc != str - 1)
60717987Speter		*startp++ = *loc++;
60817987Speter	return 1;
60917987Speter}
61017987Speter
61117987Speter
6121556Srgrimes/*
6131556Srgrimes * Expand a variable, and return a pointer to the next character in the
6141556Srgrimes * input string.
6151556Srgrimes */
6161556Srgrimes
6171556SrgrimesSTATIC char *
61890111Simpevalvar(char *p, int flag)
61917987Speter{
6201556Srgrimes	int subtype;
6211556Srgrimes	int varflags;
6221556Srgrimes	char *var;
6231556Srgrimes	char *val;
62445644Stegge	int patloc;
6251556Srgrimes	int c;
6261556Srgrimes	int set;
6271556Srgrimes	int special;
6281556Srgrimes	int startloc;
62917987Speter	int varlen;
63017987Speter	int easy;
631108935Stjr	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
6321556Srgrimes
633164081Sstefanf	varflags = (unsigned char)*p++;
6341556Srgrimes	subtype = varflags & VSTYPE;
6351556Srgrimes	var = p;
6361556Srgrimes	special = 0;
6371556Srgrimes	if (! is_name(*p))
6381556Srgrimes		special = 1;
6391556Srgrimes	p = strchr(p, '=') + 1;
6401556Srgrimesagain: /* jump here after setting a variable with ${var=text} */
641179022Sstefanf	if (varflags & VSLINENO) {
642179022Sstefanf		set = 1;
643179022Sstefanf		special = 0;
644179022Sstefanf		val = var;
645179022Sstefanf		p[-1] = '\0';	/* temporarily overwrite '=' to have \0
646179022Sstefanf				   terminated string */
647179022Sstefanf	} else if (special) {
64825233Ssteve		set = varisset(var, varflags & VSNUL);
6491556Srgrimes		val = NULL;
6501556Srgrimes	} else {
65160592Scracauer		val = bltinlookup(var, 1);
65217987Speter		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
6531556Srgrimes			val = NULL;
6541556Srgrimes			set = 0;
6551556Srgrimes		} else
6561556Srgrimes			set = 1;
6571556Srgrimes	}
65817987Speter	varlen = 0;
6591556Srgrimes	startloc = expdest - stackblock();
66096939Stjr	if (!set && uflag) {
66196939Stjr		switch (subtype) {
66296939Stjr		case VSNORMAL:
66396939Stjr		case VSTRIMLEFT:
66496939Stjr		case VSTRIMLEFTMAX:
66596939Stjr		case VSTRIMRIGHT:
66696939Stjr		case VSTRIMRIGHTMAX:
66796939Stjr		case VSLENGTH:
668112254Sru			error("%.*s: parameter not set", (int)(p - var - 1),
669112254Sru			    var);
67096939Stjr		}
67196939Stjr	}
6721556Srgrimes	if (set && subtype != VSPLUS) {
6731556Srgrimes		/* insert the value of the variable */
6741556Srgrimes		if (special) {
675164081Sstefanf			varvalue(var, varflags & VSQUOTE, subtype, flag);
67617987Speter			if (subtype == VSLENGTH) {
67725233Ssteve				varlen = expdest - stackblock() - startloc;
67825233Ssteve				STADJUST(-varlen, expdest);
67917987Speter			}
6801556Srgrimes		} else {
68120425Ssteve			char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
68217987Speter								  : BASESYNTAX;
6831556Srgrimes
68417987Speter			if (subtype == VSLENGTH) {
68517987Speter				for (;*val; val++)
68617987Speter					varlen++;
6871556Srgrimes			}
68817987Speter			else {
68917987Speter				while (*val) {
69083675Stegge					if (quotes &&
69154132Scracauer					    syntax[(int)*val] == CCTL)
69217987Speter						STPUTC(CTLESC, expdest);
69317987Speter					STPUTC(*val++, expdest);
69417987Speter				}
69517987Speter
69617987Speter			}
6971556Srgrimes		}
6981556Srgrimes	}
69920425Ssteve
7001556Srgrimes	if (subtype == VSPLUS)
7011556Srgrimes		set = ! set;
70217987Speter
70320425Ssteve	easy = ((varflags & VSQUOTE) == 0 ||
70417987Speter		(*var == '@' && shellparam.nparam != 1));
70517987Speter
70617987Speter
70717987Speter	switch (subtype) {
70817987Speter	case VSLENGTH:
70917987Speter		expdest = cvtnum(varlen, expdest);
71017987Speter		goto record;
71117987Speter
71217987Speter	case VSNORMAL:
71317987Speter		if (!easy)
71417987Speter			break;
71517987Speterrecord:
71620425Ssteve		recordregion(startloc, expdest - stackblock(),
71717987Speter			     varflags & VSQUOTE);
71817987Speter		break;
71917987Speter
72017987Speter	case VSPLUS:
72117987Speter	case VSMINUS:
72217987Speter		if (!set) {
7231556Srgrimes			argstr(p, flag);
72417987Speter			break;
72517987Speter		}
72617987Speter		if (easy)
72717987Speter			goto record;
72817987Speter		break;
72917987Speter
73017987Speter	case VSTRIMLEFT:
73117987Speter	case VSTRIMLEFTMAX:
73217987Speter	case VSTRIMRIGHT:
73317987Speter	case VSTRIMRIGHTMAX:
73417987Speter		if (!set)
73517987Speter			break;
73617987Speter		/*
73717987Speter		 * Terminate the string and start recording the pattern
73817987Speter		 * right after it
73917987Speter		 */
74017987Speter		STPUTC('\0', expdest);
74145644Stegge		patloc = expdest - stackblock();
74245644Stegge		if (subevalvar(p, NULL, patloc, subtype,
74338887Stegge			       startloc, varflags) == 0) {
74445644Stegge			int amount = (expdest - stackblock() - patloc) + 1;
74525233Ssteve			STADJUST(-amount, expdest);
74625233Ssteve		}
74738887Stegge		/* Remove any recorded regions beyond start of variable */
74838887Stegge		removerecordregions(startloc);
74938887Stegge		goto record;
75017987Speter
75117987Speter	case VSASSIGN:
75217987Speter	case VSQUESTION:
75317987Speter		if (!set) {
75420425Ssteve			if (subevalvar(p, var, 0, subtype, startloc, varflags)) {
75520425Ssteve				varflags &= ~VSNUL;
756155301Sschweikh				/*
757155301Sschweikh				 * Remove any recorded regions beyond
758155301Sschweikh				 * start of variable
75938887Stegge				 */
76038887Stegge				removerecordregions(startloc);
7611556Srgrimes				goto again;
76220425Ssteve			}
76317987Speter			break;
7641556Srgrimes		}
76517987Speter		if (easy)
76617987Speter			goto record;
76717987Speter		break;
76817987Speter
769164003Sstefanf	case VSERROR:
770164003Sstefanf		c = p - var - 1;
771164003Sstefanf		error("${%.*s%s}: Bad substitution", c, var,
772164003Sstefanf		    (c > 0 && *p != CTLENDVAR) ? "..." : "");
773164003Sstefanf
77417987Speter	default:
77517987Speter		abort();
7761556Srgrimes	}
777179022Sstefanf	p[-1] = '=';	/* recover overwritten '=' */
77817987Speter
7791556Srgrimes	if (subtype != VSNORMAL) {	/* skip to end of alternative */
7801556Srgrimes		int nesting = 1;
7811556Srgrimes		for (;;) {
7821556Srgrimes			if ((c = *p++) == CTLESC)
7831556Srgrimes				p++;
7841556Srgrimes			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
7851556Srgrimes				if (set)
7861556Srgrimes					argbackq = argbackq->next;
7871556Srgrimes			} else if (c == CTLVAR) {
7881556Srgrimes				if ((*p++ & VSTYPE) != VSNORMAL)
7891556Srgrimes					nesting++;
7901556Srgrimes			} else if (c == CTLENDVAR) {
7911556Srgrimes				if (--nesting == 0)
7921556Srgrimes					break;
7931556Srgrimes			}
7941556Srgrimes		}
7951556Srgrimes	}
7961556Srgrimes	return p;
7971556Srgrimes}
7981556Srgrimes
7991556Srgrimes
8001556Srgrimes
8011556Srgrimes/*
8021556Srgrimes * Test whether a specialized variable is set.
8031556Srgrimes */
8041556Srgrimes
8051556SrgrimesSTATIC int
80690111Simpvarisset(char *name, int nulok)
80725233Ssteve{
8081556Srgrimes
80925233Ssteve	if (*name == '!')
81025233Ssteve		return backgndpid != -1;
81125233Ssteve	else if (*name == '@' || *name == '*') {
8121556Srgrimes		if (*shellparam.p == NULL)
8131556Srgrimes			return 0;
81425233Ssteve
81525233Ssteve		if (nulok) {
81625233Ssteve			char **av;
81725233Ssteve
81825233Ssteve			for (av = shellparam.p; *av; av++)
81925233Ssteve				if (**av != '\0')
82025233Ssteve					return 1;
82125233Ssteve			return 0;
82225233Ssteve		}
82318202Speter	} else if (is_digit(*name)) {
82425233Ssteve		char *ap;
82520425Ssteve		int num = atoi(name);
82625233Ssteve
82725233Ssteve		if (num > shellparam.nparam)
82825233Ssteve			return 0;
82925233Ssteve
83025233Ssteve		if (num == 0)
83125233Ssteve			ap = arg0;
83225233Ssteve		else
83325233Ssteve			ap = shellparam.p[num - 1];
83425233Ssteve
83525233Ssteve		if (nulok && (ap == NULL || *ap == '\0'))
83625233Ssteve			return 0;
8371556Srgrimes	}
8381556Srgrimes	return 1;
8391556Srgrimes}
8401556Srgrimes
8411556Srgrimes
8421556Srgrimes
8431556Srgrimes/*
8441556Srgrimes * Add the value of a specialized variable to the stack string.
8451556Srgrimes */
8461556Srgrimes
8471556SrgrimesSTATIC void
848164081Sstefanfvarvalue(char *name, int quoted, int subtype, int flag)
84917987Speter{
8501556Srgrimes	int num;
8511556Srgrimes	char *p;
8521556Srgrimes	int i;
85317987Speter	extern int oexitstatus;
8541556Srgrimes	char sep;
8551556Srgrimes	char **ap;
8561556Srgrimes	char const *syntax;
8571556Srgrimes
8581556Srgrimes#define STRTODEST(p) \
8591556Srgrimes	do {\
860164081Sstefanf	if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) { \
8611556Srgrimes		syntax = quoted? DQSYNTAX : BASESYNTAX; \
8621556Srgrimes		while (*p) { \
86383675Stegge			if (syntax[(int)*p] == CCTL) \
8641556Srgrimes				STPUTC(CTLESC, expdest); \
8651556Srgrimes			STPUTC(*p++, expdest); \
8661556Srgrimes		} \
8671556Srgrimes	} else \
8681556Srgrimes		while (*p) \
8691556Srgrimes			STPUTC(*p++, expdest); \
8701556Srgrimes	} while (0)
8711556Srgrimes
8721556Srgrimes
87318202Speter	switch (*name) {
8741556Srgrimes	case '$':
8751556Srgrimes		num = rootpid;
8761556Srgrimes		goto numvar;
8771556Srgrimes	case '?':
87817987Speter		num = oexitstatus;
8791556Srgrimes		goto numvar;
8801556Srgrimes	case '#':
8811556Srgrimes		num = shellparam.nparam;
8821556Srgrimes		goto numvar;
8831556Srgrimes	case '!':
8841556Srgrimes		num = backgndpid;
8851556Srgrimesnumvar:
88617987Speter		expdest = cvtnum(num, expdest);
8871556Srgrimes		break;
8881556Srgrimes	case '-':
8891556Srgrimes		for (i = 0 ; i < NOPTS ; i++) {
8901556Srgrimes			if (optlist[i].val)
8911556Srgrimes				STPUTC(optlist[i].letter, expdest);
8921556Srgrimes		}
8931556Srgrimes		break;
8941556Srgrimes	case '@':
895164081Sstefanf		if (flag & EXP_FULL && quoted) {
89638887Stegge			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
89738887Stegge				STRTODEST(p);
89838887Stegge				if (*ap)
89938887Stegge					STPUTC('\0', expdest);
90038887Stegge			}
90138887Stegge			break;
9021556Srgrimes		}
903102410Scharnier		/* FALLTHROUGH */
9041556Srgrimes	case '*':
905149825Srse		if (ifsset())
90638887Stegge			sep = ifsval()[0];
90738887Stegge		else
90838887Stegge			sep = ' ';
9091556Srgrimes		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
9101556Srgrimes			STRTODEST(p);
91138887Stegge			if (*ap && sep)
9121556Srgrimes				STPUTC(sep, expdest);
9131556Srgrimes		}
9141556Srgrimes		break;
9151556Srgrimes	case '0':
9161556Srgrimes		p = arg0;
9171556Srgrimes		STRTODEST(p);
9181556Srgrimes		break;
9191556Srgrimes	default:
92018202Speter		if (is_digit(*name)) {
92118202Speter			num = atoi(name);
92218202Speter			if (num > 0 && num <= shellparam.nparam) {
92318202Speter				p = shellparam.p[num - 1];
92418202Speter				STRTODEST(p);
92518202Speter			}
9261556Srgrimes		}
9271556Srgrimes		break;
9281556Srgrimes	}
9291556Srgrimes}
9301556Srgrimes
9311556Srgrimes
9321556Srgrimes
9331556Srgrimes/*
9341556Srgrimes * Record the the fact that we have to scan this region of the
9351556Srgrimes * string for IFS characters.
9361556Srgrimes */
9371556Srgrimes
9381556SrgrimesSTATIC void
939194975Sjillesrecordregion(int start, int end, int inquotes)
94017987Speter{
94125233Ssteve	struct ifsregion *ifsp;
9421556Srgrimes
9431556Srgrimes	if (ifslastp == NULL) {
9441556Srgrimes		ifsp = &ifsfirst;
9451556Srgrimes	} else {
946194975Sjilles		if (ifslastp->endoff == start
947194975Sjilles		    && ifslastp->inquotes == inquotes) {
948194975Sjilles			/* extend previous area */
949194975Sjilles			ifslastp->endoff = end;
950194975Sjilles			return;
951194975Sjilles		}
9521556Srgrimes		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
9531556Srgrimes		ifslastp->next = ifsp;
9541556Srgrimes	}
9551556Srgrimes	ifslastp = ifsp;
9561556Srgrimes	ifslastp->next = NULL;
9571556Srgrimes	ifslastp->begoff = start;
9581556Srgrimes	ifslastp->endoff = end;
959194975Sjilles	ifslastp->inquotes = inquotes;
9601556Srgrimes}
9611556Srgrimes
9621556Srgrimes
9631556Srgrimes
9641556Srgrimes/*
9651556Srgrimes * Break the argument string into pieces based upon IFS and add the
9661556Srgrimes * strings to the argument list.  The regions of the string to be
9671556Srgrimes * searched for IFS characters have been stored by recordregion.
9681556Srgrimes */
9691556SrgrimesSTATIC void
97090111Simpifsbreakup(char *string, struct arglist *arglist)
97190111Simp{
9721556Srgrimes	struct ifsregion *ifsp;
9731556Srgrimes	struct strlist *sp;
9741556Srgrimes	char *start;
97525233Ssteve	char *p;
9761556Srgrimes	char *q;
9771556Srgrimes	char *ifs;
978194975Sjilles	const char *ifsspc;
979194975Sjilles	int had_param_ch = 0;
9801556Srgrimes
981194975Sjilles	start = string;
98217987Speter
983194975Sjilles	if (ifslastp == NULL) {
984194975Sjilles		/* Return entire argument, IFS doesn't apply to any of it */
985194975Sjilles		sp = (struct strlist *)stalloc(sizeof *sp);
986194975Sjilles		sp->text = start;
987194975Sjilles		*arglist->lastp = sp;
988194975Sjilles		arglist->lastp = &sp->next;
989194975Sjilles		return;
990194975Sjilles	}
991194975Sjilles
992194975Sjilles	ifs = ifsset() ? ifsval() : " \t\n";
993194975Sjilles
994194975Sjilles	for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
995194975Sjilles		p = string + ifsp->begoff;
996194975Sjilles		while (p < string + ifsp->endoff) {
997194975Sjilles			had_param_ch = 1;
998194975Sjilles			q = p;
999194975Sjilles			if (*p == CTLESC)
1000194975Sjilles				p++;
1001194975Sjilles			if (ifsp->inquotes) {
1002194975Sjilles				/* Only NULs (should be from "$@") end args */
1003194975Sjilles				if (*p != 0) {
10041556Srgrimes					p++;
1005194975Sjilles					continue;
1006194975Sjilles				}
1007194975Sjilles				ifsspc = NULL;
1008194975Sjilles			} else {
1009194975Sjilles				if (!strchr(ifs, *p)) {
101038887Stegge					p++;
1011194975Sjilles					continue;
1012194975Sjilles				}
1013194975Sjilles				had_param_ch = 0;
1014194975Sjilles				ifsspc = strchr(" \t\n", *p);
1015194975Sjilles
1016194975Sjilles				/* Ignore IFS whitespace at start */
1017194975Sjilles				if (q == start && ifsspc != NULL) {
1018194975Sjilles					p++;
10191556Srgrimes					start = p;
1020194975Sjilles					continue;
1021194975Sjilles				}
10221556Srgrimes			}
1023194975Sjilles
1024194975Sjilles			/* Save this argument... */
1025194975Sjilles			*q = '\0';
10261556Srgrimes			sp = (struct strlist *)stalloc(sizeof *sp);
10271556Srgrimes			sp->text = start;
10281556Srgrimes			*arglist->lastp = sp;
10291556Srgrimes			arglist->lastp = &sp->next;
1030194975Sjilles			p++;
1031194975Sjilles
1032194975Sjilles			if (ifsspc != NULL) {
1033194975Sjilles				/* Ignore further trailing IFS whitespace */
1034194975Sjilles				for (; p < string + ifsp->endoff; p++) {
1035194975Sjilles					q = p;
1036194975Sjilles					if (*p == CTLESC)
1037194975Sjilles						p++;
1038194975Sjilles					if (strchr(ifs, *p) == NULL) {
1039194975Sjilles						p = q;
1040194975Sjilles						break;
1041194975Sjilles					}
1042194975Sjilles					if (strchr(" \t\n", *p) == NULL) {
1043194975Sjilles						p++;
1044194975Sjilles						break;
1045194975Sjilles					}
1046194975Sjilles				}
1047194975Sjilles			}
1048194975Sjilles			start = p;
10491556Srgrimes		}
1050194975Sjilles	}
1051194975Sjilles
1052194975Sjilles	/*
1053194975Sjilles	 * Save anything left as an argument.
1054194975Sjilles	 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
1055194975Sjilles	 * generating 2 arguments, the second of which is empty.
1056194975Sjilles	 * Some recent clarification of the Posix spec say that it
1057194975Sjilles	 * should only generate one....
1058194975Sjilles	 */
1059194975Sjilles	if (had_param_ch || *start != 0) {
10601556Srgrimes		sp = (struct strlist *)stalloc(sizeof *sp);
10611556Srgrimes		sp->text = start;
10621556Srgrimes		*arglist->lastp = sp;
10631556Srgrimes		arglist->lastp = &sp->next;
10641556Srgrimes	}
10651556Srgrimes}
10661556Srgrimes
10671556Srgrimes
10681556Srgrimes
10691556Srgrimes/*
10701556Srgrimes * Expand shell metacharacters.  At this point, the only control characters
10711556Srgrimes * should be escapes.  The results are stored in the list exparg.
10721556Srgrimes */
10731556Srgrimes
1074117261SddsSTATIC char *expdir;
10751556Srgrimes
10761556Srgrimes
10771556SrgrimesSTATIC void
107890111Simpexpandmeta(struct strlist *str, int flag __unused)
107917987Speter{
10801556Srgrimes	char *p;
10811556Srgrimes	struct strlist **savelastp;
10821556Srgrimes	struct strlist *sp;
10831556Srgrimes	char c;
10841556Srgrimes	/* TODO - EXP_REDIR */
10851556Srgrimes
10861556Srgrimes	while (str) {
10871556Srgrimes		if (fflag)
10881556Srgrimes			goto nometa;
10891556Srgrimes		p = str->text;
10901556Srgrimes		for (;;) {			/* fast check for meta chars */
10911556Srgrimes			if ((c = *p++) == '\0')
10921556Srgrimes				goto nometa;
10931556Srgrimes			if (c == '*' || c == '?' || c == '[' || c == '!')
10941556Srgrimes				break;
10951556Srgrimes		}
10961556Srgrimes		savelastp = exparg.lastp;
10971556Srgrimes		INTOFF;
10981556Srgrimes		if (expdir == NULL) {
10991556Srgrimes			int i = strlen(str->text);
11001556Srgrimes			expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
11011556Srgrimes		}
11021556Srgrimes
11031556Srgrimes		expmeta(expdir, str->text);
11041556Srgrimes		ckfree(expdir);
11051556Srgrimes		expdir = NULL;
11061556Srgrimes		INTON;
11071556Srgrimes		if (exparg.lastp == savelastp) {
11088855Srgrimes			/*
11098855Srgrimes			 * no matches
11101556Srgrimes			 */
11111556Srgrimesnometa:
11121556Srgrimes			*exparg.lastp = str;
11131556Srgrimes			rmescapes(str->text);
11141556Srgrimes			exparg.lastp = &str->next;
11151556Srgrimes		} else {
11161556Srgrimes			*exparg.lastp = NULL;
11171556Srgrimes			*savelastp = sp = expsort(*savelastp);
11181556Srgrimes			while (sp->next != NULL)
11191556Srgrimes				sp = sp->next;
11201556Srgrimes			exparg.lastp = &sp->next;
11211556Srgrimes		}
11221556Srgrimes		str = str->next;
11231556Srgrimes	}
11241556Srgrimes}
11251556Srgrimes
11261556Srgrimes
11271556Srgrimes/*
11281556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion.
11291556Srgrimes */
11301556Srgrimes
11311556SrgrimesSTATIC void
113290111Simpexpmeta(char *enddir, char *name)
113390111Simp{
113425233Ssteve	char *p;
11351556Srgrimes	char *q;
11361556Srgrimes	char *start;
11371556Srgrimes	char *endname;
11381556Srgrimes	int metaflag;
11391556Srgrimes	struct stat statb;
11401556Srgrimes	DIR *dirp;
11411556Srgrimes	struct dirent *dp;
11421556Srgrimes	int atend;
11431556Srgrimes	int matchdot;
11441556Srgrimes
11451556Srgrimes	metaflag = 0;
11461556Srgrimes	start = name;
11471556Srgrimes	for (p = name ; ; p++) {
11481556Srgrimes		if (*p == '*' || *p == '?')
11491556Srgrimes			metaflag = 1;
11501556Srgrimes		else if (*p == '[') {
11511556Srgrimes			q = p + 1;
115226488Sache			if (*q == '!' || *q == '^')
11531556Srgrimes				q++;
11541556Srgrimes			for (;;) {
115538887Stegge				while (*q == CTLQUOTEMARK)
115638887Stegge					q++;
11571556Srgrimes				if (*q == CTLESC)
11581556Srgrimes					q++;
11591556Srgrimes				if (*q == '/' || *q == '\0')
11601556Srgrimes					break;
11611556Srgrimes				if (*++q == ']') {
11621556Srgrimes					metaflag = 1;
11631556Srgrimes					break;
11641556Srgrimes				}
11651556Srgrimes			}
11661556Srgrimes		} else if (*p == '!' && p[1] == '!'	&& (p == name || p[-1] == '/')) {
11671556Srgrimes			metaflag = 1;
11681556Srgrimes		} else if (*p == '\0')
11691556Srgrimes			break;
117038887Stegge		else if (*p == CTLQUOTEMARK)
117138887Stegge			continue;
11721556Srgrimes		else if (*p == CTLESC)
11731556Srgrimes			p++;
11741556Srgrimes		if (*p == '/') {
11751556Srgrimes			if (metaflag)
11761556Srgrimes				break;
11771556Srgrimes			start = p + 1;
11781556Srgrimes		}
11791556Srgrimes	}
11801556Srgrimes	if (metaflag == 0) {	/* we've reached the end of the file name */
11811556Srgrimes		if (enddir != expdir)
11821556Srgrimes			metaflag++;
11831556Srgrimes		for (p = name ; ; p++) {
118438887Stegge			if (*p == CTLQUOTEMARK)
118538887Stegge				continue;
11861556Srgrimes			if (*p == CTLESC)
11871556Srgrimes				p++;
11881556Srgrimes			*enddir++ = *p;
11891556Srgrimes			if (*p == '\0')
11901556Srgrimes				break;
11911556Srgrimes		}
1192147812Sdelphij		if (metaflag == 0 || lstat(expdir, &statb) >= 0)
11931556Srgrimes			addfname(expdir);
11941556Srgrimes		return;
11951556Srgrimes	}
11961556Srgrimes	endname = p;
11971556Srgrimes	if (start != name) {
11981556Srgrimes		p = name;
11991556Srgrimes		while (p < start) {
120038887Stegge			while (*p == CTLQUOTEMARK)
120138887Stegge				p++;
12021556Srgrimes			if (*p == CTLESC)
12031556Srgrimes				p++;
12041556Srgrimes			*enddir++ = *p++;
12051556Srgrimes		}
12061556Srgrimes	}
12071556Srgrimes	if (enddir == expdir) {
12081556Srgrimes		p = ".";
12091556Srgrimes	} else if (enddir == expdir + 1 && *expdir == '/') {
12101556Srgrimes		p = "/";
12111556Srgrimes	} else {
12121556Srgrimes		p = expdir;
12131556Srgrimes		enddir[-1] = '\0';
12141556Srgrimes	}
12151556Srgrimes	if ((dirp = opendir(p)) == NULL)
12161556Srgrimes		return;
12171556Srgrimes	if (enddir != expdir)
12181556Srgrimes		enddir[-1] = '/';
12191556Srgrimes	if (*endname == 0) {
12201556Srgrimes		atend = 1;
12211556Srgrimes	} else {
12221556Srgrimes		atend = 0;
12231556Srgrimes		*endname++ = '\0';
12241556Srgrimes	}
12251556Srgrimes	matchdot = 0;
122638887Stegge	p = start;
122738887Stegge	while (*p == CTLQUOTEMARK)
122838887Stegge		p++;
122938887Stegge	if (*p == CTLESC)
123038887Stegge		p++;
123138887Stegge	if (*p == '.')
12321556Srgrimes		matchdot++;
12331556Srgrimes	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
12341556Srgrimes		if (dp->d_name[0] == '.' && ! matchdot)
12351556Srgrimes			continue;
123645514Stegge		if (patmatch(start, dp->d_name, 0)) {
12371556Srgrimes			if (atend) {
12381556Srgrimes				scopy(dp->d_name, enddir);
12391556Srgrimes				addfname(expdir);
12401556Srgrimes			} else {
124117987Speter				for (p = enddir, q = dp->d_name;
124217987Speter				     (*p++ = *q++) != '\0';)
124317987Speter					continue;
12441556Srgrimes				p[-1] = '/';
12451556Srgrimes				expmeta(p, endname);
12461556Srgrimes			}
12471556Srgrimes		}
12481556Srgrimes	}
12491556Srgrimes	closedir(dirp);
12501556Srgrimes	if (! atend)
12511556Srgrimes		endname[-1] = '/';
12521556Srgrimes}
12531556Srgrimes
12541556Srgrimes
12551556Srgrimes/*
12561556Srgrimes * Add a file name to the list.
12571556Srgrimes */
12581556Srgrimes
12591556SrgrimesSTATIC void
126090111Simpaddfname(char *name)
126190111Simp{
12621556Srgrimes	char *p;
12631556Srgrimes	struct strlist *sp;
12641556Srgrimes
12651556Srgrimes	p = stalloc(strlen(name) + 1);
12661556Srgrimes	scopy(name, p);
12671556Srgrimes	sp = (struct strlist *)stalloc(sizeof *sp);
12681556Srgrimes	sp->text = p;
12691556Srgrimes	*exparg.lastp = sp;
12701556Srgrimes	exparg.lastp = &sp->next;
12711556Srgrimes}
12721556Srgrimes
12731556Srgrimes
12741556Srgrimes/*
12751556Srgrimes * Sort the results of file name expansion.  It calculates the number of
12761556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the
12771556Srgrimes * work.
12781556Srgrimes */
12791556Srgrimes
12801556SrgrimesSTATIC struct strlist *
128190111Simpexpsort(struct strlist *str)
128290111Simp{
12831556Srgrimes	int len;
12841556Srgrimes	struct strlist *sp;
12851556Srgrimes
12861556Srgrimes	len = 0;
12871556Srgrimes	for (sp = str ; sp ; sp = sp->next)
12881556Srgrimes		len++;
12891556Srgrimes	return msort(str, len);
12901556Srgrimes}
12911556Srgrimes
12921556Srgrimes
12931556SrgrimesSTATIC struct strlist *
129490111Simpmsort(struct strlist *list, int len)
129517987Speter{
129617987Speter	struct strlist *p, *q = NULL;
12971556Srgrimes	struct strlist **lpp;
12981556Srgrimes	int half;
12991556Srgrimes	int n;
13001556Srgrimes
13011556Srgrimes	if (len <= 1)
13021556Srgrimes		return list;
13038855Srgrimes	half = len >> 1;
13041556Srgrimes	p = list;
13051556Srgrimes	for (n = half ; --n >= 0 ; ) {
13061556Srgrimes		q = p;
13071556Srgrimes		p = p->next;
13081556Srgrimes	}
13091556Srgrimes	q->next = NULL;			/* terminate first half of list */
13101556Srgrimes	q = msort(list, half);		/* sort first half of list */
13111556Srgrimes	p = msort(p, len - half);		/* sort second half */
13121556Srgrimes	lpp = &list;
13131556Srgrimes	for (;;) {
13141556Srgrimes		if (strcmp(p->text, q->text) < 0) {
13151556Srgrimes			*lpp = p;
13161556Srgrimes			lpp = &p->next;
13171556Srgrimes			if ((p = *lpp) == NULL) {
13181556Srgrimes				*lpp = q;
13191556Srgrimes				break;
13201556Srgrimes			}
13211556Srgrimes		} else {
13221556Srgrimes			*lpp = q;
13231556Srgrimes			lpp = &q->next;
13241556Srgrimes			if ((q = *lpp) == NULL) {
13251556Srgrimes				*lpp = p;
13261556Srgrimes				break;
13271556Srgrimes			}
13281556Srgrimes		}
13291556Srgrimes	}
13301556Srgrimes	return list;
13311556Srgrimes}
13321556Srgrimes
13331556Srgrimes
13341556Srgrimes
13351556Srgrimes/*
13361556Srgrimes * Returns true if the pattern matches the string.
13371556Srgrimes */
13381556Srgrimes
13391556Srgrimesint
134090111Simppatmatch(char *pattern, char *string, int squoted)
134190111Simp{
13421556Srgrimes#ifdef notdef
13431556Srgrimes	if (pattern[0] == '!' && pattern[1] == '!')
13441556Srgrimes		return 1 - pmatch(pattern + 2, string);
13451556Srgrimes	else
13461556Srgrimes#endif
134745514Stegge		return pmatch(pattern, string, squoted);
13481556Srgrimes}
13491556Srgrimes
135017987Speter
135117525SacheSTATIC int
135290111Simppmatch(char *pattern, char *string, int squoted)
135390111Simp{
135425233Ssteve	char *p, *q;
135525233Ssteve	char c;
13561556Srgrimes
13571556Srgrimes	p = pattern;
13581556Srgrimes	q = string;
13591556Srgrimes	for (;;) {
13601556Srgrimes		switch (c = *p++) {
13611556Srgrimes		case '\0':
13621556Srgrimes			goto breakloop;
13631556Srgrimes		case CTLESC:
136445514Stegge			if (squoted && *q == CTLESC)
136545514Stegge				q++;
13661556Srgrimes			if (*q++ != *p++)
13671556Srgrimes				return 0;
13681556Srgrimes			break;
136938887Stegge		case CTLQUOTEMARK:
137038887Stegge			continue;
13711556Srgrimes		case '?':
137245514Stegge			if (squoted && *q == CTLESC)
137345514Stegge				q++;
13741556Srgrimes			if (*q++ == '\0')
13751556Srgrimes				return 0;
13761556Srgrimes			break;
13771556Srgrimes		case '*':
13781556Srgrimes			c = *p;
137938887Stegge			while (c == CTLQUOTEMARK || c == '*')
138038887Stegge				c = *++p;
138138887Stegge			if (c != CTLESC &&  c != CTLQUOTEMARK &&
138238887Stegge			    c != '?' && c != '*' && c != '[') {
13831556Srgrimes				while (*q != c) {
138445514Stegge					if (squoted && *q == CTLESC &&
138545514Stegge					    q[1] == c)
138645514Stegge						break;
13871556Srgrimes					if (*q == '\0')
13881556Srgrimes						return 0;
138945514Stegge					if (squoted && *q == CTLESC)
139045514Stegge						q++;
13911556Srgrimes					q++;
13921556Srgrimes				}
13931556Srgrimes			}
13941556Srgrimes			do {
139545514Stegge				if (pmatch(p, q, squoted))
13961556Srgrimes					return 1;
139745514Stegge				if (squoted && *q == CTLESC)
139845514Stegge					q++;
13991556Srgrimes			} while (*q++ != '\0');
14001556Srgrimes			return 0;
14011556Srgrimes		case '[': {
14021556Srgrimes			char *endp;
14031556Srgrimes			int invert, found;
14041556Srgrimes			char chr;
14051556Srgrimes
14061556Srgrimes			endp = p;
140726488Sache			if (*endp == '!' || *endp == '^')
14081556Srgrimes				endp++;
14091556Srgrimes			for (;;) {
141038887Stegge				while (*endp == CTLQUOTEMARK)
141138887Stegge					endp++;
14121556Srgrimes				if (*endp == '\0')
14131556Srgrimes					goto dft;		/* no matching ] */
14141556Srgrimes				if (*endp == CTLESC)
14151556Srgrimes					endp++;
14161556Srgrimes				if (*++endp == ']')
14171556Srgrimes					break;
14181556Srgrimes			}
14191556Srgrimes			invert = 0;
142026488Sache			if (*p == '!' || *p == '^') {
14211556Srgrimes				invert++;
14221556Srgrimes				p++;
14231556Srgrimes			}
14241556Srgrimes			found = 0;
14251556Srgrimes			chr = *q++;
142645514Stegge			if (squoted && chr == CTLESC)
142745514Stegge				chr = *q++;
142817987Speter			if (chr == '\0')
142917987Speter				return 0;
14301556Srgrimes			c = *p++;
14311556Srgrimes			do {
143238887Stegge				if (c == CTLQUOTEMARK)
143338887Stegge					continue;
14341556Srgrimes				if (c == CTLESC)
14351556Srgrimes					c = *p++;
14361556Srgrimes				if (*p == '-' && p[1] != ']') {
14371556Srgrimes					p++;
143838887Stegge					while (*p == CTLQUOTEMARK)
143938887Stegge						p++;
14401556Srgrimes					if (*p == CTLESC)
14411556Srgrimes						p++;
144217557Sache					if (   collate_range_cmp(chr, c) >= 0
144317557Sache					    && collate_range_cmp(chr, *p) <= 0
144417525Sache					   )
14451556Srgrimes						found = 1;
14461556Srgrimes					p++;
14471556Srgrimes				} else {
14481556Srgrimes					if (chr == c)
14491556Srgrimes						found = 1;
14501556Srgrimes				}
14511556Srgrimes			} while ((c = *p++) != ']');
14521556Srgrimes			if (found == invert)
14531556Srgrimes				return 0;
14541556Srgrimes			break;
14551556Srgrimes		}
14561556Srgrimesdft:	        default:
145745514Stegge			if (squoted && *q == CTLESC)
145845514Stegge				q++;
14591556Srgrimes			if (*q++ != c)
14601556Srgrimes				return 0;
14611556Srgrimes			break;
14621556Srgrimes		}
14631556Srgrimes	}
14641556Srgrimesbreakloop:
14651556Srgrimes	if (*q != '\0')
14661556Srgrimes		return 0;
14671556Srgrimes	return 1;
14681556Srgrimes}
14691556Srgrimes
14701556Srgrimes
14711556Srgrimes
14721556Srgrimes/*
14731556Srgrimes * Remove any CTLESC characters from a string.
14741556Srgrimes */
14751556Srgrimes
14761556Srgrimesvoid
147790111Simprmescapes(char *str)
147838887Stegge{
147925233Ssteve	char *p, *q;
14801556Srgrimes
14811556Srgrimes	p = str;
148238887Stegge	while (*p != CTLESC && *p != CTLQUOTEMARK) {
14831556Srgrimes		if (*p++ == '\0')
14841556Srgrimes			return;
14851556Srgrimes	}
14861556Srgrimes	q = p;
14871556Srgrimes	while (*p) {
148838887Stegge		if (*p == CTLQUOTEMARK) {
148938887Stegge			p++;
149038887Stegge			continue;
149138887Stegge		}
14921556Srgrimes		if (*p == CTLESC)
14931556Srgrimes			p++;
14941556Srgrimes		*q++ = *p++;
14951556Srgrimes	}
14961556Srgrimes	*q = '\0';
14971556Srgrimes}
14981556Srgrimes
14991556Srgrimes
15001556Srgrimes
15011556Srgrimes/*
15021556Srgrimes * See if a pattern matches in a case statement.
15031556Srgrimes */
15041556Srgrimes
15051556Srgrimesint
150690111Simpcasematch(union node *pattern, char *val)
150790111Simp{
15081556Srgrimes	struct stackmark smark;
15091556Srgrimes	int result;
15101556Srgrimes	char *p;
15111556Srgrimes
15121556Srgrimes	setstackmark(&smark);
15131556Srgrimes	argbackq = pattern->narg.backquote;
15141556Srgrimes	STARTSTACKSTR(expdest);
15151556Srgrimes	ifslastp = NULL;
15161556Srgrimes	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
15171556Srgrimes	STPUTC('\0', expdest);
15181556Srgrimes	p = grabstackstr(expdest);
151945514Stegge	result = patmatch(p, val, 0);
15201556Srgrimes	popstackmark(&smark);
15211556Srgrimes	return result;
15221556Srgrimes}
152317987Speter
152417987Speter/*
152517987Speter * Our own itoa().
152617987Speter */
152717987Speter
152817987SpeterSTATIC char *
152990111Simpcvtnum(int num, char *buf)
153090111Simp{
153117987Speter	char temp[32];
153217987Speter	int neg = num < 0;
153317987Speter	char *p = temp + 31;
153417987Speter
153517987Speter	temp[31] = '\0';
153617987Speter
153717987Speter	do {
153817987Speter		*--p = num % 10 + '0';
153917987Speter	} while ((num /= 10) != 0);
154017987Speter
154117987Speter	if (neg)
154217987Speter		*--p = '-';
154317987Speter
154417987Speter	while (*p)
154517987Speter		STPUTC(*p++, buf);
154617987Speter	return buf;
154717987Speter}
1548108286Stjr
1549108286Stjr/*
1550108286Stjr * Do most of the work for wordexp(3).
1551108286Stjr */
1552108286Stjr
1553108286Stjrint
1554108286Stjrwordexpcmd(int argc, char **argv)
1555108286Stjr{
1556108286Stjr	size_t len;
1557108286Stjr	int i;
1558108286Stjr
1559108286Stjr	out1fmt("%08x", argc - 1);
1560108286Stjr	for (i = 1, len = 0; i < argc; i++)
1561108286Stjr		len += strlen(argv[i]);
1562108286Stjr	out1fmt("%08x", (int)len);
1563108286Stjr	for (i = 1; i < argc; i++) {
1564108286Stjr		out1str(argv[i]);
1565108286Stjr		out1c('\0');
1566108286Stjr	}
1567108286Stjr        return (0);
1568108286Stjr}
1569