expand.c revision 26488
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.
353044Sdg *
3626488Sache *	$Id: expand.c,v 1.18 1997/05/19 00:18:40 steve Exp $
371556Srgrimes */
381556Srgrimes
391556Srgrimes#ifndef lint
4020425Sstevestatic char const sccsid[] = "@(#)expand.c	8.5 (Berkeley) 5/15/95";
411556Srgrimes#endif /* not lint */
421556Srgrimes
4317987Speter#include <sys/types.h>
4417987Speter#include <sys/time.h>
4517987Speter#include <sys/stat.h>
4617987Speter#include <errno.h>
4717987Speter#include <dirent.h>
4817987Speter#include <unistd.h>
4917987Speter#include <pwd.h>
5017987Speter#include <stdlib.h>
5119281Sache#include <limits.h>
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 */
851556Srgrimes	int nulonly;		/* search for nul bytes only */
861556Srgrimes};
871556Srgrimes
881556Srgrimes
891556Srgrimeschar *expdest;			/* output of current string */
901556Srgrimesstruct nodelist *argbackq;	/* list of back quote expressions */
911556Srgrimesstruct ifsregion ifsfirst;	/* first struct in list of ifs regions */
921556Srgrimesstruct ifsregion *ifslastp;	/* last struct in list */
931556Srgrimesstruct arglist exparg;		/* holds expanded arg list */
941556Srgrimes
9517987SpeterSTATIC void argstr __P((char *, int));
9617987SpeterSTATIC char *exptilde __P((char *, int));
9717987SpeterSTATIC void expbackq __P((union node *, int, int));
9820425SsteveSTATIC int subevalvar __P((char *, char *, int, int, int, int));
9917987SpeterSTATIC char *evalvar __P((char *, int));
10025233SsteveSTATIC int varisset __P((char *, int));
10118202SpeterSTATIC void varvalue __P((char *, int, int));
10217987SpeterSTATIC void recordregion __P((int, int, int));
10317987SpeterSTATIC void ifsbreakup __P((char *, struct arglist *));
10417987SpeterSTATIC void expandmeta __P((struct strlist *, int));
10517987SpeterSTATIC void expmeta __P((char *, char *));
10617987SpeterSTATIC void addfname __P((char *));
10717987SpeterSTATIC struct strlist *expsort __P((struct strlist *));
10817987SpeterSTATIC struct strlist *msort __P((struct strlist *, int));
10917987SpeterSTATIC int pmatch __P((char *, char *));
11017987SpeterSTATIC char *cvtnum __P((int, char *));
11119281SacheSTATIC int collate_range_cmp __P((int, int));
1121556Srgrimes
11319281SacheSTATIC int collate_range_cmp (c1, c2)
11419281Sache	int c1, c2;
11519281Sache{
11619281Sache	static char s1[2], s2[2];
11719281Sache	int ret;
11819281Sache
11919281Sache	c1 &= UCHAR_MAX;
12019281Sache	c2 &= UCHAR_MAX;
12119281Sache	if (c1 == c2)
12219281Sache		return (0);
12319281Sache	s1[0] = c1;
12419281Sache	s2[0] = c2;
12519281Sache	if ((ret = strcoll(s1, s2)) != 0)
12619281Sache		return (ret);
12719281Sache	return (c1 - c2);
12819281Sache}
12919281Sache
1301556Srgrimes/*
1311556Srgrimes * Expand shell variables and backquotes inside a here document.
1321556Srgrimes */
1331556Srgrimes
1341556Srgrimesvoid
1351556Srgrimesexpandhere(arg, fd)
1361556Srgrimes	union node *arg;	/* the document */
1371556Srgrimes	int fd;			/* where to write the expanded version */
1381556Srgrimes	{
1391556Srgrimes	herefd = fd;
1401556Srgrimes	expandarg(arg, (struct arglist *)NULL, 0);
1411556Srgrimes	xwrite(fd, stackblock(), expdest - stackblock());
1421556Srgrimes}
1431556Srgrimes
1441556Srgrimes
1451556Srgrimes/*
1461556Srgrimes * Perform variable substitution and command substitution on an argument,
1471556Srgrimes * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
1481556Srgrimes * perform splitting and file name expansion.  When arglist is NULL, perform
1491556Srgrimes * here document expansion.
1501556Srgrimes */
1511556Srgrimes
1521556Srgrimesvoid
1531556Srgrimesexpandarg(arg, arglist, flag)
1541556Srgrimes	union node *arg;
1551556Srgrimes	struct arglist *arglist;
15617987Speter	int flag;
15717987Speter{
1581556Srgrimes	struct strlist *sp;
1591556Srgrimes	char *p;
1601556Srgrimes
1611556Srgrimes	argbackq = arg->narg.backquote;
1621556Srgrimes	STARTSTACKSTR(expdest);
1631556Srgrimes	ifsfirst.next = NULL;
1641556Srgrimes	ifslastp = NULL;
1651556Srgrimes	argstr(arg->narg.text, flag);
1661556Srgrimes	if (arglist == NULL) {
1671556Srgrimes		return;			/* here document expanded */
1681556Srgrimes	}
1691556Srgrimes	STPUTC('\0', expdest);
1701556Srgrimes	p = grabstackstr(expdest);
1711556Srgrimes	exparg.lastp = &exparg.list;
1721556Srgrimes	/*
1731556Srgrimes	 * TODO - EXP_REDIR
1741556Srgrimes	 */
1751556Srgrimes	if (flag & EXP_FULL) {
1761556Srgrimes		ifsbreakup(p, &exparg);
1771556Srgrimes		*exparg.lastp = NULL;
1781556Srgrimes		exparg.lastp = &exparg.list;
1791556Srgrimes		expandmeta(exparg.list, flag);
1801556Srgrimes	} else {
1811556Srgrimes		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
1821556Srgrimes			rmescapes(p);
1831556Srgrimes		sp = (struct strlist *)stalloc(sizeof (struct strlist));
1841556Srgrimes		sp->text = p;
1851556Srgrimes		*exparg.lastp = sp;
1861556Srgrimes		exparg.lastp = &sp->next;
1871556Srgrimes	}
1881556Srgrimes	while (ifsfirst.next != NULL) {
1891556Srgrimes		struct ifsregion *ifsp;
1901556Srgrimes		INTOFF;
1911556Srgrimes		ifsp = ifsfirst.next->next;
1921556Srgrimes		ckfree(ifsfirst.next);
1931556Srgrimes		ifsfirst.next = ifsp;
1941556Srgrimes		INTON;
1951556Srgrimes	}
1961556Srgrimes	*exparg.lastp = NULL;
1971556Srgrimes	if (exparg.list) {
1981556Srgrimes		*arglist->lastp = exparg.list;
1991556Srgrimes		arglist->lastp = exparg.lastp;
2001556Srgrimes	}
2011556Srgrimes}
2021556Srgrimes
2031556Srgrimes
2041556Srgrimes
2051556Srgrimes/*
2061556Srgrimes * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
2071556Srgrimes * characters to allow for further processing.  Otherwise treat
2081556Srgrimes * $@ like $* since no splitting will be performed.
2091556Srgrimes */
2101556Srgrimes
2111556SrgrimesSTATIC void
2121556Srgrimesargstr(p, flag)
21325233Ssteve	char *p;
21417987Speter	int flag;
21517987Speter{
21625233Ssteve	char c;
2171556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);	/* do CTLESC */
2181556Srgrimes	int firsteq = 1;
2191556Srgrimes
2201556Srgrimes	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
2211556Srgrimes		p = exptilde(p, flag);
2221556Srgrimes	for (;;) {
2231556Srgrimes		switch (c = *p++) {
2241556Srgrimes		case '\0':
2251556Srgrimes		case CTLENDVAR: /* ??? */
2261556Srgrimes			goto breakloop;
2271556Srgrimes		case CTLESC:
2281556Srgrimes			if (quotes)
2291556Srgrimes				STPUTC(c, expdest);
2301556Srgrimes			c = *p++;
2311556Srgrimes			STPUTC(c, expdest);
2321556Srgrimes			break;
2331556Srgrimes		case CTLVAR:
2341556Srgrimes			p = evalvar(p, flag);
2351556Srgrimes			break;
2361556Srgrimes		case CTLBACKQ:
2371556Srgrimes		case CTLBACKQ|CTLQUOTE:
2381556Srgrimes			expbackq(argbackq->n, c & CTLQUOTE, flag);
2391556Srgrimes			argbackq = argbackq->next;
2401556Srgrimes			break;
2411556Srgrimes		case CTLENDARI:
2421556Srgrimes			expari(flag);
2431556Srgrimes			break;
2441556Srgrimes		case ':':
2451556Srgrimes		case '=':
2461556Srgrimes			/*
2471556Srgrimes			 * sort of a hack - expand tildes in variable
2481556Srgrimes			 * assignments (after the first '=' and after ':'s).
2491556Srgrimes			 */
2501556Srgrimes			STPUTC(c, expdest);
2511556Srgrimes			if (flag & EXP_VARTILDE && *p == '~') {
2521556Srgrimes				if (c == '=') {
2531556Srgrimes					if (firsteq)
2541556Srgrimes						firsteq = 0;
2551556Srgrimes					else
2561556Srgrimes						break;
2571556Srgrimes				}
2581556Srgrimes				p = exptilde(p, flag);
2591556Srgrimes			}
2601556Srgrimes			break;
2611556Srgrimes		default:
2621556Srgrimes			STPUTC(c, expdest);
2631556Srgrimes		}
2641556Srgrimes	}
2651556Srgrimesbreakloop:;
2661556Srgrimes}
2671556Srgrimes
2681556SrgrimesSTATIC char *
2691556Srgrimesexptilde(p, flag)
2701556Srgrimes	char *p;
27117987Speter	int flag;
27217987Speter{
2731556Srgrimes	char c, *startp = p;
2741556Srgrimes	struct passwd *pw;
2751556Srgrimes	char *home;
2761556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
2771556Srgrimes
27817987Speter	while ((c = *p) != '\0') {
2791556Srgrimes		switch(c) {
2801556Srgrimes		case CTLESC:
2811556Srgrimes			return (startp);
2821556Srgrimes		case ':':
2831556Srgrimes			if (flag & EXP_VARTILDE)
2841556Srgrimes				goto done;
2851556Srgrimes			break;
2861556Srgrimes		case '/':
2871556Srgrimes			goto done;
2881556Srgrimes		}
2891556Srgrimes		p++;
2901556Srgrimes	}
2911556Srgrimesdone:
2921556Srgrimes	*p = '\0';
2931556Srgrimes	if (*(startp+1) == '\0') {
2941556Srgrimes		if ((home = lookupvar("HOME")) == NULL)
2951556Srgrimes			goto lose;
2961556Srgrimes	} else {
2971556Srgrimes		if ((pw = getpwnam(startp+1)) == NULL)
2981556Srgrimes			goto lose;
2991556Srgrimes		home = pw->pw_dir;
3001556Srgrimes	}
3011556Srgrimes	if (*home == '\0')
3021556Srgrimes		goto lose;
3031556Srgrimes	*p = c;
30417987Speter	while ((c = *home++) != '\0') {
3051556Srgrimes		if (quotes && SQSYNTAX[c] == CCTL)
3061556Srgrimes			STPUTC(CTLESC, expdest);
3071556Srgrimes		STPUTC(c, expdest);
3081556Srgrimes	}
3091556Srgrimes	return (p);
3101556Srgrimeslose:
3111556Srgrimes	*p = c;
3121556Srgrimes	return (startp);
3131556Srgrimes}
3141556Srgrimes
3151556Srgrimes
3161556Srgrimes/*
3171556Srgrimes * Expand arithmetic expression.  Backup to start of expression,
3181556Srgrimes * evaluate, place result in (backed up) result, adjust string position.
3191556Srgrimes */
3201556Srgrimesvoid
3211556Srgrimesexpari(flag)
32217987Speter	int flag;
32317987Speter{
3241556Srgrimes	char *p, *start;
3251556Srgrimes	int result;
3261556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
3271556Srgrimes
32825233Ssteve	while (ifsfirst.next != NULL) {
32925233Ssteve		struct ifsregion *ifsp;
33025233Ssteve		INTOFF;
33125233Ssteve		ifsp = ifsfirst.next->next;
33225233Ssteve		ckfree(ifsfirst.next);
33325233Ssteve		ifsfirst.next = ifsp;
33425233Ssteve		INTON;
33525233Ssteve	}
33625233Ssteve	ifslastp = NULL;
33725233Ssteve
3381556Srgrimes	/*
3391556Srgrimes	 * This routine is slightly over-compilcated for
3401556Srgrimes	 * efficiency.  First we make sure there is
3411556Srgrimes	 * enough space for the result, which may be bigger
3421556Srgrimes	 * than the expression if we add exponentation.  Next we
3431556Srgrimes	 * scan backwards looking for the start of arithmetic.  If the
3441556Srgrimes	 * next previous character is a CTLESC character, then we
3451556Srgrimes	 * have to rescan starting from the beginning since CTLESC
3468855Srgrimes	 * characters have to be processed left to right.
3471556Srgrimes	 */
34822777Ssteve#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10
34922777Ssteve#error "integers with more than 10 digits are not supported"
35022777Ssteve#endif
35122777Ssteve	CHECKSTRSPACE(12 - 2, expdest);
3528855Srgrimes	USTPUTC('\0', expdest);
3531556Srgrimes	start = stackblock();
3541556Srgrimes	p = expdest;
3551556Srgrimes	while (*p != CTLARI && p >= start)
3561556Srgrimes		--p;
3571556Srgrimes	if (*p != CTLARI)
3581556Srgrimes		error("missing CTLARI (shouldn't happen)");
3591556Srgrimes	if (p > start && *(p-1) == CTLESC)
3601556Srgrimes		for (p = start; *p != CTLARI; p++)
3611556Srgrimes			if (*p == CTLESC)
3621556Srgrimes				p++;
3631556Srgrimes	if (quotes)
3641556Srgrimes		rmescapes(p+1);
3651556Srgrimes	result = arith(p+1);
36622777Ssteve	fmtstr(p, 12, "%d", result);
3671556Srgrimes	while (*p++)
3681556Srgrimes		;
3691556Srgrimes	result = expdest - p + 1;
3701556Srgrimes	STADJUST(-result, expdest);
3711556Srgrimes}
3721556Srgrimes
3731556Srgrimes
3741556Srgrimes/*
3751556Srgrimes * Expand stuff in backwards quotes.
3761556Srgrimes */
3771556Srgrimes
3781556SrgrimesSTATIC void
3791556Srgrimesexpbackq(cmd, quoted, flag)
3801556Srgrimes	union node *cmd;
38117987Speter	int quoted;
38217987Speter	int flag;
38317987Speter{
3841556Srgrimes	struct backcmd in;
3851556Srgrimes	int i;
3861556Srgrimes	char buf[128];
3871556Srgrimes	char *p;
3881556Srgrimes	char *dest = expdest;
3891556Srgrimes	struct ifsregion saveifs, *savelastp;
3901556Srgrimes	struct nodelist *saveargbackq;
3911556Srgrimes	char lastc;
3921556Srgrimes	int startloc = dest - stackblock();
3931556Srgrimes	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
3941556Srgrimes	int saveherefd;
3951556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
3961556Srgrimes
3971556Srgrimes	INTOFF;
3981556Srgrimes	saveifs = ifsfirst;
3991556Srgrimes	savelastp = ifslastp;
4001556Srgrimes	saveargbackq = argbackq;
4018855Srgrimes	saveherefd = herefd;
4021556Srgrimes	herefd = -1;
4031556Srgrimes	p = grabstackstr(dest);
4041556Srgrimes	evalbackcmd(cmd, &in);
4051556Srgrimes	ungrabstackstr(p, dest);
4061556Srgrimes	ifsfirst = saveifs;
4071556Srgrimes	ifslastp = savelastp;
4081556Srgrimes	argbackq = saveargbackq;
4091556Srgrimes	herefd = saveherefd;
4101556Srgrimes
4111556Srgrimes	p = in.buf;
4121556Srgrimes	lastc = '\0';
4131556Srgrimes	for (;;) {
4141556Srgrimes		if (--in.nleft < 0) {
4151556Srgrimes			if (in.fd < 0)
4161556Srgrimes				break;
4171556Srgrimes			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
4181556Srgrimes			TRACE(("expbackq: read returns %d\n", i));
4191556Srgrimes			if (i <= 0)
4201556Srgrimes				break;
4211556Srgrimes			p = buf;
4221556Srgrimes			in.nleft = i - 1;
4231556Srgrimes		}
4241556Srgrimes		lastc = *p++;
4251556Srgrimes		if (lastc != '\0') {
4261556Srgrimes			if (quotes && syntax[lastc] == CCTL)
4271556Srgrimes				STPUTC(CTLESC, dest);
4281556Srgrimes			STPUTC(lastc, dest);
4291556Srgrimes		}
4301556Srgrimes	}
43117987Speter
43217987Speter	/* Eat all trailing newlines */
43317987Speter	for (p--; lastc == '\n'; lastc = *--p)
4341556Srgrimes		STUNPUTC(dest);
43517987Speter
4361556Srgrimes	if (in.fd >= 0)
4371556Srgrimes		close(in.fd);
4381556Srgrimes	if (in.buf)
4391556Srgrimes		ckfree(in.buf);
4401556Srgrimes	if (in.jp)
4411556Srgrimes		exitstatus = waitforjob(in.jp);
4421556Srgrimes	if (quoted == 0)
4431556Srgrimes		recordregion(startloc, dest - stackblock(), 0);
4441556Srgrimes	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
4451556Srgrimes		(dest - stackblock()) - startloc,
4461556Srgrimes		(dest - stackblock()) - startloc,
4471556Srgrimes		stackblock() + startloc));
4481556Srgrimes	expdest = dest;
4491556Srgrimes	INTON;
4501556Srgrimes}
4511556Srgrimes
4521556Srgrimes
4531556Srgrimes
45417987SpeterSTATIC int
45520425Sstevesubevalvar(p, str, strloc, subtype, startloc, varflags)
45617987Speter	char *p;
45717987Speter	char *str;
45820425Ssteve	int strloc;
45917987Speter	int subtype;
46017987Speter	int startloc;
46117987Speter	int varflags;
46217987Speter{
46317987Speter	char *startp;
46417987Speter	char *loc = NULL;
46517987Speter	int c = 0;
46617987Speter	int saveherefd = herefd;
46717987Speter	struct nodelist *saveargbackq = argbackq;
46820425Ssteve	int amount;
46920425Ssteve
47017987Speter	herefd = -1;
47117987Speter	argstr(p, 0);
47217987Speter	STACKSTRNUL(expdest);
47317987Speter	herefd = saveherefd;
47417987Speter	argbackq = saveargbackq;
47517987Speter	startp = stackblock() + startloc;
47620425Ssteve	if (str == NULL)
47720425Ssteve	    str = stackblock() + strloc;
47817987Speter
47917987Speter	switch (subtype) {
48017987Speter	case VSASSIGN:
48117987Speter		setvar(str, startp, 0);
48220425Ssteve		amount = startp - expdest;
48320425Ssteve		STADJUST(amount, expdest);
48417987Speter		varflags &= ~VSNUL;
48517987Speter		if (c != 0)
48617987Speter			*loc = c;
48717987Speter		return 1;
48817987Speter
48917987Speter	case VSQUESTION:
49017987Speter		if (*p != CTLENDVAR) {
49117987Speter			outfmt(&errout, "%s\n", startp);
49217987Speter			error((char *)NULL);
49317987Speter		}
49417987Speter		error("%.*s: parameter %snot set", p - str - 1,
49517987Speter		      str, (varflags & VSNUL) ? "null or "
49617987Speter					      : nullstr);
49717987Speter		return 0;
49817987Speter
49917987Speter	case VSTRIMLEFT:
50025233Ssteve		for (loc = startp; loc < str; loc++) {
50117987Speter			c = *loc;
50217987Speter			*loc = '\0';
50317987Speter			if (patmatch(str, startp)) {
50417987Speter				*loc = c;
50517987Speter				goto recordleft;
50617987Speter			}
50717987Speter			*loc = c;
50817987Speter		}
50917987Speter		return 0;
51017987Speter
51117987Speter	case VSTRIMLEFTMAX:
51217987Speter		for (loc = str - 1; loc >= startp; loc--) {
51317987Speter			c = *loc;
51417987Speter			*loc = '\0';
51517987Speter			if (patmatch(str, startp)) {
51617987Speter				*loc = c;
51717987Speter				goto recordleft;
51817987Speter			}
51917987Speter			*loc = c;
52017987Speter		}
52117987Speter		return 0;
52217987Speter
52317987Speter	case VSTRIMRIGHT:
52417987Speter		for (loc = str - 1; loc >= startp; loc--) {
52517987Speter			if (patmatch(str, loc)) {
52620425Ssteve				amount = loc - expdest;
52720425Ssteve				STADJUST(amount, expdest);
52817987Speter				return 1;
52917987Speter			}
53017987Speter		}
53117987Speter		return 0;
53217987Speter
53317987Speter	case VSTRIMRIGHTMAX:
53417987Speter		for (loc = startp; loc < str - 1; loc++) {
53517987Speter			if (patmatch(str, loc)) {
53620425Ssteve				amount = loc - expdest;
53720425Ssteve				STADJUST(amount, expdest);
53817987Speter				return 1;
53917987Speter			}
54017987Speter		}
54117987Speter		return 0;
54217987Speter
54317987Speter
54417987Speter	default:
54517987Speter		abort();
54617987Speter	}
54717987Speter
54817987Speterrecordleft:
54920425Ssteve	amount = ((str - 1) - (loc - startp)) - expdest;
55020425Ssteve	STADJUST(amount, expdest);
55117987Speter	while (loc != str - 1)
55217987Speter		*startp++ = *loc++;
55317987Speter	return 1;
55417987Speter}
55517987Speter
55617987Speter
5571556Srgrimes/*
5581556Srgrimes * Expand a variable, and return a pointer to the next character in the
5591556Srgrimes * input string.
5601556Srgrimes */
5611556Srgrimes
5621556SrgrimesSTATIC char *
5631556Srgrimesevalvar(p, flag)
5641556Srgrimes	char *p;
56517987Speter	int flag;
56617987Speter{
5671556Srgrimes	int subtype;
5681556Srgrimes	int varflags;
5691556Srgrimes	char *var;
5701556Srgrimes	char *val;
57117987Speter	char *pat;
5721556Srgrimes	int c;
5731556Srgrimes	int set;
5741556Srgrimes	int special;
5751556Srgrimes	int startloc;
57617987Speter	int varlen;
57717987Speter	int easy;
5781556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
5791556Srgrimes
5801556Srgrimes	varflags = *p++;
5811556Srgrimes	subtype = varflags & VSTYPE;
5821556Srgrimes	var = p;
5831556Srgrimes	special = 0;
5841556Srgrimes	if (! is_name(*p))
5851556Srgrimes		special = 1;
5861556Srgrimes	p = strchr(p, '=') + 1;
5871556Srgrimesagain: /* jump here after setting a variable with ${var=text} */
5881556Srgrimes	if (special) {
58925233Ssteve		set = varisset(var, varflags & VSNUL);
5901556Srgrimes		val = NULL;
5911556Srgrimes	} else {
5921556Srgrimes		val = lookupvar(var);
59317987Speter		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
5941556Srgrimes			val = NULL;
5951556Srgrimes			set = 0;
5961556Srgrimes		} else
5971556Srgrimes			set = 1;
5981556Srgrimes	}
59917987Speter	varlen = 0;
6001556Srgrimes	startloc = expdest - stackblock();
6011556Srgrimes	if (set && subtype != VSPLUS) {
6021556Srgrimes		/* insert the value of the variable */
6031556Srgrimes		if (special) {
60418202Speter			varvalue(var, varflags & VSQUOTE, flag & EXP_FULL);
60517987Speter			if (subtype == VSLENGTH) {
60625233Ssteve				varlen = expdest - stackblock() - startloc;
60725233Ssteve				STADJUST(-varlen, expdest);
60817987Speter			}
6091556Srgrimes		} else {
61020425Ssteve			char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
61117987Speter								  : BASESYNTAX;
6121556Srgrimes
61317987Speter			if (subtype == VSLENGTH) {
61417987Speter				for (;*val; val++)
61517987Speter					varlen++;
6161556Srgrimes			}
61717987Speter			else {
61817987Speter				while (*val) {
61917987Speter					if (quotes && syntax[*val] == CCTL)
62017987Speter						STPUTC(CTLESC, expdest);
62117987Speter					STPUTC(*val++, expdest);
62217987Speter				}
62317987Speter
62417987Speter			}
6251556Srgrimes		}
6261556Srgrimes	}
62720425Ssteve
6281556Srgrimes	if (subtype == VSPLUS)
6291556Srgrimes		set = ! set;
63017987Speter
63120425Ssteve	easy = ((varflags & VSQUOTE) == 0 ||
63217987Speter		(*var == '@' && shellparam.nparam != 1));
63317987Speter
63417987Speter
63517987Speter	switch (subtype) {
63617987Speter	case VSLENGTH:
63717987Speter		expdest = cvtnum(varlen, expdest);
63817987Speter		goto record;
63917987Speter
64017987Speter	case VSNORMAL:
64117987Speter		if (!easy)
64217987Speter			break;
64317987Speterrecord:
64420425Ssteve		recordregion(startloc, expdest - stackblock(),
64517987Speter			     varflags & VSQUOTE);
64617987Speter		break;
64717987Speter
64817987Speter	case VSPLUS:
64917987Speter	case VSMINUS:
65017987Speter		if (!set) {
6511556Srgrimes			argstr(p, flag);
65217987Speter			break;
65317987Speter		}
65417987Speter		if (easy)
65517987Speter			goto record;
65617987Speter		break;
65717987Speter
65817987Speter	case VSTRIMLEFT:
65917987Speter	case VSTRIMLEFTMAX:
66017987Speter	case VSTRIMRIGHT:
66117987Speter	case VSTRIMRIGHTMAX:
66217987Speter		if (!set)
66317987Speter			break;
66417987Speter		/*
66517987Speter		 * Terminate the string and start recording the pattern
66617987Speter		 * right after it
66717987Speter		 */
66817987Speter		STPUTC('\0', expdest);
66917987Speter		pat = expdest;
67020425Ssteve		if (subevalvar(p, NULL, expdest - stackblock(), subtype,
67120425Ssteve			       startloc, varflags))
67217987Speter			goto record;
67325233Ssteve		else {
67425233Ssteve			int amount = (expdest - pat) + 1;
67525233Ssteve			STADJUST(-amount, expdest);
67625233Ssteve		}
67717987Speter		break;
67817987Speter
67917987Speter	case VSASSIGN:
68017987Speter	case VSQUESTION:
68117987Speter		if (!set) {
68220425Ssteve			if (subevalvar(p, var, 0, subtype, startloc, varflags)) {
68320425Ssteve				varflags &= ~VSNUL;
6841556Srgrimes				goto again;
68520425Ssteve			}
68617987Speter			break;
6871556Srgrimes		}
68817987Speter		if (easy)
68917987Speter			goto record;
69017987Speter		break;
69117987Speter
69217987Speter	default:
69317987Speter		abort();
6941556Srgrimes	}
69517987Speter
6961556Srgrimes	if (subtype != VSNORMAL) {	/* skip to end of alternative */
6971556Srgrimes		int nesting = 1;
6981556Srgrimes		for (;;) {
6991556Srgrimes			if ((c = *p++) == CTLESC)
7001556Srgrimes				p++;
7011556Srgrimes			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
7021556Srgrimes				if (set)
7031556Srgrimes					argbackq = argbackq->next;
7041556Srgrimes			} else if (c == CTLVAR) {
7051556Srgrimes				if ((*p++ & VSTYPE) != VSNORMAL)
7061556Srgrimes					nesting++;
7071556Srgrimes			} else if (c == CTLENDVAR) {
7081556Srgrimes				if (--nesting == 0)
7091556Srgrimes					break;
7101556Srgrimes			}
7111556Srgrimes		}
7121556Srgrimes	}
7131556Srgrimes	return p;
7141556Srgrimes}
7151556Srgrimes
7161556Srgrimes
7171556Srgrimes
7181556Srgrimes/*
7191556Srgrimes * Test whether a specialized variable is set.
7201556Srgrimes */
7211556Srgrimes
7221556SrgrimesSTATIC int
72325233Sstevevarisset(name, nulok)
72418202Speter	char *name;
72525233Ssteve	int nulok;
72625233Ssteve{
7271556Srgrimes
72825233Ssteve	if (*name == '!')
72925233Ssteve		return backgndpid != -1;
73025233Ssteve	else if (*name == '@' || *name == '*') {
7311556Srgrimes		if (*shellparam.p == NULL)
7321556Srgrimes			return 0;
73325233Ssteve
73425233Ssteve		if (nulok) {
73525233Ssteve			char **av;
73625233Ssteve
73725233Ssteve			for (av = shellparam.p; *av; av++)
73825233Ssteve				if (**av != '\0')
73925233Ssteve					return 1;
74025233Ssteve			return 0;
74125233Ssteve		}
74218202Speter	} else if (is_digit(*name)) {
74325233Ssteve		char *ap;
74420425Ssteve		int num = atoi(name);
74525233Ssteve
74625233Ssteve		if (num > shellparam.nparam)
74725233Ssteve			return 0;
74825233Ssteve
74925233Ssteve		if (num == 0)
75025233Ssteve			ap = arg0;
75125233Ssteve		else
75225233Ssteve			ap = shellparam.p[num - 1];
75325233Ssteve
75425233Ssteve		if (nulok && (ap == NULL || *ap == '\0'))
75525233Ssteve			return 0;
7561556Srgrimes	}
7571556Srgrimes	return 1;
7581556Srgrimes}
7591556Srgrimes
7601556Srgrimes
7611556Srgrimes
7621556Srgrimes/*
7631556Srgrimes * Add the value of a specialized variable to the stack string.
7641556Srgrimes */
7651556Srgrimes
7661556SrgrimesSTATIC void
7671556Srgrimesvarvalue(name, quoted, allow_split)
76818202Speter	char *name;
76917987Speter	int quoted;
77017987Speter	int allow_split;
77117987Speter{
7721556Srgrimes	int num;
7731556Srgrimes	char *p;
7741556Srgrimes	int i;
77517987Speter	extern int oexitstatus;
7761556Srgrimes	char sep;
7771556Srgrimes	char **ap;
7781556Srgrimes	char const *syntax;
7791556Srgrimes
7801556Srgrimes#define STRTODEST(p) \
7811556Srgrimes	do {\
7821556Srgrimes	if (allow_split) { \
7831556Srgrimes		syntax = quoted? DQSYNTAX : BASESYNTAX; \
7841556Srgrimes		while (*p) { \
7851556Srgrimes			if (syntax[*p] == CCTL) \
7861556Srgrimes				STPUTC(CTLESC, expdest); \
7871556Srgrimes			STPUTC(*p++, expdest); \
7881556Srgrimes		} \
7891556Srgrimes	} else \
7901556Srgrimes		while (*p) \
7911556Srgrimes			STPUTC(*p++, expdest); \
7921556Srgrimes	} while (0)
7931556Srgrimes
7941556Srgrimes
79518202Speter	switch (*name) {
7961556Srgrimes	case '$':
7971556Srgrimes		num = rootpid;
7981556Srgrimes		goto numvar;
7991556Srgrimes	case '?':
80017987Speter		num = oexitstatus;
8011556Srgrimes		goto numvar;
8021556Srgrimes	case '#':
8031556Srgrimes		num = shellparam.nparam;
8041556Srgrimes		goto numvar;
8051556Srgrimes	case '!':
8061556Srgrimes		num = backgndpid;
8071556Srgrimesnumvar:
80817987Speter		expdest = cvtnum(num, expdest);
8091556Srgrimes		break;
8101556Srgrimes	case '-':
8111556Srgrimes		for (i = 0 ; i < NOPTS ; i++) {
8121556Srgrimes			if (optlist[i].val)
8131556Srgrimes				STPUTC(optlist[i].letter, expdest);
8141556Srgrimes		}
8151556Srgrimes		break;
8161556Srgrimes	case '@':
8171556Srgrimes		if (allow_split) {
8181556Srgrimes			sep = '\0';
8191556Srgrimes			goto allargs;
8201556Srgrimes		}
8218855Srgrimes		/* fall through */
8221556Srgrimes	case '*':
8231556Srgrimes		sep = ' ';
8241556Srgrimesallargs:
8251556Srgrimes		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
8261556Srgrimes			STRTODEST(p);
8271556Srgrimes			if (*ap)
8281556Srgrimes				STPUTC(sep, expdest);
8291556Srgrimes		}
8301556Srgrimes		break;
8311556Srgrimes	case '0':
8321556Srgrimes		p = arg0;
8331556Srgrimes		STRTODEST(p);
8341556Srgrimes		break;
8351556Srgrimes	default:
83618202Speter		if (is_digit(*name)) {
83718202Speter			num = atoi(name);
83818202Speter			if (num > 0 && num <= shellparam.nparam) {
83918202Speter				p = shellparam.p[num - 1];
84018202Speter				STRTODEST(p);
84118202Speter			}
8421556Srgrimes		}
8431556Srgrimes		break;
8441556Srgrimes	}
8451556Srgrimes}
8461556Srgrimes
8471556Srgrimes
8481556Srgrimes
8491556Srgrimes/*
8501556Srgrimes * Record the the fact that we have to scan this region of the
8511556Srgrimes * string for IFS characters.
8521556Srgrimes */
8531556Srgrimes
8541556SrgrimesSTATIC void
85520425Ssteverecordregion(start, end, nulonly)
85617987Speter	int start;
85717987Speter	int end;
85817987Speter	int nulonly;
85917987Speter{
86025233Ssteve	struct ifsregion *ifsp;
8611556Srgrimes
8621556Srgrimes	if (ifslastp == NULL) {
8631556Srgrimes		ifsp = &ifsfirst;
8641556Srgrimes	} else {
8651556Srgrimes		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
8661556Srgrimes		ifslastp->next = ifsp;
8671556Srgrimes	}
8681556Srgrimes	ifslastp = ifsp;
8691556Srgrimes	ifslastp->next = NULL;
8701556Srgrimes	ifslastp->begoff = start;
8711556Srgrimes	ifslastp->endoff = end;
8721556Srgrimes	ifslastp->nulonly = nulonly;
8731556Srgrimes}
8741556Srgrimes
8751556Srgrimes
8761556Srgrimes
8771556Srgrimes/*
8781556Srgrimes * Break the argument string into pieces based upon IFS and add the
8791556Srgrimes * strings to the argument list.  The regions of the string to be
8801556Srgrimes * searched for IFS characters have been stored by recordregion.
8811556Srgrimes */
8821556SrgrimesSTATIC void
8831556Srgrimesifsbreakup(string, arglist)
8841556Srgrimes	char *string;
8851556Srgrimes	struct arglist *arglist;
8861556Srgrimes	{
8871556Srgrimes	struct ifsregion *ifsp;
8881556Srgrimes	struct strlist *sp;
8891556Srgrimes	char *start;
89025233Ssteve	char *p;
8911556Srgrimes	char *q;
8921556Srgrimes	char *ifs;
89317987Speter	int ifsspc;
8941556Srgrimes
89517987Speter
8961556Srgrimes	start = string;
8971556Srgrimes	if (ifslastp != NULL) {
8981556Srgrimes		ifsp = &ifsfirst;
8991556Srgrimes		do {
9001556Srgrimes			p = string + ifsp->begoff;
9011556Srgrimes			ifs = ifsp->nulonly? nullstr : ifsval();
90217987Speter			ifsspc = strchr(ifs, ' ') != NULL;
9031556Srgrimes			while (p < string + ifsp->endoff) {
9041556Srgrimes				q = p;
9051556Srgrimes				if (*p == CTLESC)
9061556Srgrimes					p++;
9071556Srgrimes				if (strchr(ifs, *p++)) {
90817987Speter					if (q > start || !ifsspc) {
9091556Srgrimes						*q = '\0';
9101556Srgrimes						sp = (struct strlist *)stalloc(sizeof *sp);
9111556Srgrimes						sp->text = start;
9121556Srgrimes						*arglist->lastp = sp;
9131556Srgrimes						arglist->lastp = &sp->next;
9141556Srgrimes					}
91517987Speter					if (ifsspc) {
9161556Srgrimes						for (;;) {
9171556Srgrimes							if (p >= string + ifsp->endoff)
9181556Srgrimes								break;
9191556Srgrimes							q = p;
9201556Srgrimes							if (*p == CTLESC)
9211556Srgrimes								p++;
9221556Srgrimes							if (strchr(ifs, *p++) == NULL) {
9231556Srgrimes								p = q;
9241556Srgrimes								break;
9251556Srgrimes							}
9261556Srgrimes						}
9271556Srgrimes					}
9281556Srgrimes					start = p;
9291556Srgrimes				}
9301556Srgrimes			}
9311556Srgrimes		} while ((ifsp = ifsp->next) != NULL);
93217987Speter		if (*start || (!ifsspc && start > string)) {
9331556Srgrimes			sp = (struct strlist *)stalloc(sizeof *sp);
9341556Srgrimes			sp->text = start;
9351556Srgrimes			*arglist->lastp = sp;
9361556Srgrimes			arglist->lastp = &sp->next;
9371556Srgrimes		}
9381556Srgrimes	} else {
9391556Srgrimes		sp = (struct strlist *)stalloc(sizeof *sp);
9401556Srgrimes		sp->text = start;
9411556Srgrimes		*arglist->lastp = sp;
9421556Srgrimes		arglist->lastp = &sp->next;
9431556Srgrimes	}
9441556Srgrimes}
9451556Srgrimes
9461556Srgrimes
9471556Srgrimes
9481556Srgrimes/*
9491556Srgrimes * Expand shell metacharacters.  At this point, the only control characters
9501556Srgrimes * should be escapes.  The results are stored in the list exparg.
9511556Srgrimes */
9521556Srgrimes
9531556Srgrimeschar *expdir;
9541556Srgrimes
9551556Srgrimes
9561556SrgrimesSTATIC void
9571556Srgrimesexpandmeta(str, flag)
9581556Srgrimes	struct strlist *str;
95925905Ssteve	int flag __unused;
96017987Speter{
9611556Srgrimes	char *p;
9621556Srgrimes	struct strlist **savelastp;
9631556Srgrimes	struct strlist *sp;
9641556Srgrimes	char c;
9651556Srgrimes	/* TODO - EXP_REDIR */
9661556Srgrimes
9671556Srgrimes	while (str) {
9681556Srgrimes		if (fflag)
9691556Srgrimes			goto nometa;
9701556Srgrimes		p = str->text;
9711556Srgrimes		for (;;) {			/* fast check for meta chars */
9721556Srgrimes			if ((c = *p++) == '\0')
9731556Srgrimes				goto nometa;
9741556Srgrimes			if (c == '*' || c == '?' || c == '[' || c == '!')
9751556Srgrimes				break;
9761556Srgrimes		}
9771556Srgrimes		savelastp = exparg.lastp;
9781556Srgrimes		INTOFF;
9791556Srgrimes		if (expdir == NULL) {
9801556Srgrimes			int i = strlen(str->text);
9811556Srgrimes			expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
9821556Srgrimes		}
9831556Srgrimes
9841556Srgrimes		expmeta(expdir, str->text);
9851556Srgrimes		ckfree(expdir);
9861556Srgrimes		expdir = NULL;
9871556Srgrimes		INTON;
9881556Srgrimes		if (exparg.lastp == savelastp) {
9898855Srgrimes			/*
9908855Srgrimes			 * no matches
9911556Srgrimes			 */
9921556Srgrimesnometa:
9931556Srgrimes			*exparg.lastp = str;
9941556Srgrimes			rmescapes(str->text);
9951556Srgrimes			exparg.lastp = &str->next;
9961556Srgrimes		} else {
9971556Srgrimes			*exparg.lastp = NULL;
9981556Srgrimes			*savelastp = sp = expsort(*savelastp);
9991556Srgrimes			while (sp->next != NULL)
10001556Srgrimes				sp = sp->next;
10011556Srgrimes			exparg.lastp = &sp->next;
10021556Srgrimes		}
10031556Srgrimes		str = str->next;
10041556Srgrimes	}
10051556Srgrimes}
10061556Srgrimes
10071556Srgrimes
10081556Srgrimes/*
10091556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion.
10101556Srgrimes */
10111556Srgrimes
10121556SrgrimesSTATIC void
10131556Srgrimesexpmeta(enddir, name)
10141556Srgrimes	char *enddir;
10151556Srgrimes	char *name;
10161556Srgrimes	{
101725233Ssteve	char *p;
10181556Srgrimes	char *q;
10191556Srgrimes	char *start;
10201556Srgrimes	char *endname;
10211556Srgrimes	int metaflag;
10221556Srgrimes	struct stat statb;
10231556Srgrimes	DIR *dirp;
10241556Srgrimes	struct dirent *dp;
10251556Srgrimes	int atend;
10261556Srgrimes	int matchdot;
10271556Srgrimes
10281556Srgrimes	metaflag = 0;
10291556Srgrimes	start = name;
10301556Srgrimes	for (p = name ; ; p++) {
10311556Srgrimes		if (*p == '*' || *p == '?')
10321556Srgrimes			metaflag = 1;
10331556Srgrimes		else if (*p == '[') {
10341556Srgrimes			q = p + 1;
103526488Sache			if (*q == '!' || *q == '^')
10361556Srgrimes				q++;
10371556Srgrimes			for (;;) {
10381556Srgrimes				if (*q == CTLESC)
10391556Srgrimes					q++;
10401556Srgrimes				if (*q == '/' || *q == '\0')
10411556Srgrimes					break;
10421556Srgrimes				if (*++q == ']') {
10431556Srgrimes					metaflag = 1;
10441556Srgrimes					break;
10451556Srgrimes				}
10461556Srgrimes			}
10471556Srgrimes		} else if (*p == '!' && p[1] == '!'	&& (p == name || p[-1] == '/')) {
10481556Srgrimes			metaflag = 1;
10491556Srgrimes		} else if (*p == '\0')
10501556Srgrimes			break;
10511556Srgrimes		else if (*p == CTLESC)
10521556Srgrimes			p++;
10531556Srgrimes		if (*p == '/') {
10541556Srgrimes			if (metaflag)
10551556Srgrimes				break;
10561556Srgrimes			start = p + 1;
10571556Srgrimes		}
10581556Srgrimes	}
10591556Srgrimes	if (metaflag == 0) {	/* we've reached the end of the file name */
10601556Srgrimes		if (enddir != expdir)
10611556Srgrimes			metaflag++;
10621556Srgrimes		for (p = name ; ; p++) {
10631556Srgrimes			if (*p == CTLESC)
10641556Srgrimes				p++;
10651556Srgrimes			*enddir++ = *p;
10661556Srgrimes			if (*p == '\0')
10671556Srgrimes				break;
10681556Srgrimes		}
10691556Srgrimes		if (metaflag == 0 || stat(expdir, &statb) >= 0)
10701556Srgrimes			addfname(expdir);
10711556Srgrimes		return;
10721556Srgrimes	}
10731556Srgrimes	endname = p;
10741556Srgrimes	if (start != name) {
10751556Srgrimes		p = name;
10761556Srgrimes		while (p < start) {
10771556Srgrimes			if (*p == CTLESC)
10781556Srgrimes				p++;
10791556Srgrimes			*enddir++ = *p++;
10801556Srgrimes		}
10811556Srgrimes	}
10821556Srgrimes	if (enddir == expdir) {
10831556Srgrimes		p = ".";
10841556Srgrimes	} else if (enddir == expdir + 1 && *expdir == '/') {
10851556Srgrimes		p = "/";
10861556Srgrimes	} else {
10871556Srgrimes		p = expdir;
10881556Srgrimes		enddir[-1] = '\0';
10891556Srgrimes	}
10901556Srgrimes	if ((dirp = opendir(p)) == NULL)
10911556Srgrimes		return;
10921556Srgrimes	if (enddir != expdir)
10931556Srgrimes		enddir[-1] = '/';
10941556Srgrimes	if (*endname == 0) {
10951556Srgrimes		atend = 1;
10961556Srgrimes	} else {
10971556Srgrimes		atend = 0;
10981556Srgrimes		*endname++ = '\0';
10991556Srgrimes	}
11001556Srgrimes	matchdot = 0;
110117987Speter	if (start[0] == '.' || (start[0] == CTLESC && start[1] == '.'))
11021556Srgrimes		matchdot++;
11031556Srgrimes	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
11041556Srgrimes		if (dp->d_name[0] == '.' && ! matchdot)
11051556Srgrimes			continue;
11061556Srgrimes		if (patmatch(start, dp->d_name)) {
11071556Srgrimes			if (atend) {
11081556Srgrimes				scopy(dp->d_name, enddir);
11091556Srgrimes				addfname(expdir);
11101556Srgrimes			} else {
11111556Srgrimes				char *q;
111217987Speter				for (p = enddir, q = dp->d_name;
111317987Speter				     (*p++ = *q++) != '\0';)
111417987Speter					continue;
11151556Srgrimes				p[-1] = '/';
11161556Srgrimes				expmeta(p, endname);
11171556Srgrimes			}
11181556Srgrimes		}
11191556Srgrimes	}
11201556Srgrimes	closedir(dirp);
11211556Srgrimes	if (! atend)
11221556Srgrimes		endname[-1] = '/';
11231556Srgrimes}
11241556Srgrimes
11251556Srgrimes
11261556Srgrimes/*
11271556Srgrimes * Add a file name to the list.
11281556Srgrimes */
11291556Srgrimes
11301556SrgrimesSTATIC void
11311556Srgrimesaddfname(name)
11321556Srgrimes	char *name;
11331556Srgrimes	{
11341556Srgrimes	char *p;
11351556Srgrimes	struct strlist *sp;
11361556Srgrimes
11371556Srgrimes	p = stalloc(strlen(name) + 1);
11381556Srgrimes	scopy(name, p);
11391556Srgrimes	sp = (struct strlist *)stalloc(sizeof *sp);
11401556Srgrimes	sp->text = p;
11411556Srgrimes	*exparg.lastp = sp;
11421556Srgrimes	exparg.lastp = &sp->next;
11431556Srgrimes}
11441556Srgrimes
11451556Srgrimes
11461556Srgrimes/*
11471556Srgrimes * Sort the results of file name expansion.  It calculates the number of
11481556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the
11491556Srgrimes * work.
11501556Srgrimes */
11511556Srgrimes
11521556SrgrimesSTATIC struct strlist *
11531556Srgrimesexpsort(str)
11541556Srgrimes	struct strlist *str;
11551556Srgrimes	{
11561556Srgrimes	int len;
11571556Srgrimes	struct strlist *sp;
11581556Srgrimes
11591556Srgrimes	len = 0;
11601556Srgrimes	for (sp = str ; sp ; sp = sp->next)
11611556Srgrimes		len++;
11621556Srgrimes	return msort(str, len);
11631556Srgrimes}
11641556Srgrimes
11651556Srgrimes
11661556SrgrimesSTATIC struct strlist *
11671556Srgrimesmsort(list, len)
11681556Srgrimes	struct strlist *list;
116917987Speter	int len;
117017987Speter{
117117987Speter	struct strlist *p, *q = NULL;
11721556Srgrimes	struct strlist **lpp;
11731556Srgrimes	int half;
11741556Srgrimes	int n;
11751556Srgrimes
11761556Srgrimes	if (len <= 1)
11771556Srgrimes		return list;
11788855Srgrimes	half = len >> 1;
11791556Srgrimes	p = list;
11801556Srgrimes	for (n = half ; --n >= 0 ; ) {
11811556Srgrimes		q = p;
11821556Srgrimes		p = p->next;
11831556Srgrimes	}
11841556Srgrimes	q->next = NULL;			/* terminate first half of list */
11851556Srgrimes	q = msort(list, half);		/* sort first half of list */
11861556Srgrimes	p = msort(p, len - half);		/* sort second half */
11871556Srgrimes	lpp = &list;
11881556Srgrimes	for (;;) {
11891556Srgrimes		if (strcmp(p->text, q->text) < 0) {
11901556Srgrimes			*lpp = p;
11911556Srgrimes			lpp = &p->next;
11921556Srgrimes			if ((p = *lpp) == NULL) {
11931556Srgrimes				*lpp = q;
11941556Srgrimes				break;
11951556Srgrimes			}
11961556Srgrimes		} else {
11971556Srgrimes			*lpp = q;
11981556Srgrimes			lpp = &q->next;
11991556Srgrimes			if ((q = *lpp) == NULL) {
12001556Srgrimes				*lpp = p;
12011556Srgrimes				break;
12021556Srgrimes			}
12031556Srgrimes		}
12041556Srgrimes	}
12051556Srgrimes	return list;
12061556Srgrimes}
12071556Srgrimes
12081556Srgrimes
12091556Srgrimes
12101556Srgrimes/*
12111556Srgrimes * Returns true if the pattern matches the string.
12121556Srgrimes */
12131556Srgrimes
12141556Srgrimesint
12151556Srgrimespatmatch(pattern, string)
12161556Srgrimes	char *pattern;
12171556Srgrimes	char *string;
12181556Srgrimes	{
12191556Srgrimes#ifdef notdef
12201556Srgrimes	if (pattern[0] == '!' && pattern[1] == '!')
12211556Srgrimes		return 1 - pmatch(pattern + 2, string);
12221556Srgrimes	else
12231556Srgrimes#endif
12241556Srgrimes		return pmatch(pattern, string);
12251556Srgrimes}
12261556Srgrimes
122717987Speter
122817525SacheSTATIC int
12291556Srgrimespmatch(pattern, string)
12301556Srgrimes	char *pattern;
12311556Srgrimes	char *string;
12321556Srgrimes	{
123325233Ssteve	char *p, *q;
123425233Ssteve	char c;
12351556Srgrimes
12361556Srgrimes	p = pattern;
12371556Srgrimes	q = string;
12381556Srgrimes	for (;;) {
12391556Srgrimes		switch (c = *p++) {
12401556Srgrimes		case '\0':
12411556Srgrimes			goto breakloop;
12421556Srgrimes		case CTLESC:
12431556Srgrimes			if (*q++ != *p++)
12441556Srgrimes				return 0;
12451556Srgrimes			break;
12461556Srgrimes		case '?':
12471556Srgrimes			if (*q++ == '\0')
12481556Srgrimes				return 0;
12491556Srgrimes			break;
12501556Srgrimes		case '*':
12511556Srgrimes			c = *p;
12521556Srgrimes			if (c != CTLESC && c != '?' && c != '*' && c != '[') {
12531556Srgrimes				while (*q != c) {
12541556Srgrimes					if (*q == '\0')
12551556Srgrimes						return 0;
12561556Srgrimes					q++;
12571556Srgrimes				}
12581556Srgrimes			}
12591556Srgrimes			do {
12601556Srgrimes				if (pmatch(p, q))
12611556Srgrimes					return 1;
12621556Srgrimes			} while (*q++ != '\0');
12631556Srgrimes			return 0;
12641556Srgrimes		case '[': {
12651556Srgrimes			char *endp;
12661556Srgrimes			int invert, found;
12671556Srgrimes			char chr;
12681556Srgrimes
12691556Srgrimes			endp = p;
127026488Sache			if (*endp == '!' || *endp == '^')
12711556Srgrimes				endp++;
12721556Srgrimes			for (;;) {
12731556Srgrimes				if (*endp == '\0')
12741556Srgrimes					goto dft;		/* no matching ] */
12751556Srgrimes				if (*endp == CTLESC)
12761556Srgrimes					endp++;
12771556Srgrimes				if (*++endp == ']')
12781556Srgrimes					break;
12791556Srgrimes			}
12801556Srgrimes			invert = 0;
128126488Sache			if (*p == '!' || *p == '^') {
12821556Srgrimes				invert++;
12831556Srgrimes				p++;
12841556Srgrimes			}
12851556Srgrimes			found = 0;
12861556Srgrimes			chr = *q++;
128717987Speter			if (chr == '\0')
128817987Speter				return 0;
12891556Srgrimes			c = *p++;
12901556Srgrimes			do {
12911556Srgrimes				if (c == CTLESC)
12921556Srgrimes					c = *p++;
12931556Srgrimes				if (*p == '-' && p[1] != ']') {
12941556Srgrimes					p++;
12951556Srgrimes					if (*p == CTLESC)
12961556Srgrimes						p++;
129717557Sache					if (   collate_range_cmp(chr, c) >= 0
129817557Sache					    && collate_range_cmp(chr, *p) <= 0
129917525Sache					   )
13001556Srgrimes						found = 1;
13011556Srgrimes					p++;
13021556Srgrimes				} else {
13031556Srgrimes					if (chr == c)
13041556Srgrimes						found = 1;
13051556Srgrimes				}
13061556Srgrimes			} while ((c = *p++) != ']');
13071556Srgrimes			if (found == invert)
13081556Srgrimes				return 0;
13091556Srgrimes			break;
13101556Srgrimes		}
13111556Srgrimesdft:	        default:
13121556Srgrimes			if (*q++ != c)
13131556Srgrimes				return 0;
13141556Srgrimes			break;
13151556Srgrimes		}
13161556Srgrimes	}
13171556Srgrimesbreakloop:
13181556Srgrimes	if (*q != '\0')
13191556Srgrimes		return 0;
13201556Srgrimes	return 1;
13211556Srgrimes}
13221556Srgrimes
13231556Srgrimes
13241556Srgrimes
13251556Srgrimes/*
13261556Srgrimes * Remove any CTLESC characters from a string.
13271556Srgrimes */
13281556Srgrimes
13291556Srgrimesvoid
13301556Srgrimesrmescapes(str)
13311556Srgrimes	char *str;
13321556Srgrimes	{
133325233Ssteve	char *p, *q;
13341556Srgrimes
13351556Srgrimes	p = str;
13361556Srgrimes	while (*p != CTLESC) {
13371556Srgrimes		if (*p++ == '\0')
13381556Srgrimes			return;
13391556Srgrimes	}
13401556Srgrimes	q = p;
13411556Srgrimes	while (*p) {
13421556Srgrimes		if (*p == CTLESC)
13431556Srgrimes			p++;
13441556Srgrimes		*q++ = *p++;
13451556Srgrimes	}
13461556Srgrimes	*q = '\0';
13471556Srgrimes}
13481556Srgrimes
13491556Srgrimes
13501556Srgrimes
13511556Srgrimes/*
13521556Srgrimes * See if a pattern matches in a case statement.
13531556Srgrimes */
13541556Srgrimes
13551556Srgrimesint
13561556Srgrimescasematch(pattern, val)
13571556Srgrimes	union node *pattern;
13581556Srgrimes	char *val;
13591556Srgrimes	{
13601556Srgrimes	struct stackmark smark;
13611556Srgrimes	int result;
13621556Srgrimes	char *p;
13631556Srgrimes
13641556Srgrimes	setstackmark(&smark);
13651556Srgrimes	argbackq = pattern->narg.backquote;
13661556Srgrimes	STARTSTACKSTR(expdest);
13671556Srgrimes	ifslastp = NULL;
13681556Srgrimes	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
13691556Srgrimes	STPUTC('\0', expdest);
13701556Srgrimes	p = grabstackstr(expdest);
13711556Srgrimes	result = patmatch(p, val);
13721556Srgrimes	popstackmark(&smark);
13731556Srgrimes	return result;
13741556Srgrimes}
137517987Speter
137617987Speter/*
137717987Speter * Our own itoa().
137817987Speter */
137917987Speter
138017987SpeterSTATIC char *
138117987Spetercvtnum(num, buf)
138217987Speter	int num;
138317987Speter	char *buf;
138417987Speter	{
138517987Speter	char temp[32];
138617987Speter	int neg = num < 0;
138717987Speter	char *p = temp + 31;
138817987Speter
138917987Speter	temp[31] = '\0';
139017987Speter
139117987Speter	do {
139217987Speter		*--p = num % 10 + '0';
139317987Speter	} while ((num /= 10) != 0);
139417987Speter
139517987Speter	if (neg)
139617987Speter		*--p = '-';
139717987Speter
139817987Speter	while (*p)
139917987Speter		STPUTC(*p++, buf);
140017987Speter	return buf;
140117987Speter}
1402