expand.c revision 83676
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 * 3. All advertising materials mentioning features or use of this software
171556Srgrimes *    must display the following acknowledgement:
181556Srgrimes *	This product includes software developed by the University of
191556Srgrimes *	California, Berkeley and its contributors.
201556Srgrimes * 4. Neither the name of the University nor the names of its contributors
211556Srgrimes *    may be used to endorse or promote products derived from this software
221556Srgrimes *    without specific prior written permission.
231556Srgrimes *
241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341556Srgrimes * SUCH DAMAGE.
351556Srgrimes */
361556Srgrimes
371556Srgrimes#ifndef lint
3836150Scharnier#if 0
3936150Scharnierstatic char sccsid[] = "@(#)expand.c	8.5 (Berkeley) 5/15/95";
4036150Scharnier#endif
4136150Scharnierstatic const char rcsid[] =
4250471Speter  "$FreeBSD: head/bin/sh/expand.c 83676 2001-09-19 20:16:38Z tegge $";
431556Srgrimes#endif /* not lint */
441556Srgrimes
4517987Speter#include <sys/types.h>
4617987Speter#include <sys/time.h>
4717987Speter#include <sys/stat.h>
4817987Speter#include <errno.h>
4917987Speter#include <dirent.h>
5017987Speter#include <unistd.h>
5117987Speter#include <pwd.h>
5217987Speter#include <stdlib.h>
5319281Sache#include <limits.h>
5438887Stegge#include <stdio.h>
5517987Speter
561556Srgrimes/*
571556Srgrimes * Routines to expand arguments to commands.  We have to deal with
581556Srgrimes * backquotes, shell variables, and file metacharacters.
591556Srgrimes */
601556Srgrimes
611556Srgrimes#include "shell.h"
621556Srgrimes#include "main.h"
631556Srgrimes#include "nodes.h"
641556Srgrimes#include "eval.h"
651556Srgrimes#include "expand.h"
661556Srgrimes#include "syntax.h"
671556Srgrimes#include "parser.h"
681556Srgrimes#include "jobs.h"
691556Srgrimes#include "options.h"
701556Srgrimes#include "var.h"
711556Srgrimes#include "input.h"
721556Srgrimes#include "output.h"
731556Srgrimes#include "memalloc.h"
741556Srgrimes#include "error.h"
751556Srgrimes#include "mystring.h"
7617987Speter#include "arith.h"
7717987Speter#include "show.h"
781556Srgrimes
791556Srgrimes/*
801556Srgrimes * Structure specifying which parts of the string should be searched
811556Srgrimes * for IFS characters.
821556Srgrimes */
831556Srgrimes
841556Srgrimesstruct ifsregion {
851556Srgrimes	struct ifsregion *next;	/* next region in list */
861556Srgrimes	int begoff;		/* offset of start of region */
871556Srgrimes	int endoff;		/* offset of end of region */
881556Srgrimes	int nulonly;		/* search for nul bytes only */
891556Srgrimes};
901556Srgrimes
911556Srgrimes
921556Srgrimeschar *expdest;			/* output of current string */
931556Srgrimesstruct nodelist *argbackq;	/* list of back quote expressions */
941556Srgrimesstruct ifsregion ifsfirst;	/* first struct in list of ifs regions */
951556Srgrimesstruct ifsregion *ifslastp;	/* last struct in list */
961556Srgrimesstruct arglist exparg;		/* holds expanded arg list */
971556Srgrimes
9817987SpeterSTATIC void argstr __P((char *, int));
9917987SpeterSTATIC char *exptilde __P((char *, int));
10017987SpeterSTATIC void expbackq __P((union node *, int, int));
10120425SsteveSTATIC int subevalvar __P((char *, char *, int, int, int, int));
10217987SpeterSTATIC char *evalvar __P((char *, int));
10325233SsteveSTATIC int varisset __P((char *, int));
10418202SpeterSTATIC void varvalue __P((char *, int, int));
10517987SpeterSTATIC void recordregion __P((int, int, int));
10638887SteggeSTATIC void removerecordregions __P((int));
10717987SpeterSTATIC void ifsbreakup __P((char *, struct arglist *));
10817987SpeterSTATIC void expandmeta __P((struct strlist *, int));
10917987SpeterSTATIC void expmeta __P((char *, char *));
11017987SpeterSTATIC void addfname __P((char *));
11117987SpeterSTATIC struct strlist *expsort __P((struct strlist *));
11217987SpeterSTATIC struct strlist *msort __P((struct strlist *, int));
11345514SteggeSTATIC int pmatch __P((char *, char *, int));
11417987SpeterSTATIC char *cvtnum __P((int, char *));
11519281SacheSTATIC int collate_range_cmp __P((int, int));
1161556Srgrimes
11719281SacheSTATIC int collate_range_cmp (c1, c2)
11819281Sache	int c1, c2;
11919281Sache{
12019281Sache	static char s1[2], s2[2];
12119281Sache	int ret;
12219281Sache
12319281Sache	c1 &= UCHAR_MAX;
12419281Sache	c2 &= UCHAR_MAX;
12519281Sache	if (c1 == c2)
12619281Sache		return (0);
12719281Sache	s1[0] = c1;
12819281Sache	s2[0] = c2;
12919281Sache	if ((ret = strcoll(s1, s2)) != 0)
13019281Sache		return (ret);
13119281Sache	return (c1 - c2);
13219281Sache}
13319281Sache
1341556Srgrimes/*
1351556Srgrimes * Expand shell variables and backquotes inside a here document.
1361556Srgrimes */
1371556Srgrimes
1381556Srgrimesvoid
1391556Srgrimesexpandhere(arg, fd)
1401556Srgrimes	union node *arg;	/* the document */
1411556Srgrimes	int fd;			/* where to write the expanded version */
1421556Srgrimes	{
1431556Srgrimes	herefd = fd;
1441556Srgrimes	expandarg(arg, (struct arglist *)NULL, 0);
14539137Stegge	xwrite(fd, stackblock(), expdest - stackblock());
1461556Srgrimes}
1471556Srgrimes
1481556Srgrimes
1491556Srgrimes/*
1501556Srgrimes * Perform variable substitution and command substitution on an argument,
1511556Srgrimes * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
1521556Srgrimes * perform splitting and file name expansion.  When arglist is NULL, perform
1531556Srgrimes * here document expansion.
1541556Srgrimes */
1551556Srgrimes
1561556Srgrimesvoid
1571556Srgrimesexpandarg(arg, arglist, flag)
1581556Srgrimes	union node *arg;
1591556Srgrimes	struct arglist *arglist;
16017987Speter	int flag;
16117987Speter{
1621556Srgrimes	struct strlist *sp;
1631556Srgrimes	char *p;
1641556Srgrimes
1651556Srgrimes	argbackq = arg->narg.backquote;
1661556Srgrimes	STARTSTACKSTR(expdest);
1671556Srgrimes	ifsfirst.next = NULL;
1681556Srgrimes	ifslastp = NULL;
1691556Srgrimes	argstr(arg->narg.text, flag);
1701556Srgrimes	if (arglist == NULL) {
1711556Srgrimes		return;			/* here document expanded */
1721556Srgrimes	}
1731556Srgrimes	STPUTC('\0', expdest);
1741556Srgrimes	p = grabstackstr(expdest);
1751556Srgrimes	exparg.lastp = &exparg.list;
1761556Srgrimes	/*
1771556Srgrimes	 * TODO - EXP_REDIR
1781556Srgrimes	 */
1791556Srgrimes	if (flag & EXP_FULL) {
1801556Srgrimes		ifsbreakup(p, &exparg);
1811556Srgrimes		*exparg.lastp = NULL;
1821556Srgrimes		exparg.lastp = &exparg.list;
1831556Srgrimes		expandmeta(exparg.list, flag);
1841556Srgrimes	} else {
1851556Srgrimes		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
1861556Srgrimes			rmescapes(p);
1871556Srgrimes		sp = (struct strlist *)stalloc(sizeof (struct strlist));
1881556Srgrimes		sp->text = p;
1891556Srgrimes		*exparg.lastp = sp;
1901556Srgrimes		exparg.lastp = &sp->next;
1911556Srgrimes	}
1921556Srgrimes	while (ifsfirst.next != NULL) {
1931556Srgrimes		struct ifsregion *ifsp;
1941556Srgrimes		INTOFF;
1951556Srgrimes		ifsp = ifsfirst.next->next;
1961556Srgrimes		ckfree(ifsfirst.next);
1971556Srgrimes		ifsfirst.next = ifsp;
1981556Srgrimes		INTON;
1991556Srgrimes	}
2001556Srgrimes	*exparg.lastp = NULL;
2011556Srgrimes	if (exparg.list) {
2021556Srgrimes		*arglist->lastp = exparg.list;
2031556Srgrimes		arglist->lastp = exparg.lastp;
2041556Srgrimes	}
2051556Srgrimes}
2061556Srgrimes
2071556Srgrimes
2081556Srgrimes
2091556Srgrimes/*
2101556Srgrimes * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
2111556Srgrimes * characters to allow for further processing.  Otherwise treat
2121556Srgrimes * $@ like $* since no splitting will be performed.
2131556Srgrimes */
2141556Srgrimes
2151556SrgrimesSTATIC void
2161556Srgrimesargstr(p, flag)
21725233Ssteve	char *p;
21817987Speter	int flag;
21917987Speter{
22025233Ssteve	char c;
2211556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);	/* do CTLESC */
2221556Srgrimes	int firsteq = 1;
2231556Srgrimes
2241556Srgrimes	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
2251556Srgrimes		p = exptilde(p, flag);
2261556Srgrimes	for (;;) {
2271556Srgrimes		switch (c = *p++) {
2281556Srgrimes		case '\0':
2291556Srgrimes		case CTLENDVAR: /* ??? */
2301556Srgrimes			goto breakloop;
23138887Stegge		case CTLQUOTEMARK:
23238887Stegge			/* "$@" syntax adherence hack */
23338887Stegge			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
23438887Stegge				break;
23539137Stegge			if ((flag & EXP_FULL) != 0)
23639137Stegge				STPUTC(c, expdest);
23738887Stegge			break;
2381556Srgrimes		case CTLESC:
2391556Srgrimes			if (quotes)
2401556Srgrimes				STPUTC(c, expdest);
2411556Srgrimes			c = *p++;
2421556Srgrimes			STPUTC(c, expdest);
2431556Srgrimes			break;
2441556Srgrimes		case CTLVAR:
2451556Srgrimes			p = evalvar(p, flag);
2461556Srgrimes			break;
2471556Srgrimes		case CTLBACKQ:
2481556Srgrimes		case CTLBACKQ|CTLQUOTE:
2491556Srgrimes			expbackq(argbackq->n, c & CTLQUOTE, flag);
2501556Srgrimes			argbackq = argbackq->next;
2511556Srgrimes			break;
2521556Srgrimes		case CTLENDARI:
2531556Srgrimes			expari(flag);
2541556Srgrimes			break;
2551556Srgrimes		case ':':
2561556Srgrimes		case '=':
2571556Srgrimes			/*
2581556Srgrimes			 * sort of a hack - expand tildes in variable
2591556Srgrimes			 * assignments (after the first '=' and after ':'s).
2601556Srgrimes			 */
2611556Srgrimes			STPUTC(c, expdest);
2621556Srgrimes			if (flag & EXP_VARTILDE && *p == '~') {
2631556Srgrimes				if (c == '=') {
2641556Srgrimes					if (firsteq)
2651556Srgrimes						firsteq = 0;
2661556Srgrimes					else
2671556Srgrimes						break;
2681556Srgrimes				}
2691556Srgrimes				p = exptilde(p, flag);
2701556Srgrimes			}
2711556Srgrimes			break;
2721556Srgrimes		default:
2731556Srgrimes			STPUTC(c, expdest);
2741556Srgrimes		}
2751556Srgrimes	}
2761556Srgrimesbreakloop:;
2771556Srgrimes}
2781556Srgrimes
2791556SrgrimesSTATIC char *
2801556Srgrimesexptilde(p, flag)
2811556Srgrimes	char *p;
28217987Speter	int flag;
28317987Speter{
2841556Srgrimes	char c, *startp = p;
2851556Srgrimes	struct passwd *pw;
2861556Srgrimes	char *home;
2871556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
2881556Srgrimes
28917987Speter	while ((c = *p) != '\0') {
2901556Srgrimes		switch(c) {
2911556Srgrimes		case CTLESC:
2921556Srgrimes			return (startp);
29339137Stegge		case CTLQUOTEMARK:
29439137Stegge			return (startp);
2951556Srgrimes		case ':':
2961556Srgrimes			if (flag & EXP_VARTILDE)
2971556Srgrimes				goto done;
2981556Srgrimes			break;
2991556Srgrimes		case '/':
3001556Srgrimes			goto done;
3011556Srgrimes		}
3021556Srgrimes		p++;
3031556Srgrimes	}
3041556Srgrimesdone:
3051556Srgrimes	*p = '\0';
3061556Srgrimes	if (*(startp+1) == '\0') {
3071556Srgrimes		if ((home = lookupvar("HOME")) == NULL)
3081556Srgrimes			goto lose;
3091556Srgrimes	} else {
3101556Srgrimes		if ((pw = getpwnam(startp+1)) == NULL)
3111556Srgrimes			goto lose;
3121556Srgrimes		home = pw->pw_dir;
3131556Srgrimes	}
3141556Srgrimes	if (*home == '\0')
3151556Srgrimes		goto lose;
3161556Srgrimes	*p = c;
31717987Speter	while ((c = *home++) != '\0') {
31883675Stegge		if (quotes && SQSYNTAX[(int)c] == CCTL)
3191556Srgrimes			STPUTC(CTLESC, expdest);
3201556Srgrimes		STPUTC(c, expdest);
3211556Srgrimes	}
3221556Srgrimes	return (p);
3231556Srgrimeslose:
3241556Srgrimes	*p = c;
3251556Srgrimes	return (startp);
3261556Srgrimes}
3271556Srgrimes
3281556Srgrimes
32938887SteggeSTATIC void
33038887Steggeremoverecordregions(endoff)
33138887Stegge	int endoff;
33238887Stegge{
33338887Stegge	if (ifslastp == NULL)
33438887Stegge		return;
33538887Stegge
33638887Stegge	if (ifsfirst.endoff > endoff) {
33738887Stegge		while (ifsfirst.next != NULL) {
33838887Stegge			struct ifsregion *ifsp;
33938887Stegge			INTOFF;
34038887Stegge			ifsp = ifsfirst.next->next;
34138887Stegge			ckfree(ifsfirst.next);
34238887Stegge			ifsfirst.next = ifsp;
34338887Stegge			INTON;
34438887Stegge		}
34538887Stegge		if (ifsfirst.begoff > endoff)
34638887Stegge			ifslastp = NULL;
34738887Stegge		else {
34838887Stegge			ifslastp = &ifsfirst;
34938887Stegge			ifsfirst.endoff = endoff;
35038887Stegge		}
35138887Stegge		return;
35238887Stegge	}
35338887Stegge
35438887Stegge	ifslastp = &ifsfirst;
35538887Stegge	while (ifslastp->next && ifslastp->next->begoff < endoff)
35638887Stegge		ifslastp=ifslastp->next;
35738887Stegge	while (ifslastp->next != NULL) {
35838887Stegge		struct ifsregion *ifsp;
35938887Stegge		INTOFF;
36038887Stegge		ifsp = ifslastp->next->next;
36138887Stegge		ckfree(ifslastp->next);
36238887Stegge		ifslastp->next = ifsp;
36338887Stegge		INTON;
36438887Stegge	}
36538887Stegge	if (ifslastp->endoff > endoff)
36638887Stegge		ifslastp->endoff = endoff;
36738887Stegge}
36838887Stegge
3691556Srgrimes/*
3701556Srgrimes * Expand arithmetic expression.  Backup to start of expression,
3711556Srgrimes * evaluate, place result in (backed up) result, adjust string position.
3721556Srgrimes */
3731556Srgrimesvoid
3741556Srgrimesexpari(flag)
37517987Speter	int flag;
37617987Speter{
3771556Srgrimes	char *p, *start;
3781556Srgrimes	int result;
37938887Stegge	int begoff;
3801556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
38138887Stegge	int quoted;
3821556Srgrimes
38325233Ssteve
3841556Srgrimes	/*
38546684Skris	 * This routine is slightly over-complicated for
3861556Srgrimes	 * efficiency.  First we make sure there is
3871556Srgrimes	 * enough space for the result, which may be bigger
38846684Skris	 * than the expression if we add exponentiation.  Next we
3891556Srgrimes	 * scan backwards looking for the start of arithmetic.  If the
3901556Srgrimes	 * next previous character is a CTLESC character, then we
3911556Srgrimes	 * have to rescan starting from the beginning since CTLESC
3928855Srgrimes	 * characters have to be processed left to right.
3931556Srgrimes	 */
39422777Ssteve#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10
39522777Ssteve#error "integers with more than 10 digits are not supported"
39622777Ssteve#endif
39722777Ssteve	CHECKSTRSPACE(12 - 2, expdest);
3988855Srgrimes	USTPUTC('\0', expdest);
3991556Srgrimes	start = stackblock();
40083676Stegge	p = expdest - 2;
40183676Stegge	while (p >= start && *p != CTLARI)
4021556Srgrimes		--p;
40383676Stegge	if (p < start || *p != CTLARI)
4041556Srgrimes		error("missing CTLARI (shouldn't happen)");
40583676Stegge	if (p > start && *(p - 1) == CTLESC)
4061556Srgrimes		for (p = start; *p != CTLARI; p++)
4071556Srgrimes			if (*p == CTLESC)
4081556Srgrimes				p++;
40938887Stegge
41038887Stegge	if (p[1] == '"')
41138887Stegge		quoted=1;
41238887Stegge	else
41338887Stegge		quoted=0;
41438887Stegge	begoff = p - start;
41538887Stegge	removerecordregions(begoff);
4161556Srgrimes	if (quotes)
41738887Stegge		rmescapes(p+2);
41838887Stegge	result = arith(p+2);
41922777Ssteve	fmtstr(p, 12, "%d", result);
4201556Srgrimes	while (*p++)
4211556Srgrimes		;
42238887Stegge	if (quoted == 0)
42338887Stegge		recordregion(begoff, p - 1 - start, 0);
4241556Srgrimes	result = expdest - p + 1;
4251556Srgrimes	STADJUST(-result, expdest);
4261556Srgrimes}
4271556Srgrimes
4281556Srgrimes
4291556Srgrimes/*
4301556Srgrimes * Expand stuff in backwards quotes.
4311556Srgrimes */
4321556Srgrimes
4331556SrgrimesSTATIC void
4341556Srgrimesexpbackq(cmd, quoted, flag)
4351556Srgrimes	union node *cmd;
43617987Speter	int quoted;
43717987Speter	int flag;
43817987Speter{
4391556Srgrimes	struct backcmd in;
4401556Srgrimes	int i;
4411556Srgrimes	char buf[128];
4421556Srgrimes	char *p;
4431556Srgrimes	char *dest = expdest;
4441556Srgrimes	struct ifsregion saveifs, *savelastp;
4451556Srgrimes	struct nodelist *saveargbackq;
4461556Srgrimes	char lastc;
4471556Srgrimes	int startloc = dest - stackblock();
4481556Srgrimes	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
4491556Srgrimes	int saveherefd;
4501556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
4511556Srgrimes
4521556Srgrimes	INTOFF;
4531556Srgrimes	saveifs = ifsfirst;
4541556Srgrimes	savelastp = ifslastp;
4551556Srgrimes	saveargbackq = argbackq;
4568855Srgrimes	saveherefd = herefd;
4571556Srgrimes	herefd = -1;
4581556Srgrimes	p = grabstackstr(dest);
4591556Srgrimes	evalbackcmd(cmd, &in);
4601556Srgrimes	ungrabstackstr(p, dest);
4611556Srgrimes	ifsfirst = saveifs;
4621556Srgrimes	ifslastp = savelastp;
4631556Srgrimes	argbackq = saveargbackq;
4641556Srgrimes	herefd = saveherefd;
4651556Srgrimes
4661556Srgrimes	p = in.buf;
4671556Srgrimes	lastc = '\0';
4681556Srgrimes	for (;;) {
4691556Srgrimes		if (--in.nleft < 0) {
4701556Srgrimes			if (in.fd < 0)
4711556Srgrimes				break;
4721556Srgrimes			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
4731556Srgrimes			TRACE(("expbackq: read returns %d\n", i));
4741556Srgrimes			if (i <= 0)
4751556Srgrimes				break;
4761556Srgrimes			p = buf;
4771556Srgrimes			in.nleft = i - 1;
4781556Srgrimes		}
4791556Srgrimes		lastc = *p++;
4801556Srgrimes		if (lastc != '\0') {
48183675Stegge			if (quotes && syntax[(int)lastc] == CCTL)
4821556Srgrimes				STPUTC(CTLESC, dest);
4831556Srgrimes			STPUTC(lastc, dest);
4841556Srgrimes		}
4851556Srgrimes	}
48617987Speter
48717987Speter	/* Eat all trailing newlines */
48817987Speter	for (p--; lastc == '\n'; lastc = *--p)
4891556Srgrimes		STUNPUTC(dest);
49017987Speter
4911556Srgrimes	if (in.fd >= 0)
4921556Srgrimes		close(in.fd);
4931556Srgrimes	if (in.buf)
4941556Srgrimes		ckfree(in.buf);
4951556Srgrimes	if (in.jp)
49645916Scracauer		exitstatus = waitforjob(in.jp, (int *)NULL);
4971556Srgrimes	if (quoted == 0)
4981556Srgrimes		recordregion(startloc, dest - stackblock(), 0);
4991556Srgrimes	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5001556Srgrimes		(dest - stackblock()) - startloc,
5011556Srgrimes		(dest - stackblock()) - startloc,
5021556Srgrimes		stackblock() + startloc));
5031556Srgrimes	expdest = dest;
5041556Srgrimes	INTON;
5051556Srgrimes}
5061556Srgrimes
5071556Srgrimes
5081556Srgrimes
50917987SpeterSTATIC int
51020425Sstevesubevalvar(p, str, strloc, subtype, startloc, varflags)
51117987Speter	char *p;
51217987Speter	char *str;
51320425Ssteve	int strloc;
51417987Speter	int subtype;
51517987Speter	int startloc;
51617987Speter	int varflags;
51717987Speter{
51817987Speter	char *startp;
51917987Speter	char *loc = NULL;
52045514Stegge	char *q;
52117987Speter	int c = 0;
52217987Speter	int saveherefd = herefd;
52317987Speter	struct nodelist *saveargbackq = argbackq;
52420425Ssteve	int amount;
52520425Ssteve
52617987Speter	herefd = -1;
52717987Speter	argstr(p, 0);
52817987Speter	STACKSTRNUL(expdest);
52917987Speter	herefd = saveherefd;
53017987Speter	argbackq = saveargbackq;
53117987Speter	startp = stackblock() + startloc;
53220425Ssteve	if (str == NULL)
53320425Ssteve	    str = stackblock() + strloc;
53417987Speter
53517987Speter	switch (subtype) {
53617987Speter	case VSASSIGN:
53717987Speter		setvar(str, startp, 0);
53820425Ssteve		amount = startp - expdest;
53920425Ssteve		STADJUST(amount, expdest);
54017987Speter		varflags &= ~VSNUL;
54117987Speter		if (c != 0)
54217987Speter			*loc = c;
54317987Speter		return 1;
54417987Speter
54517987Speter	case VSQUESTION:
54617987Speter		if (*p != CTLENDVAR) {
54717987Speter			outfmt(&errout, "%s\n", startp);
54817987Speter			error((char *)NULL);
54917987Speter		}
55017987Speter		error("%.*s: parameter %snot set", p - str - 1,
55117987Speter		      str, (varflags & VSNUL) ? "null or "
55217987Speter					      : nullstr);
55317987Speter		return 0;
55417987Speter
55517987Speter	case VSTRIMLEFT:
55625233Ssteve		for (loc = startp; loc < str; loc++) {
55717987Speter			c = *loc;
55817987Speter			*loc = '\0';
55945514Stegge			if (patmatch(str, startp, varflags & VSQUOTE)) {
56017987Speter				*loc = c;
56117987Speter				goto recordleft;
56217987Speter			}
56317987Speter			*loc = c;
56445514Stegge			if ((varflags & VSQUOTE) && *loc == CTLESC)
56545514Stegge				loc++;
56617987Speter		}
56717987Speter		return 0;
56817987Speter
56917987Speter	case VSTRIMLEFTMAX:
57045514Stegge		for (loc = str - 1; loc >= startp;) {
57117987Speter			c = *loc;
57217987Speter			*loc = '\0';
57345514Stegge			if (patmatch(str, startp, varflags & VSQUOTE)) {
57417987Speter				*loc = c;
57517987Speter				goto recordleft;
57617987Speter			}
57717987Speter			*loc = c;
57845514Stegge			loc--;
57945514Stegge			if ((varflags & VSQUOTE) && loc > startp &&
58045514Stegge			    *(loc - 1) == CTLESC) {
58145514Stegge				for (q = startp; q < loc; q++)
58245514Stegge					if (*q == CTLESC)
58345514Stegge						q++;
58445514Stegge				if (q > loc)
58545514Stegge					loc--;
58645514Stegge			}
58717987Speter		}
58817987Speter		return 0;
58917987Speter
59017987Speter	case VSTRIMRIGHT:
59145514Stegge		for (loc = str - 1; loc >= startp;) {
59245514Stegge			if (patmatch(str, loc, varflags & VSQUOTE)) {
59320425Ssteve				amount = loc - expdest;
59420425Ssteve				STADJUST(amount, expdest);
59517987Speter				return 1;
59617987Speter			}
59745514Stegge			loc--;
59845514Stegge			if ((varflags & VSQUOTE) && loc > startp &&
59945514Stegge			    *(loc - 1) == CTLESC) {
60045514Stegge				for (q = startp; q < loc; q++)
60145514Stegge					if (*q == CTLESC)
60245514Stegge						q++;
60345514Stegge				if (q > loc)
60445514Stegge					loc--;
60545514Stegge			}
60617987Speter		}
60717987Speter		return 0;
60817987Speter
60917987Speter	case VSTRIMRIGHTMAX:
61017987Speter		for (loc = startp; loc < str - 1; loc++) {
61145514Stegge			if (patmatch(str, loc, varflags & VSQUOTE)) {
61220425Ssteve				amount = loc - expdest;
61320425Ssteve				STADJUST(amount, expdest);
61417987Speter				return 1;
61517987Speter			}
61645514Stegge			if ((varflags & VSQUOTE) && *loc == CTLESC)
61745514Stegge				loc++;
61817987Speter		}
61917987Speter		return 0;
62017987Speter
62117987Speter
62217987Speter	default:
62317987Speter		abort();
62417987Speter	}
62517987Speter
62617987Speterrecordleft:
62720425Ssteve	amount = ((str - 1) - (loc - startp)) - expdest;
62820425Ssteve	STADJUST(amount, expdest);
62917987Speter	while (loc != str - 1)
63017987Speter		*startp++ = *loc++;
63117987Speter	return 1;
63217987Speter}
63317987Speter
63417987Speter
6351556Srgrimes/*
6361556Srgrimes * Expand a variable, and return a pointer to the next character in the
6371556Srgrimes * input string.
6381556Srgrimes */
6391556Srgrimes
6401556SrgrimesSTATIC char *
6411556Srgrimesevalvar(p, flag)
6421556Srgrimes	char *p;
64317987Speter	int flag;
64417987Speter{
6451556Srgrimes	int subtype;
6461556Srgrimes	int varflags;
6471556Srgrimes	char *var;
6481556Srgrimes	char *val;
64945644Stegge	int patloc;
6501556Srgrimes	int c;
6511556Srgrimes	int set;
6521556Srgrimes	int special;
6531556Srgrimes	int startloc;
65417987Speter	int varlen;
65517987Speter	int easy;
6561556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
6571556Srgrimes
6581556Srgrimes	varflags = *p++;
6591556Srgrimes	subtype = varflags & VSTYPE;
6601556Srgrimes	var = p;
6611556Srgrimes	special = 0;
6621556Srgrimes	if (! is_name(*p))
6631556Srgrimes		special = 1;
6641556Srgrimes	p = strchr(p, '=') + 1;
6651556Srgrimesagain: /* jump here after setting a variable with ${var=text} */
6661556Srgrimes	if (special) {
66725233Ssteve		set = varisset(var, varflags & VSNUL);
6681556Srgrimes		val = NULL;
6691556Srgrimes	} else {
67060592Scracauer		val = bltinlookup(var, 1);
67117987Speter		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
6721556Srgrimes			val = NULL;
6731556Srgrimes			set = 0;
6741556Srgrimes		} else
6751556Srgrimes			set = 1;
6761556Srgrimes	}
67717987Speter	varlen = 0;
6781556Srgrimes	startloc = expdest - stackblock();
6791556Srgrimes	if (set && subtype != VSPLUS) {
6801556Srgrimes		/* insert the value of the variable */
6811556Srgrimes		if (special) {
68218202Speter			varvalue(var, varflags & VSQUOTE, flag & EXP_FULL);
68317987Speter			if (subtype == VSLENGTH) {
68425233Ssteve				varlen = expdest - stackblock() - startloc;
68525233Ssteve				STADJUST(-varlen, expdest);
68617987Speter			}
6871556Srgrimes		} else {
68820425Ssteve			char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
68917987Speter								  : BASESYNTAX;
6901556Srgrimes
69117987Speter			if (subtype == VSLENGTH) {
69217987Speter				for (;*val; val++)
69317987Speter					varlen++;
6941556Srgrimes			}
69517987Speter			else {
69617987Speter				while (*val) {
69783675Stegge					if (quotes &&
69854132Scracauer					    syntax[(int)*val] == CCTL)
69917987Speter						STPUTC(CTLESC, expdest);
70017987Speter					STPUTC(*val++, expdest);
70117987Speter				}
70217987Speter
70317987Speter			}
7041556Srgrimes		}
7051556Srgrimes	}
70620425Ssteve
7071556Srgrimes	if (subtype == VSPLUS)
7081556Srgrimes		set = ! set;
70917987Speter
71020425Ssteve	easy = ((varflags & VSQUOTE) == 0 ||
71117987Speter		(*var == '@' && shellparam.nparam != 1));
71217987Speter
71317987Speter
71417987Speter	switch (subtype) {
71517987Speter	case VSLENGTH:
71617987Speter		expdest = cvtnum(varlen, expdest);
71717987Speter		goto record;
71817987Speter
71917987Speter	case VSNORMAL:
72017987Speter		if (!easy)
72117987Speter			break;
72217987Speterrecord:
72320425Ssteve		recordregion(startloc, expdest - stackblock(),
72417987Speter			     varflags & VSQUOTE);
72517987Speter		break;
72617987Speter
72717987Speter	case VSPLUS:
72817987Speter	case VSMINUS:
72917987Speter		if (!set) {
7301556Srgrimes			argstr(p, flag);
73117987Speter			break;
73217987Speter		}
73317987Speter		if (easy)
73417987Speter			goto record;
73517987Speter		break;
73617987Speter
73717987Speter	case VSTRIMLEFT:
73817987Speter	case VSTRIMLEFTMAX:
73917987Speter	case VSTRIMRIGHT:
74017987Speter	case VSTRIMRIGHTMAX:
74117987Speter		if (!set)
74217987Speter			break;
74317987Speter		/*
74417987Speter		 * Terminate the string and start recording the pattern
74517987Speter		 * right after it
74617987Speter		 */
74717987Speter		STPUTC('\0', expdest);
74845644Stegge		patloc = expdest - stackblock();
74945644Stegge		if (subevalvar(p, NULL, patloc, subtype,
75038887Stegge			       startloc, varflags) == 0) {
75145644Stegge			int amount = (expdest - stackblock() - patloc) + 1;
75225233Ssteve			STADJUST(-amount, expdest);
75325233Ssteve		}
75438887Stegge		/* Remove any recorded regions beyond start of variable */
75538887Stegge		removerecordregions(startloc);
75638887Stegge		goto record;
75717987Speter
75817987Speter	case VSASSIGN:
75917987Speter	case VSQUESTION:
76017987Speter		if (!set) {
76120425Ssteve			if (subevalvar(p, var, 0, subtype, startloc, varflags)) {
76220425Ssteve				varflags &= ~VSNUL;
76338887Stegge				/*
76438887Stegge				 * Remove any recorded regions beyond
76538887Stegge				 * start of variable
76638887Stegge				 */
76738887Stegge				removerecordregions(startloc);
7681556Srgrimes				goto again;
76920425Ssteve			}
77017987Speter			break;
7711556Srgrimes		}
77217987Speter		if (easy)
77317987Speter			goto record;
77417987Speter		break;
77517987Speter
77617987Speter	default:
77717987Speter		abort();
7781556Srgrimes	}
77917987Speter
7801556Srgrimes	if (subtype != VSNORMAL) {	/* skip to end of alternative */
7811556Srgrimes		int nesting = 1;
7821556Srgrimes		for (;;) {
7831556Srgrimes			if ((c = *p++) == CTLESC)
7841556Srgrimes				p++;
7851556Srgrimes			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
7861556Srgrimes				if (set)
7871556Srgrimes					argbackq = argbackq->next;
7881556Srgrimes			} else if (c == CTLVAR) {
7891556Srgrimes				if ((*p++ & VSTYPE) != VSNORMAL)
7901556Srgrimes					nesting++;
7911556Srgrimes			} else if (c == CTLENDVAR) {
7921556Srgrimes				if (--nesting == 0)
7931556Srgrimes					break;
7941556Srgrimes			}
7951556Srgrimes		}
7961556Srgrimes	}
7971556Srgrimes	return p;
7981556Srgrimes}
7991556Srgrimes
8001556Srgrimes
8011556Srgrimes
8021556Srgrimes/*
8031556Srgrimes * Test whether a specialized variable is set.
8041556Srgrimes */
8051556Srgrimes
8061556SrgrimesSTATIC int
80725233Sstevevarisset(name, nulok)
80818202Speter	char *name;
80925233Ssteve	int nulok;
81025233Ssteve{
8111556Srgrimes
81225233Ssteve	if (*name == '!')
81325233Ssteve		return backgndpid != -1;
81425233Ssteve	else if (*name == '@' || *name == '*') {
8151556Srgrimes		if (*shellparam.p == NULL)
8161556Srgrimes			return 0;
81725233Ssteve
81825233Ssteve		if (nulok) {
81925233Ssteve			char **av;
82025233Ssteve
82125233Ssteve			for (av = shellparam.p; *av; av++)
82225233Ssteve				if (**av != '\0')
82325233Ssteve					return 1;
82425233Ssteve			return 0;
82525233Ssteve		}
82618202Speter	} else if (is_digit(*name)) {
82725233Ssteve		char *ap;
82820425Ssteve		int num = atoi(name);
82925233Ssteve
83025233Ssteve		if (num > shellparam.nparam)
83125233Ssteve			return 0;
83225233Ssteve
83325233Ssteve		if (num == 0)
83425233Ssteve			ap = arg0;
83525233Ssteve		else
83625233Ssteve			ap = shellparam.p[num - 1];
83725233Ssteve
83825233Ssteve		if (nulok && (ap == NULL || *ap == '\0'))
83925233Ssteve			return 0;
8401556Srgrimes	}
8411556Srgrimes	return 1;
8421556Srgrimes}
8431556Srgrimes
8441556Srgrimes
8451556Srgrimes
8461556Srgrimes/*
8471556Srgrimes * Add the value of a specialized variable to the stack string.
8481556Srgrimes */
8491556Srgrimes
8501556SrgrimesSTATIC void
8511556Srgrimesvarvalue(name, quoted, allow_split)
85218202Speter	char *name;
85317987Speter	int quoted;
85417987Speter	int allow_split;
85517987Speter{
8561556Srgrimes	int num;
8571556Srgrimes	char *p;
8581556Srgrimes	int i;
85917987Speter	extern int oexitstatus;
8601556Srgrimes	char sep;
8611556Srgrimes	char **ap;
8621556Srgrimes	char const *syntax;
8631556Srgrimes
8641556Srgrimes#define STRTODEST(p) \
8651556Srgrimes	do {\
8661556Srgrimes	if (allow_split) { \
8671556Srgrimes		syntax = quoted? DQSYNTAX : BASESYNTAX; \
8681556Srgrimes		while (*p) { \
86983675Stegge			if (syntax[(int)*p] == CCTL) \
8701556Srgrimes				STPUTC(CTLESC, expdest); \
8711556Srgrimes			STPUTC(*p++, expdest); \
8721556Srgrimes		} \
8731556Srgrimes	} else \
8741556Srgrimes		while (*p) \
8751556Srgrimes			STPUTC(*p++, expdest); \
8761556Srgrimes	} while (0)
8771556Srgrimes
8781556Srgrimes
87918202Speter	switch (*name) {
8801556Srgrimes	case '$':
8811556Srgrimes		num = rootpid;
8821556Srgrimes		goto numvar;
8831556Srgrimes	case '?':
88417987Speter		num = oexitstatus;
8851556Srgrimes		goto numvar;
8861556Srgrimes	case '#':
8871556Srgrimes		num = shellparam.nparam;
8881556Srgrimes		goto numvar;
8891556Srgrimes	case '!':
8901556Srgrimes		num = backgndpid;
8911556Srgrimesnumvar:
89217987Speter		expdest = cvtnum(num, expdest);
8931556Srgrimes		break;
8941556Srgrimes	case '-':
8951556Srgrimes		for (i = 0 ; i < NOPTS ; i++) {
8961556Srgrimes			if (optlist[i].val)
8971556Srgrimes				STPUTC(optlist[i].letter, expdest);
8981556Srgrimes		}
8991556Srgrimes		break;
9001556Srgrimes	case '@':
90138887Stegge		if (allow_split && quoted) {
90238887Stegge			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
90338887Stegge				STRTODEST(p);
90438887Stegge				if (*ap)
90538887Stegge					STPUTC('\0', expdest);
90638887Stegge			}
90738887Stegge			break;
9081556Srgrimes		}
9098855Srgrimes		/* fall through */
9101556Srgrimes	case '*':
91138887Stegge		if (ifsset() != 0)
91238887Stegge			sep = ifsval()[0];
91338887Stegge		else
91438887Stegge			sep = ' ';
9151556Srgrimes		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
9161556Srgrimes			STRTODEST(p);
91738887Stegge			if (*ap && sep)
9181556Srgrimes				STPUTC(sep, expdest);
9191556Srgrimes		}
9201556Srgrimes		break;
9211556Srgrimes	case '0':
9221556Srgrimes		p = arg0;
9231556Srgrimes		STRTODEST(p);
9241556Srgrimes		break;
9251556Srgrimes	default:
92618202Speter		if (is_digit(*name)) {
92718202Speter			num = atoi(name);
92818202Speter			if (num > 0 && num <= shellparam.nparam) {
92918202Speter				p = shellparam.p[num - 1];
93018202Speter				STRTODEST(p);
93118202Speter			}
9321556Srgrimes		}
9331556Srgrimes		break;
9341556Srgrimes	}
9351556Srgrimes}
9361556Srgrimes
9371556Srgrimes
9381556Srgrimes
9391556Srgrimes/*
9401556Srgrimes * Record the the fact that we have to scan this region of the
9411556Srgrimes * string for IFS characters.
9421556Srgrimes */
9431556Srgrimes
9441556SrgrimesSTATIC void
94520425Ssteverecordregion(start, end, nulonly)
94617987Speter	int start;
94717987Speter	int end;
94817987Speter	int nulonly;
94917987Speter{
95025233Ssteve	struct ifsregion *ifsp;
9511556Srgrimes
9521556Srgrimes	if (ifslastp == NULL) {
9531556Srgrimes		ifsp = &ifsfirst;
9541556Srgrimes	} else {
9551556Srgrimes		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
9561556Srgrimes		ifslastp->next = ifsp;
9571556Srgrimes	}
9581556Srgrimes	ifslastp = ifsp;
9591556Srgrimes	ifslastp->next = NULL;
9601556Srgrimes	ifslastp->begoff = start;
9611556Srgrimes	ifslastp->endoff = end;
9621556Srgrimes	ifslastp->nulonly = nulonly;
9631556Srgrimes}
9641556Srgrimes
9651556Srgrimes
9661556Srgrimes
9671556Srgrimes/*
9681556Srgrimes * Break the argument string into pieces based upon IFS and add the
9691556Srgrimes * strings to the argument list.  The regions of the string to be
9701556Srgrimes * searched for IFS characters have been stored by recordregion.
9711556Srgrimes */
9721556SrgrimesSTATIC void
9731556Srgrimesifsbreakup(string, arglist)
9741556Srgrimes	char *string;
9751556Srgrimes	struct arglist *arglist;
9761556Srgrimes	{
9771556Srgrimes	struct ifsregion *ifsp;
9781556Srgrimes	struct strlist *sp;
9791556Srgrimes	char *start;
98025233Ssteve	char *p;
9811556Srgrimes	char *q;
9821556Srgrimes	char *ifs;
98317987Speter	int ifsspc;
98438887Stegge	int nulonly;
9851556Srgrimes
98617987Speter
9871556Srgrimes	start = string;
98838887Stegge	ifsspc = 0;
98938887Stegge	nulonly = 0;
9901556Srgrimes	if (ifslastp != NULL) {
9911556Srgrimes		ifsp = &ifsfirst;
9921556Srgrimes		do {
9931556Srgrimes			p = string + ifsp->begoff;
99438887Stegge			nulonly = ifsp->nulonly;
99538887Stegge			ifs = nulonly ? nullstr :
99638887Stegge				( ifsset() ? ifsval() : " \t\n" );
99738887Stegge			ifsspc = 0;
9981556Srgrimes			while (p < string + ifsp->endoff) {
9991556Srgrimes				q = p;
10001556Srgrimes				if (*p == CTLESC)
10011556Srgrimes					p++;
100238887Stegge				if (strchr(ifs, *p)) {
100338887Stegge					if (!nulonly)
100438887Stegge						ifsspc = (strchr(" \t\n", *p) != NULL);
100538887Stegge					/* Ignore IFS whitespace at start */
100638887Stegge					if (q == start && ifsspc) {
100738887Stegge						p++;
100838887Stegge						start = p;
100938887Stegge						continue;
10101556Srgrimes					}
101138887Stegge					*q = '\0';
101238887Stegge					sp = (struct strlist *)stalloc(sizeof *sp);
101338887Stegge					sp->text = start;
101438887Stegge					*arglist->lastp = sp;
101538887Stegge					arglist->lastp = &sp->next;
101638887Stegge					p++;
101738887Stegge					if (!nulonly) {
10181556Srgrimes						for (;;) {
101938887Stegge							if (p >= string + ifsp->endoff) {
10201556Srgrimes								break;
102138887Stegge							}
10221556Srgrimes							q = p;
10231556Srgrimes							if (*p == CTLESC)
10241556Srgrimes								p++;
102538887Stegge							if (strchr(ifs, *p) == NULL ) {
10261556Srgrimes								p = q;
10271556Srgrimes								break;
102838887Stegge							} else if (strchr(" \t\n",*p) == NULL) {
102938887Stegge								if (ifsspc) {
103038887Stegge									p++;
103138887Stegge									ifsspc = 0;
103238887Stegge								} else {
103338887Stegge									p = q;
103438887Stegge									break;
103538887Stegge								}
103638887Stegge							} else
103738887Stegge								p++;
10381556Srgrimes						}
10391556Srgrimes					}
10401556Srgrimes					start = p;
104138887Stegge				} else
104238887Stegge					p++;
10431556Srgrimes			}
10441556Srgrimes		} while ((ifsp = ifsp->next) != NULL);
104538887Stegge		if (*start || (!ifsspc && start > string &&
104638887Stegge			(nulonly || 1))) {
10471556Srgrimes			sp = (struct strlist *)stalloc(sizeof *sp);
10481556Srgrimes			sp->text = start;
10491556Srgrimes			*arglist->lastp = sp;
10501556Srgrimes			arglist->lastp = &sp->next;
10511556Srgrimes		}
10521556Srgrimes	} else {
10531556Srgrimes		sp = (struct strlist *)stalloc(sizeof *sp);
10541556Srgrimes		sp->text = start;
10551556Srgrimes		*arglist->lastp = sp;
10561556Srgrimes		arglist->lastp = &sp->next;
10571556Srgrimes	}
10581556Srgrimes}
10591556Srgrimes
10601556Srgrimes
10611556Srgrimes
10621556Srgrimes/*
10631556Srgrimes * Expand shell metacharacters.  At this point, the only control characters
10641556Srgrimes * should be escapes.  The results are stored in the list exparg.
10651556Srgrimes */
10661556Srgrimes
10671556Srgrimeschar *expdir;
10681556Srgrimes
10691556Srgrimes
10701556SrgrimesSTATIC void
10711556Srgrimesexpandmeta(str, flag)
10721556Srgrimes	struct strlist *str;
107325905Ssteve	int flag __unused;
107417987Speter{
10751556Srgrimes	char *p;
10761556Srgrimes	struct strlist **savelastp;
10771556Srgrimes	struct strlist *sp;
10781556Srgrimes	char c;
10791556Srgrimes	/* TODO - EXP_REDIR */
10801556Srgrimes
10811556Srgrimes	while (str) {
10821556Srgrimes		if (fflag)
10831556Srgrimes			goto nometa;
10841556Srgrimes		p = str->text;
10851556Srgrimes		for (;;) {			/* fast check for meta chars */
10861556Srgrimes			if ((c = *p++) == '\0')
10871556Srgrimes				goto nometa;
10881556Srgrimes			if (c == '*' || c == '?' || c == '[' || c == '!')
10891556Srgrimes				break;
10901556Srgrimes		}
10911556Srgrimes		savelastp = exparg.lastp;
10921556Srgrimes		INTOFF;
10931556Srgrimes		if (expdir == NULL) {
10941556Srgrimes			int i = strlen(str->text);
10951556Srgrimes			expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
10961556Srgrimes		}
10971556Srgrimes
10981556Srgrimes		expmeta(expdir, str->text);
10991556Srgrimes		ckfree(expdir);
11001556Srgrimes		expdir = NULL;
11011556Srgrimes		INTON;
11021556Srgrimes		if (exparg.lastp == savelastp) {
11038855Srgrimes			/*
11048855Srgrimes			 * no matches
11051556Srgrimes			 */
11061556Srgrimesnometa:
11071556Srgrimes			*exparg.lastp = str;
11081556Srgrimes			rmescapes(str->text);
11091556Srgrimes			exparg.lastp = &str->next;
11101556Srgrimes		} else {
11111556Srgrimes			*exparg.lastp = NULL;
11121556Srgrimes			*savelastp = sp = expsort(*savelastp);
11131556Srgrimes			while (sp->next != NULL)
11141556Srgrimes				sp = sp->next;
11151556Srgrimes			exparg.lastp = &sp->next;
11161556Srgrimes		}
11171556Srgrimes		str = str->next;
11181556Srgrimes	}
11191556Srgrimes}
11201556Srgrimes
11211556Srgrimes
11221556Srgrimes/*
11231556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion.
11241556Srgrimes */
11251556Srgrimes
11261556SrgrimesSTATIC void
11271556Srgrimesexpmeta(enddir, name)
11281556Srgrimes	char *enddir;
11291556Srgrimes	char *name;
11301556Srgrimes	{
113125233Ssteve	char *p;
11321556Srgrimes	char *q;
11331556Srgrimes	char *start;
11341556Srgrimes	char *endname;
11351556Srgrimes	int metaflag;
11361556Srgrimes	struct stat statb;
11371556Srgrimes	DIR *dirp;
11381556Srgrimes	struct dirent *dp;
11391556Srgrimes	int atend;
11401556Srgrimes	int matchdot;
11411556Srgrimes
11421556Srgrimes	metaflag = 0;
11431556Srgrimes	start = name;
11441556Srgrimes	for (p = name ; ; p++) {
11451556Srgrimes		if (*p == '*' || *p == '?')
11461556Srgrimes			metaflag = 1;
11471556Srgrimes		else if (*p == '[') {
11481556Srgrimes			q = p + 1;
114926488Sache			if (*q == '!' || *q == '^')
11501556Srgrimes				q++;
11511556Srgrimes			for (;;) {
115238887Stegge				while (*q == CTLQUOTEMARK)
115338887Stegge					q++;
11541556Srgrimes				if (*q == CTLESC)
11551556Srgrimes					q++;
11561556Srgrimes				if (*q == '/' || *q == '\0')
11571556Srgrimes					break;
11581556Srgrimes				if (*++q == ']') {
11591556Srgrimes					metaflag = 1;
11601556Srgrimes					break;
11611556Srgrimes				}
11621556Srgrimes			}
11631556Srgrimes		} else if (*p == '!' && p[1] == '!'	&& (p == name || p[-1] == '/')) {
11641556Srgrimes			metaflag = 1;
11651556Srgrimes		} else if (*p == '\0')
11661556Srgrimes			break;
116738887Stegge		else if (*p == CTLQUOTEMARK)
116838887Stegge			continue;
11691556Srgrimes		else if (*p == CTLESC)
11701556Srgrimes			p++;
11711556Srgrimes		if (*p == '/') {
11721556Srgrimes			if (metaflag)
11731556Srgrimes				break;
11741556Srgrimes			start = p + 1;
11751556Srgrimes		}
11761556Srgrimes	}
11771556Srgrimes	if (metaflag == 0) {	/* we've reached the end of the file name */
11781556Srgrimes		if (enddir != expdir)
11791556Srgrimes			metaflag++;
11801556Srgrimes		for (p = name ; ; p++) {
118138887Stegge			if (*p == CTLQUOTEMARK)
118238887Stegge				continue;
11831556Srgrimes			if (*p == CTLESC)
11841556Srgrimes				p++;
11851556Srgrimes			*enddir++ = *p;
11861556Srgrimes			if (*p == '\0')
11871556Srgrimes				break;
11881556Srgrimes		}
11891556Srgrimes		if (metaflag == 0 || stat(expdir, &statb) >= 0)
11901556Srgrimes			addfname(expdir);
11911556Srgrimes		return;
11921556Srgrimes	}
11931556Srgrimes	endname = p;
11941556Srgrimes	if (start != name) {
11951556Srgrimes		p = name;
11961556Srgrimes		while (p < start) {
119738887Stegge			while (*p == CTLQUOTEMARK)
119838887Stegge				p++;
11991556Srgrimes			if (*p == CTLESC)
12001556Srgrimes				p++;
12011556Srgrimes			*enddir++ = *p++;
12021556Srgrimes		}
12031556Srgrimes	}
12041556Srgrimes	if (enddir == expdir) {
12051556Srgrimes		p = ".";
12061556Srgrimes	} else if (enddir == expdir + 1 && *expdir == '/') {
12071556Srgrimes		p = "/";
12081556Srgrimes	} else {
12091556Srgrimes		p = expdir;
12101556Srgrimes		enddir[-1] = '\0';
12111556Srgrimes	}
12121556Srgrimes	if ((dirp = opendir(p)) == NULL)
12131556Srgrimes		return;
12141556Srgrimes	if (enddir != expdir)
12151556Srgrimes		enddir[-1] = '/';
12161556Srgrimes	if (*endname == 0) {
12171556Srgrimes		atend = 1;
12181556Srgrimes	} else {
12191556Srgrimes		atend = 0;
12201556Srgrimes		*endname++ = '\0';
12211556Srgrimes	}
12221556Srgrimes	matchdot = 0;
122338887Stegge	p = start;
122438887Stegge	while (*p == CTLQUOTEMARK)
122538887Stegge		p++;
122638887Stegge	if (*p == CTLESC)
122738887Stegge		p++;
122838887Stegge	if (*p == '.')
12291556Srgrimes		matchdot++;
12301556Srgrimes	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
12311556Srgrimes		if (dp->d_name[0] == '.' && ! matchdot)
12321556Srgrimes			continue;
123345514Stegge		if (patmatch(start, dp->d_name, 0)) {
12341556Srgrimes			if (atend) {
12351556Srgrimes				scopy(dp->d_name, enddir);
12361556Srgrimes				addfname(expdir);
12371556Srgrimes			} else {
12381556Srgrimes				char *q;
123917987Speter				for (p = enddir, q = dp->d_name;
124017987Speter				     (*p++ = *q++) != '\0';)
124117987Speter					continue;
12421556Srgrimes				p[-1] = '/';
12431556Srgrimes				expmeta(p, endname);
12441556Srgrimes			}
12451556Srgrimes		}
12461556Srgrimes	}
12471556Srgrimes	closedir(dirp);
12481556Srgrimes	if (! atend)
12491556Srgrimes		endname[-1] = '/';
12501556Srgrimes}
12511556Srgrimes
12521556Srgrimes
12531556Srgrimes/*
12541556Srgrimes * Add a file name to the list.
12551556Srgrimes */
12561556Srgrimes
12571556SrgrimesSTATIC void
12581556Srgrimesaddfname(name)
12591556Srgrimes	char *name;
12601556Srgrimes	{
12611556Srgrimes	char *p;
12621556Srgrimes	struct strlist *sp;
12631556Srgrimes
12641556Srgrimes	p = stalloc(strlen(name) + 1);
12651556Srgrimes	scopy(name, p);
12661556Srgrimes	sp = (struct strlist *)stalloc(sizeof *sp);
12671556Srgrimes	sp->text = p;
12681556Srgrimes	*exparg.lastp = sp;
12691556Srgrimes	exparg.lastp = &sp->next;
12701556Srgrimes}
12711556Srgrimes
12721556Srgrimes
12731556Srgrimes/*
12741556Srgrimes * Sort the results of file name expansion.  It calculates the number of
12751556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the
12761556Srgrimes * work.
12771556Srgrimes */
12781556Srgrimes
12791556SrgrimesSTATIC struct strlist *
12801556Srgrimesexpsort(str)
12811556Srgrimes	struct strlist *str;
12821556Srgrimes	{
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 *
12941556Srgrimesmsort(list, len)
12951556Srgrimes	struct strlist *list;
129617987Speter	int len;
129717987Speter{
129817987Speter	struct strlist *p, *q = NULL;
12991556Srgrimes	struct strlist **lpp;
13001556Srgrimes	int half;
13011556Srgrimes	int n;
13021556Srgrimes
13031556Srgrimes	if (len <= 1)
13041556Srgrimes		return list;
13058855Srgrimes	half = len >> 1;
13061556Srgrimes	p = list;
13071556Srgrimes	for (n = half ; --n >= 0 ; ) {
13081556Srgrimes		q = p;
13091556Srgrimes		p = p->next;
13101556Srgrimes	}
13111556Srgrimes	q->next = NULL;			/* terminate first half of list */
13121556Srgrimes	q = msort(list, half);		/* sort first half of list */
13131556Srgrimes	p = msort(p, len - half);		/* sort second half */
13141556Srgrimes	lpp = &list;
13151556Srgrimes	for (;;) {
13161556Srgrimes		if (strcmp(p->text, q->text) < 0) {
13171556Srgrimes			*lpp = p;
13181556Srgrimes			lpp = &p->next;
13191556Srgrimes			if ((p = *lpp) == NULL) {
13201556Srgrimes				*lpp = q;
13211556Srgrimes				break;
13221556Srgrimes			}
13231556Srgrimes		} else {
13241556Srgrimes			*lpp = q;
13251556Srgrimes			lpp = &q->next;
13261556Srgrimes			if ((q = *lpp) == NULL) {
13271556Srgrimes				*lpp = p;
13281556Srgrimes				break;
13291556Srgrimes			}
13301556Srgrimes		}
13311556Srgrimes	}
13321556Srgrimes	return list;
13331556Srgrimes}
13341556Srgrimes
13351556Srgrimes
13361556Srgrimes
13371556Srgrimes/*
13381556Srgrimes * Returns true if the pattern matches the string.
13391556Srgrimes */
13401556Srgrimes
13411556Srgrimesint
134245514Steggepatmatch(pattern, string, squoted)
13431556Srgrimes	char *pattern;
13441556Srgrimes	char *string;
134545514Stegge	int squoted;	/* string might have quote chars */
13461556Srgrimes	{
13471556Srgrimes#ifdef notdef
13481556Srgrimes	if (pattern[0] == '!' && pattern[1] == '!')
13491556Srgrimes		return 1 - pmatch(pattern + 2, string);
13501556Srgrimes	else
13511556Srgrimes#endif
135245514Stegge		return pmatch(pattern, string, squoted);
13531556Srgrimes}
13541556Srgrimes
135517987Speter
135617525SacheSTATIC int
135745514Steggepmatch(pattern, string, squoted)
13581556Srgrimes	char *pattern;
13591556Srgrimes	char *string;
136045514Stegge	int squoted;
13611556Srgrimes	{
136225233Ssteve	char *p, *q;
136325233Ssteve	char c;
13641556Srgrimes
13651556Srgrimes	p = pattern;
13661556Srgrimes	q = string;
13671556Srgrimes	for (;;) {
13681556Srgrimes		switch (c = *p++) {
13691556Srgrimes		case '\0':
13701556Srgrimes			goto breakloop;
13711556Srgrimes		case CTLESC:
137245514Stegge			if (squoted && *q == CTLESC)
137345514Stegge				q++;
13741556Srgrimes			if (*q++ != *p++)
13751556Srgrimes				return 0;
13761556Srgrimes			break;
137738887Stegge		case CTLQUOTEMARK:
137838887Stegge			continue;
13791556Srgrimes		case '?':
138045514Stegge			if (squoted && *q == CTLESC)
138145514Stegge				q++;
13821556Srgrimes			if (*q++ == '\0')
13831556Srgrimes				return 0;
13841556Srgrimes			break;
13851556Srgrimes		case '*':
13861556Srgrimes			c = *p;
138738887Stegge			while (c == CTLQUOTEMARK || c == '*')
138838887Stegge				c = *++p;
138938887Stegge			if (c != CTLESC &&  c != CTLQUOTEMARK &&
139038887Stegge			    c != '?' && c != '*' && c != '[') {
13911556Srgrimes				while (*q != c) {
139245514Stegge					if (squoted && *q == CTLESC &&
139345514Stegge					    q[1] == c)
139445514Stegge						break;
13951556Srgrimes					if (*q == '\0')
13961556Srgrimes						return 0;
139745514Stegge					if (squoted && *q == CTLESC)
139845514Stegge						q++;
13991556Srgrimes					q++;
14001556Srgrimes				}
14011556Srgrimes			}
14021556Srgrimes			do {
140345514Stegge				if (pmatch(p, q, squoted))
14041556Srgrimes					return 1;
140545514Stegge				if (squoted && *q == CTLESC)
140645514Stegge					q++;
14071556Srgrimes			} while (*q++ != '\0');
14081556Srgrimes			return 0;
14091556Srgrimes		case '[': {
14101556Srgrimes			char *endp;
14111556Srgrimes			int invert, found;
14121556Srgrimes			char chr;
14131556Srgrimes
14141556Srgrimes			endp = p;
141526488Sache			if (*endp == '!' || *endp == '^')
14161556Srgrimes				endp++;
14171556Srgrimes			for (;;) {
141838887Stegge				while (*endp == CTLQUOTEMARK)
141938887Stegge					endp++;
14201556Srgrimes				if (*endp == '\0')
14211556Srgrimes					goto dft;		/* no matching ] */
14221556Srgrimes				if (*endp == CTLESC)
14231556Srgrimes					endp++;
14241556Srgrimes				if (*++endp == ']')
14251556Srgrimes					break;
14261556Srgrimes			}
14271556Srgrimes			invert = 0;
142826488Sache			if (*p == '!' || *p == '^') {
14291556Srgrimes				invert++;
14301556Srgrimes				p++;
14311556Srgrimes			}
14321556Srgrimes			found = 0;
14331556Srgrimes			chr = *q++;
143445514Stegge			if (squoted && chr == CTLESC)
143545514Stegge				chr = *q++;
143617987Speter			if (chr == '\0')
143717987Speter				return 0;
14381556Srgrimes			c = *p++;
14391556Srgrimes			do {
144038887Stegge				if (c == CTLQUOTEMARK)
144138887Stegge					continue;
14421556Srgrimes				if (c == CTLESC)
14431556Srgrimes					c = *p++;
14441556Srgrimes				if (*p == '-' && p[1] != ']') {
14451556Srgrimes					p++;
144638887Stegge					while (*p == CTLQUOTEMARK)
144738887Stegge						p++;
14481556Srgrimes					if (*p == CTLESC)
14491556Srgrimes						p++;
145017557Sache					if (   collate_range_cmp(chr, c) >= 0
145117557Sache					    && collate_range_cmp(chr, *p) <= 0
145217525Sache					   )
14531556Srgrimes						found = 1;
14541556Srgrimes					p++;
14551556Srgrimes				} else {
14561556Srgrimes					if (chr == c)
14571556Srgrimes						found = 1;
14581556Srgrimes				}
14591556Srgrimes			} while ((c = *p++) != ']');
14601556Srgrimes			if (found == invert)
14611556Srgrimes				return 0;
14621556Srgrimes			break;
14631556Srgrimes		}
14641556Srgrimesdft:	        default:
146545514Stegge			if (squoted && *q == CTLESC)
146645514Stegge				q++;
14671556Srgrimes			if (*q++ != c)
14681556Srgrimes				return 0;
14691556Srgrimes			break;
14701556Srgrimes		}
14711556Srgrimes	}
14721556Srgrimesbreakloop:
14731556Srgrimes	if (*q != '\0')
14741556Srgrimes		return 0;
14751556Srgrimes	return 1;
14761556Srgrimes}
14771556Srgrimes
14781556Srgrimes
14791556Srgrimes
14801556Srgrimes/*
14811556Srgrimes * Remove any CTLESC characters from a string.
14821556Srgrimes */
14831556Srgrimes
14841556Srgrimesvoid
14851556Srgrimesrmescapes(str)
14861556Srgrimes	char *str;
148738887Stegge{
148825233Ssteve	char *p, *q;
14891556Srgrimes
14901556Srgrimes	p = str;
149138887Stegge	while (*p != CTLESC && *p != CTLQUOTEMARK) {
14921556Srgrimes		if (*p++ == '\0')
14931556Srgrimes			return;
14941556Srgrimes	}
14951556Srgrimes	q = p;
14961556Srgrimes	while (*p) {
149738887Stegge		if (*p == CTLQUOTEMARK) {
149838887Stegge			p++;
149938887Stegge			continue;
150038887Stegge		}
15011556Srgrimes		if (*p == CTLESC)
15021556Srgrimes			p++;
15031556Srgrimes		*q++ = *p++;
15041556Srgrimes	}
15051556Srgrimes	*q = '\0';
15061556Srgrimes}
15071556Srgrimes
15081556Srgrimes
15091556Srgrimes
15101556Srgrimes/*
15111556Srgrimes * See if a pattern matches in a case statement.
15121556Srgrimes */
15131556Srgrimes
15141556Srgrimesint
15151556Srgrimescasematch(pattern, val)
15161556Srgrimes	union node *pattern;
15171556Srgrimes	char *val;
15181556Srgrimes	{
15191556Srgrimes	struct stackmark smark;
15201556Srgrimes	int result;
15211556Srgrimes	char *p;
15221556Srgrimes
15231556Srgrimes	setstackmark(&smark);
15241556Srgrimes	argbackq = pattern->narg.backquote;
15251556Srgrimes	STARTSTACKSTR(expdest);
15261556Srgrimes	ifslastp = NULL;
15271556Srgrimes	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
15281556Srgrimes	STPUTC('\0', expdest);
15291556Srgrimes	p = grabstackstr(expdest);
153045514Stegge	result = patmatch(p, val, 0);
15311556Srgrimes	popstackmark(&smark);
15321556Srgrimes	return result;
15331556Srgrimes}
153417987Speter
153517987Speter/*
153617987Speter * Our own itoa().
153717987Speter */
153817987Speter
153917987SpeterSTATIC char *
154017987Spetercvtnum(num, buf)
154117987Speter	int num;
154217987Speter	char *buf;
154317987Speter	{
154417987Speter	char temp[32];
154517987Speter	int neg = num < 0;
154617987Speter	char *p = temp + 31;
154717987Speter
154817987Speter	temp[31] = '\0';
154917987Speter
155017987Speter	do {
155117987Speter		*--p = num % 10 + '0';
155217987Speter	} while ((num /= 10) != 0);
155317987Speter
155417987Speter	if (neg)
155517987Speter		*--p = '-';
155617987Speter
155717987Speter	while (*p)
155817987Speter		STPUTC(*p++, buf);
155917987Speter	return buf;
156017987Speter}
1561