expand.c revision 18202
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 *
3618202Speter *	$Id$
371556Srgrimes */
381556Srgrimes
391556Srgrimes#ifndef lint
4017987Speterstatic char 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>
5117987Speter#include <locale.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));
9817987SpeterSTATIC int subevalvar __P((char *, char *, int, int, int));
9917987SpeterSTATIC char *evalvar __P((char *, int));
10018202SpeterSTATIC int varisset __P((char *));
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 *));
1111556Srgrimes
1121556Srgrimes/*
1131556Srgrimes * Expand shell variables and backquotes inside a here document.
1141556Srgrimes */
1151556Srgrimes
1161556Srgrimesvoid
1171556Srgrimesexpandhere(arg, fd)
1181556Srgrimes	union node *arg;	/* the document */
1191556Srgrimes	int fd;			/* where to write the expanded version */
1201556Srgrimes	{
1211556Srgrimes	herefd = fd;
1221556Srgrimes	expandarg(arg, (struct arglist *)NULL, 0);
1231556Srgrimes	xwrite(fd, stackblock(), expdest - stackblock());
1241556Srgrimes}
1251556Srgrimes
1261556Srgrimes
1271556Srgrimes/*
1281556Srgrimes * Perform variable substitution and command substitution on an argument,
1291556Srgrimes * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
1301556Srgrimes * perform splitting and file name expansion.  When arglist is NULL, perform
1311556Srgrimes * here document expansion.
1321556Srgrimes */
1331556Srgrimes
1341556Srgrimesvoid
1351556Srgrimesexpandarg(arg, arglist, flag)
1361556Srgrimes	union node *arg;
1371556Srgrimes	struct arglist *arglist;
13817987Speter	int flag;
13917987Speter{
1401556Srgrimes	struct strlist *sp;
1411556Srgrimes	char *p;
1421556Srgrimes
1431556Srgrimes	argbackq = arg->narg.backquote;
1441556Srgrimes	STARTSTACKSTR(expdest);
1451556Srgrimes	ifsfirst.next = NULL;
1461556Srgrimes	ifslastp = NULL;
1471556Srgrimes	argstr(arg->narg.text, flag);
1481556Srgrimes	if (arglist == NULL) {
1491556Srgrimes		return;			/* here document expanded */
1501556Srgrimes	}
1511556Srgrimes	STPUTC('\0', expdest);
1521556Srgrimes	p = grabstackstr(expdest);
1531556Srgrimes	exparg.lastp = &exparg.list;
1541556Srgrimes	/*
1551556Srgrimes	 * TODO - EXP_REDIR
1561556Srgrimes	 */
1571556Srgrimes	if (flag & EXP_FULL) {
1581556Srgrimes		ifsbreakup(p, &exparg);
1591556Srgrimes		*exparg.lastp = NULL;
1601556Srgrimes		exparg.lastp = &exparg.list;
1611556Srgrimes		expandmeta(exparg.list, flag);
1621556Srgrimes	} else {
1631556Srgrimes		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
1641556Srgrimes			rmescapes(p);
1651556Srgrimes		sp = (struct strlist *)stalloc(sizeof (struct strlist));
1661556Srgrimes		sp->text = p;
1671556Srgrimes		*exparg.lastp = sp;
1681556Srgrimes		exparg.lastp = &sp->next;
1691556Srgrimes	}
1701556Srgrimes	while (ifsfirst.next != NULL) {
1711556Srgrimes		struct ifsregion *ifsp;
1721556Srgrimes		INTOFF;
1731556Srgrimes		ifsp = ifsfirst.next->next;
1741556Srgrimes		ckfree(ifsfirst.next);
1751556Srgrimes		ifsfirst.next = ifsp;
1761556Srgrimes		INTON;
1771556Srgrimes	}
1781556Srgrimes	*exparg.lastp = NULL;
1791556Srgrimes	if (exparg.list) {
1801556Srgrimes		*arglist->lastp = exparg.list;
1811556Srgrimes		arglist->lastp = exparg.lastp;
1821556Srgrimes	}
1831556Srgrimes}
1841556Srgrimes
1851556Srgrimes
1861556Srgrimes
1871556Srgrimes/*
1881556Srgrimes * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
1891556Srgrimes * characters to allow for further processing.  Otherwise treat
1901556Srgrimes * $@ like $* since no splitting will be performed.
1911556Srgrimes */
1921556Srgrimes
1931556SrgrimesSTATIC void
1941556Srgrimesargstr(p, flag)
1951556Srgrimes	register char *p;
19617987Speter	int flag;
19717987Speter{
1981556Srgrimes	register char c;
1991556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);	/* do CTLESC */
2001556Srgrimes	int firsteq = 1;
2011556Srgrimes
2021556Srgrimes	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
2031556Srgrimes		p = exptilde(p, flag);
2041556Srgrimes	for (;;) {
2051556Srgrimes		switch (c = *p++) {
2061556Srgrimes		case '\0':
2071556Srgrimes		case CTLENDVAR: /* ??? */
2081556Srgrimes			goto breakloop;
2091556Srgrimes		case CTLESC:
2101556Srgrimes			if (quotes)
2111556Srgrimes				STPUTC(c, expdest);
2121556Srgrimes			c = *p++;
2131556Srgrimes			STPUTC(c, expdest);
2141556Srgrimes			break;
2151556Srgrimes		case CTLVAR:
2161556Srgrimes			p = evalvar(p, flag);
2171556Srgrimes			break;
2181556Srgrimes		case CTLBACKQ:
2191556Srgrimes		case CTLBACKQ|CTLQUOTE:
2201556Srgrimes			expbackq(argbackq->n, c & CTLQUOTE, flag);
2211556Srgrimes			argbackq = argbackq->next;
2221556Srgrimes			break;
2231556Srgrimes		case CTLENDARI:
2241556Srgrimes			expari(flag);
2251556Srgrimes			break;
2261556Srgrimes		case ':':
2271556Srgrimes		case '=':
2281556Srgrimes			/*
2291556Srgrimes			 * sort of a hack - expand tildes in variable
2301556Srgrimes			 * assignments (after the first '=' and after ':'s).
2311556Srgrimes			 */
2321556Srgrimes			STPUTC(c, expdest);
2331556Srgrimes			if (flag & EXP_VARTILDE && *p == '~') {
2341556Srgrimes				if (c == '=') {
2351556Srgrimes					if (firsteq)
2361556Srgrimes						firsteq = 0;
2371556Srgrimes					else
2381556Srgrimes						break;
2391556Srgrimes				}
2401556Srgrimes				p = exptilde(p, flag);
2411556Srgrimes			}
2421556Srgrimes			break;
2431556Srgrimes		default:
2441556Srgrimes			STPUTC(c, expdest);
2451556Srgrimes		}
2461556Srgrimes	}
2471556Srgrimesbreakloop:;
2481556Srgrimes}
2491556Srgrimes
2501556SrgrimesSTATIC char *
2511556Srgrimesexptilde(p, flag)
2521556Srgrimes	char *p;
25317987Speter	int flag;
25417987Speter{
2551556Srgrimes	char c, *startp = p;
2561556Srgrimes	struct passwd *pw;
2571556Srgrimes	char *home;
2581556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
2591556Srgrimes
26017987Speter	while ((c = *p) != '\0') {
2611556Srgrimes		switch(c) {
2621556Srgrimes		case CTLESC:
2631556Srgrimes			return (startp);
2641556Srgrimes		case ':':
2651556Srgrimes			if (flag & EXP_VARTILDE)
2661556Srgrimes				goto done;
2671556Srgrimes			break;
2681556Srgrimes		case '/':
2691556Srgrimes			goto done;
2701556Srgrimes		}
2711556Srgrimes		p++;
2721556Srgrimes	}
2731556Srgrimesdone:
2741556Srgrimes	*p = '\0';
2751556Srgrimes	if (*(startp+1) == '\0') {
2761556Srgrimes		if ((home = lookupvar("HOME")) == NULL)
2771556Srgrimes			goto lose;
2781556Srgrimes	} else {
2791556Srgrimes		if ((pw = getpwnam(startp+1)) == NULL)
2801556Srgrimes			goto lose;
2811556Srgrimes		home = pw->pw_dir;
2821556Srgrimes	}
2831556Srgrimes	if (*home == '\0')
2841556Srgrimes		goto lose;
2851556Srgrimes	*p = c;
28617987Speter	while ((c = *home++) != '\0') {
2871556Srgrimes		if (quotes && SQSYNTAX[c] == CCTL)
2881556Srgrimes			STPUTC(CTLESC, expdest);
2891556Srgrimes		STPUTC(c, expdest);
2901556Srgrimes	}
2911556Srgrimes	return (p);
2921556Srgrimeslose:
2931556Srgrimes	*p = c;
2941556Srgrimes	return (startp);
2951556Srgrimes}
2961556Srgrimes
2971556Srgrimes
2981556Srgrimes/*
2991556Srgrimes * Expand arithmetic expression.  Backup to start of expression,
3001556Srgrimes * evaluate, place result in (backed up) result, adjust string position.
3011556Srgrimes */
3021556Srgrimesvoid
3031556Srgrimesexpari(flag)
30417987Speter	int flag;
30517987Speter{
3061556Srgrimes	char *p, *start;
3071556Srgrimes	int result;
3081556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
3091556Srgrimes
3101556Srgrimes	/*
3111556Srgrimes	 * This routine is slightly over-compilcated for
3121556Srgrimes	 * efficiency.  First we make sure there is
3131556Srgrimes	 * enough space for the result, which may be bigger
3141556Srgrimes	 * than the expression if we add exponentation.  Next we
3151556Srgrimes	 * scan backwards looking for the start of arithmetic.  If the
3161556Srgrimes	 * next previous character is a CTLESC character, then we
3171556Srgrimes	 * have to rescan starting from the beginning since CTLESC
3188855Srgrimes	 * characters have to be processed left to right.
3191556Srgrimes	 */
3201556Srgrimes	CHECKSTRSPACE(8, expdest);
3218855Srgrimes	USTPUTC('\0', expdest);
3221556Srgrimes	start = stackblock();
3231556Srgrimes	p = expdest;
3241556Srgrimes	while (*p != CTLARI && p >= start)
3251556Srgrimes		--p;
3261556Srgrimes	if (*p != CTLARI)
3271556Srgrimes		error("missing CTLARI (shouldn't happen)");
3281556Srgrimes	if (p > start && *(p-1) == CTLESC)
3291556Srgrimes		for (p = start; *p != CTLARI; p++)
3301556Srgrimes			if (*p == CTLESC)
3311556Srgrimes				p++;
3321556Srgrimes	if (quotes)
3331556Srgrimes		rmescapes(p+1);
3341556Srgrimes	result = arith(p+1);
3351556Srgrimes	fmtstr(p, 10, "%d", result);
3361556Srgrimes	while (*p++)
3371556Srgrimes		;
3381556Srgrimes	result = expdest - p + 1;
3391556Srgrimes	STADJUST(-result, expdest);
3401556Srgrimes}
3411556Srgrimes
3421556Srgrimes
3431556Srgrimes/*
3441556Srgrimes * Expand stuff in backwards quotes.
3451556Srgrimes */
3461556Srgrimes
3471556SrgrimesSTATIC void
3481556Srgrimesexpbackq(cmd, quoted, flag)
3491556Srgrimes	union node *cmd;
35017987Speter	int quoted;
35117987Speter	int flag;
35217987Speter{
3531556Srgrimes	struct backcmd in;
3541556Srgrimes	int i;
3551556Srgrimes	char buf[128];
3561556Srgrimes	char *p;
3571556Srgrimes	char *dest = expdest;
3581556Srgrimes	struct ifsregion saveifs, *savelastp;
3591556Srgrimes	struct nodelist *saveargbackq;
3601556Srgrimes	char lastc;
3611556Srgrimes	int startloc = dest - stackblock();
3621556Srgrimes	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
3631556Srgrimes	int saveherefd;
3641556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
3651556Srgrimes
3661556Srgrimes	INTOFF;
3671556Srgrimes	saveifs = ifsfirst;
3681556Srgrimes	savelastp = ifslastp;
3691556Srgrimes	saveargbackq = argbackq;
3708855Srgrimes	saveherefd = herefd;
3711556Srgrimes	herefd = -1;
3721556Srgrimes	p = grabstackstr(dest);
3731556Srgrimes	evalbackcmd(cmd, &in);
3741556Srgrimes	ungrabstackstr(p, dest);
3751556Srgrimes	ifsfirst = saveifs;
3761556Srgrimes	ifslastp = savelastp;
3771556Srgrimes	argbackq = saveargbackq;
3781556Srgrimes	herefd = saveherefd;
3791556Srgrimes
3801556Srgrimes	p = in.buf;
3811556Srgrimes	lastc = '\0';
3821556Srgrimes	for (;;) {
3831556Srgrimes		if (--in.nleft < 0) {
3841556Srgrimes			if (in.fd < 0)
3851556Srgrimes				break;
3861556Srgrimes			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
3871556Srgrimes			TRACE(("expbackq: read returns %d\n", i));
3881556Srgrimes			if (i <= 0)
3891556Srgrimes				break;
3901556Srgrimes			p = buf;
3911556Srgrimes			in.nleft = i - 1;
3921556Srgrimes		}
3931556Srgrimes		lastc = *p++;
3941556Srgrimes		if (lastc != '\0') {
3951556Srgrimes			if (quotes && syntax[lastc] == CCTL)
3961556Srgrimes				STPUTC(CTLESC, dest);
3971556Srgrimes			STPUTC(lastc, dest);
3981556Srgrimes		}
3991556Srgrimes	}
40017987Speter
40117987Speter	/* Eat all trailing newlines */
40217987Speter	for (p--; lastc == '\n'; lastc = *--p)
4031556Srgrimes		STUNPUTC(dest);
40417987Speter
4051556Srgrimes	if (in.fd >= 0)
4061556Srgrimes		close(in.fd);
4071556Srgrimes	if (in.buf)
4081556Srgrimes		ckfree(in.buf);
4091556Srgrimes	if (in.jp)
4101556Srgrimes		exitstatus = waitforjob(in.jp);
4111556Srgrimes	if (quoted == 0)
4121556Srgrimes		recordregion(startloc, dest - stackblock(), 0);
4131556Srgrimes	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
4141556Srgrimes		(dest - stackblock()) - startloc,
4151556Srgrimes		(dest - stackblock()) - startloc,
4161556Srgrimes		stackblock() + startloc));
4171556Srgrimes	expdest = dest;
4181556Srgrimes	INTON;
4191556Srgrimes}
4201556Srgrimes
4211556Srgrimes
4221556Srgrimes
42317987SpeterSTATIC int
42417987Spetersubevalvar(p, str, subtype, startloc, varflags)
42517987Speter	char *p;
42617987Speter	char *str;
42717987Speter	int subtype;
42817987Speter	int startloc;
42917987Speter	int varflags;
43017987Speter{
43117987Speter
43217987Speter	char *startp;
43317987Speter	char *loc = NULL;
43417987Speter	int c = 0;
43517987Speter	int saveherefd = herefd;
43617987Speter	struct nodelist *saveargbackq = argbackq;
43717987Speter	herefd = -1;
43817987Speter	argstr(p, 0);
43917987Speter	STACKSTRNUL(expdest);
44017987Speter	herefd = saveherefd;
44117987Speter	argbackq = saveargbackq;
44217987Speter	startp = stackblock() + startloc;
44317987Speter
44417987Speter	switch (subtype) {
44517987Speter	case VSASSIGN:
44617987Speter		setvar(str, startp, 0);
44717987Speter		STADJUST(startp - expdest, expdest);
44817987Speter		varflags &= ~VSNUL;
44917987Speter		if (c != 0)
45017987Speter			*loc = c;
45117987Speter		return 1;
45217987Speter
45317987Speter	case VSQUESTION:
45417987Speter		if (*p != CTLENDVAR) {
45517987Speter			outfmt(&errout, "%s\n", startp);
45617987Speter			error((char *)NULL);
45717987Speter		}
45817987Speter		error("%.*s: parameter %snot set", p - str - 1,
45917987Speter		      str, (varflags & VSNUL) ? "null or "
46017987Speter					      : nullstr);
46117987Speter		return 0;
46217987Speter
46317987Speter	case VSTRIMLEFT:
46417987Speter		for (loc = startp; loc < str - 1; loc++) {
46517987Speter			c = *loc;
46617987Speter			*loc = '\0';
46717987Speter			if (patmatch(str, startp)) {
46817987Speter				*loc = c;
46917987Speter				goto recordleft;
47017987Speter			}
47117987Speter			*loc = c;
47217987Speter		}
47317987Speter		return 0;
47417987Speter
47517987Speter	case VSTRIMLEFTMAX:
47617987Speter		for (loc = str - 1; loc >= startp; loc--) {
47717987Speter			c = *loc;
47817987Speter			*loc = '\0';
47917987Speter			if (patmatch(str, startp)) {
48017987Speter				*loc = c;
48117987Speter				goto recordleft;
48217987Speter			}
48317987Speter			*loc = c;
48417987Speter		}
48517987Speter		return 0;
48617987Speter
48717987Speter	case VSTRIMRIGHT:
48817987Speter		for (loc = str - 1; loc >= startp; loc--) {
48917987Speter			if (patmatch(str, loc)) {
49017987Speter				expdest = loc;
49117987Speter				return 1;
49217987Speter			}
49317987Speter		}
49417987Speter		return 0;
49517987Speter
49617987Speter	case VSTRIMRIGHTMAX:
49717987Speter		for (loc = startp; loc < str - 1; loc++) {
49817987Speter			if (patmatch(str, loc)) {
49917987Speter				expdest = loc;
50017987Speter				return 1;
50117987Speter			}
50217987Speter		}
50317987Speter		return 0;
50417987Speter
50517987Speter
50617987Speter	default:
50717987Speter		abort();
50817987Speter	}
50917987Speter
51017987Speterrecordleft:
51117987Speter	expdest = (str - 1) - (loc - startp);
51217987Speter	while (loc != str - 1)
51317987Speter		*startp++ = *loc++;
51417987Speter	return 1;
51517987Speter}
51617987Speter
51717987Speter
5181556Srgrimes/*
5191556Srgrimes * Expand a variable, and return a pointer to the next character in the
5201556Srgrimes * input string.
5211556Srgrimes */
5221556Srgrimes
5231556SrgrimesSTATIC char *
5241556Srgrimesevalvar(p, flag)
5251556Srgrimes	char *p;
52617987Speter	int flag;
52717987Speter{
5281556Srgrimes	int subtype;
5291556Srgrimes	int varflags;
5301556Srgrimes	char *var;
5311556Srgrimes	char *val;
53217987Speter	char *pat;
5331556Srgrimes	int c;
5341556Srgrimes	int set;
5351556Srgrimes	int special;
5361556Srgrimes	int startloc;
53717987Speter	int varlen;
53817987Speter	int easy;
5391556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
5401556Srgrimes
5411556Srgrimes	varflags = *p++;
5421556Srgrimes	subtype = varflags & VSTYPE;
5431556Srgrimes	var = p;
5441556Srgrimes	special = 0;
5451556Srgrimes	if (! is_name(*p))
5461556Srgrimes		special = 1;
5471556Srgrimes	p = strchr(p, '=') + 1;
5481556Srgrimesagain: /* jump here after setting a variable with ${var=text} */
5491556Srgrimes	if (special) {
55018202Speter		set = varisset(var);
5511556Srgrimes		val = NULL;
5521556Srgrimes	} else {
5531556Srgrimes		val = lookupvar(var);
55417987Speter		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
5551556Srgrimes			val = NULL;
5561556Srgrimes			set = 0;
5571556Srgrimes		} else
5581556Srgrimes			set = 1;
5591556Srgrimes	}
56017987Speter	varlen = 0;
5611556Srgrimes	startloc = expdest - stackblock();
5621556Srgrimes	if (set && subtype != VSPLUS) {
5631556Srgrimes		/* insert the value of the variable */
5641556Srgrimes		if (special) {
56517987Speter			char *exp, *oexpdest = expdest;
56618202Speter			varvalue(var, varflags & VSQUOTE, flag & EXP_FULL);
56717987Speter			if (subtype == VSLENGTH) {
56817987Speter				for (exp = oexpdest;exp != expdest; exp++)
56917987Speter					varlen++;
57017987Speter				expdest = oexpdest;
57117987Speter			}
5721556Srgrimes		} else {
57317987Speter			char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
57417987Speter								  : BASESYNTAX;
5751556Srgrimes
57617987Speter			if (subtype == VSLENGTH) {
57717987Speter				for (;*val; val++)
57817987Speter					varlen++;
5791556Srgrimes			}
58017987Speter			else {
58117987Speter				while (*val) {
58217987Speter					if (quotes && syntax[*val] == CCTL)
58317987Speter						STPUTC(CTLESC, expdest);
58417987Speter					STPUTC(*val++, expdest);
58517987Speter				}
58617987Speter
58717987Speter			}
5881556Srgrimes		}
5891556Srgrimes	}
59017987Speter
5911556Srgrimes	if (subtype == VSPLUS)
5921556Srgrimes		set = ! set;
59317987Speter
59417987Speter	easy = ((varflags & VSQUOTE) == 0 ||
59517987Speter		(*var == '@' && shellparam.nparam != 1));
59617987Speter
59717987Speter
59817987Speter	switch (subtype) {
59917987Speter	case VSLENGTH:
60017987Speter		expdest = cvtnum(varlen, expdest);
60117987Speter		goto record;
60217987Speter
60317987Speter	case VSNORMAL:
60417987Speter		if (!easy)
60517987Speter			break;
60617987Speterrecord:
60717987Speter		recordregion(startloc, expdest - stackblock(),
60817987Speter			     varflags & VSQUOTE);
60917987Speter		break;
61017987Speter
61117987Speter	case VSPLUS:
61217987Speter	case VSMINUS:
61317987Speter		if (!set) {
6141556Srgrimes			argstr(p, flag);
61517987Speter			break;
61617987Speter		}
61717987Speter		if (easy)
61817987Speter			goto record;
61917987Speter		break;
62017987Speter
62117987Speter	case VSTRIMLEFT:
62217987Speter	case VSTRIMLEFTMAX:
62317987Speter	case VSTRIMRIGHT:
62417987Speter	case VSTRIMRIGHTMAX:
62517987Speter		if (!set)
62617987Speter			break;
62717987Speter		/*
62817987Speter		 * Terminate the string and start recording the pattern
62917987Speter		 * right after it
63017987Speter		 */
63117987Speter		STPUTC('\0', expdest);
63217987Speter		pat = expdest;
63317987Speter		if (subevalvar(p, pat, subtype, startloc, varflags))
63417987Speter			goto record;
63517987Speter		break;
63617987Speter
63717987Speter	case VSASSIGN:
63817987Speter	case VSQUESTION:
63917987Speter		if (!set) {
64017987Speter			if (subevalvar(p, var, subtype, startloc, varflags))
6411556Srgrimes				goto again;
64217987Speter			break;
6431556Srgrimes		}
64417987Speter		if (easy)
64517987Speter			goto record;
64617987Speter		break;
64717987Speter
64817987Speter	default:
64917987Speter		abort();
6501556Srgrimes	}
65117987Speter
6521556Srgrimes	if (subtype != VSNORMAL) {	/* skip to end of alternative */
6531556Srgrimes		int nesting = 1;
6541556Srgrimes		for (;;) {
6551556Srgrimes			if ((c = *p++) == CTLESC)
6561556Srgrimes				p++;
6571556Srgrimes			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6581556Srgrimes				if (set)
6591556Srgrimes					argbackq = argbackq->next;
6601556Srgrimes			} else if (c == CTLVAR) {
6611556Srgrimes				if ((*p++ & VSTYPE) != VSNORMAL)
6621556Srgrimes					nesting++;
6631556Srgrimes			} else if (c == CTLENDVAR) {
6641556Srgrimes				if (--nesting == 0)
6651556Srgrimes					break;
6661556Srgrimes			}
6671556Srgrimes		}
6681556Srgrimes	}
6691556Srgrimes	return p;
6701556Srgrimes}
6711556Srgrimes
6721556Srgrimes
6731556Srgrimes
6741556Srgrimes/*
6751556Srgrimes * Test whether a specialized variable is set.
6761556Srgrimes */
6771556Srgrimes
6781556SrgrimesSTATIC int
6791556Srgrimesvarisset(name)
68018202Speter	char *name;
6811556Srgrimes	{
6821556Srgrimes	char **ap;
68318202Speter	int num;
6841556Srgrimes
68518202Speter	if (*name == '!') {
6861556Srgrimes		if (backgndpid == -1)
6871556Srgrimes			return 0;
68818202Speter	} else if (*name == '@' || *name == '*') {
6891556Srgrimes		if (*shellparam.p == NULL)
6901556Srgrimes			return 0;
69118202Speter	} else if (is_digit(*name)) {
69218202Speter		num = atoi(name);
6931556Srgrimes		ap = shellparam.p;
69418202Speter		while (num-- > 0)
6951556Srgrimes			if (*ap++ == NULL)
6961556Srgrimes				return 0;
6971556Srgrimes	}
6981556Srgrimes	return 1;
6991556Srgrimes}
7001556Srgrimes
7011556Srgrimes
7021556Srgrimes
7031556Srgrimes/*
7041556Srgrimes * Add the value of a specialized variable to the stack string.
7051556Srgrimes */
7061556Srgrimes
7071556SrgrimesSTATIC void
7081556Srgrimesvarvalue(name, quoted, allow_split)
70918202Speter	char *name;
71017987Speter	int quoted;
71117987Speter	int allow_split;
71217987Speter{
7131556Srgrimes	int num;
7141556Srgrimes	char *p;
7151556Srgrimes	int i;
71617987Speter	extern int oexitstatus;
7171556Srgrimes	char sep;
7181556Srgrimes	char **ap;
7191556Srgrimes	char const *syntax;
7201556Srgrimes
7211556Srgrimes#define STRTODEST(p) \
7221556Srgrimes	do {\
7231556Srgrimes	if (allow_split) { \
7241556Srgrimes		syntax = quoted? DQSYNTAX : BASESYNTAX; \
7251556Srgrimes		while (*p) { \
7261556Srgrimes			if (syntax[*p] == CCTL) \
7271556Srgrimes				STPUTC(CTLESC, expdest); \
7281556Srgrimes			STPUTC(*p++, expdest); \
7291556Srgrimes		} \
7301556Srgrimes	} else \
7311556Srgrimes		while (*p) \
7321556Srgrimes			STPUTC(*p++, expdest); \
7331556Srgrimes	} while (0)
7341556Srgrimes
7351556Srgrimes
73618202Speter	switch (*name) {
7371556Srgrimes	case '$':
7381556Srgrimes		num = rootpid;
7391556Srgrimes		goto numvar;
7401556Srgrimes	case '?':
74117987Speter		num = oexitstatus;
7421556Srgrimes		goto numvar;
7431556Srgrimes	case '#':
7441556Srgrimes		num = shellparam.nparam;
7451556Srgrimes		goto numvar;
7461556Srgrimes	case '!':
7471556Srgrimes		num = backgndpid;
7481556Srgrimesnumvar:
74917987Speter		expdest = cvtnum(num, expdest);
7501556Srgrimes		break;
7511556Srgrimes	case '-':
7521556Srgrimes		for (i = 0 ; i < NOPTS ; i++) {
7531556Srgrimes			if (optlist[i].val)
7541556Srgrimes				STPUTC(optlist[i].letter, expdest);
7551556Srgrimes		}
7561556Srgrimes		break;
7571556Srgrimes	case '@':
7581556Srgrimes		if (allow_split) {
7591556Srgrimes			sep = '\0';
7601556Srgrimes			goto allargs;
7611556Srgrimes		}
7628855Srgrimes		/* fall through */
7631556Srgrimes	case '*':
7641556Srgrimes		sep = ' ';
7651556Srgrimesallargs:
7661556Srgrimes		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
7671556Srgrimes			STRTODEST(p);
7681556Srgrimes			if (*ap)
7691556Srgrimes				STPUTC(sep, expdest);
7701556Srgrimes		}
7711556Srgrimes		break;
7721556Srgrimes	case '0':
7731556Srgrimes		p = arg0;
7741556Srgrimes		STRTODEST(p);
7751556Srgrimes		break;
7761556Srgrimes	default:
77718202Speter		if (is_digit(*name)) {
77818202Speter			num = atoi(name);
77918202Speter			if (num > 0 && num <= shellparam.nparam) {
78018202Speter				p = shellparam.p[num - 1];
78118202Speter				STRTODEST(p);
78218202Speter			}
7831556Srgrimes		}
7841556Srgrimes		break;
7851556Srgrimes	}
7861556Srgrimes}
7871556Srgrimes
7881556Srgrimes
7891556Srgrimes
7901556Srgrimes/*
7911556Srgrimes * Record the the fact that we have to scan this region of the
7921556Srgrimes * string for IFS characters.
7931556Srgrimes */
7941556Srgrimes
7951556SrgrimesSTATIC void
79617987Speterrecordregion(start, end, nulonly)
79717987Speter	int start;
79817987Speter	int end;
79917987Speter	int nulonly;
80017987Speter{
8011556Srgrimes	register struct ifsregion *ifsp;
8021556Srgrimes
8031556Srgrimes	if (ifslastp == NULL) {
8041556Srgrimes		ifsp = &ifsfirst;
8051556Srgrimes	} else {
8061556Srgrimes		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
8071556Srgrimes		ifslastp->next = ifsp;
8081556Srgrimes	}
8091556Srgrimes	ifslastp = ifsp;
8101556Srgrimes	ifslastp->next = NULL;
8111556Srgrimes	ifslastp->begoff = start;
8121556Srgrimes	ifslastp->endoff = end;
8131556Srgrimes	ifslastp->nulonly = nulonly;
8141556Srgrimes}
8151556Srgrimes
8161556Srgrimes
8171556Srgrimes
8181556Srgrimes/*
8191556Srgrimes * Break the argument string into pieces based upon IFS and add the
8201556Srgrimes * strings to the argument list.  The regions of the string to be
8211556Srgrimes * searched for IFS characters have been stored by recordregion.
8221556Srgrimes */
8231556SrgrimesSTATIC void
8241556Srgrimesifsbreakup(string, arglist)
8251556Srgrimes	char *string;
8261556Srgrimes	struct arglist *arglist;
8271556Srgrimes	{
8281556Srgrimes	struct ifsregion *ifsp;
8291556Srgrimes	struct strlist *sp;
8301556Srgrimes	char *start;
8311556Srgrimes	register char *p;
8321556Srgrimes	char *q;
8331556Srgrimes	char *ifs;
83417987Speter	int ifsspc;
8351556Srgrimes
83617987Speter
8371556Srgrimes	start = string;
8381556Srgrimes	if (ifslastp != NULL) {
8391556Srgrimes		ifsp = &ifsfirst;
8401556Srgrimes		do {
8411556Srgrimes			p = string + ifsp->begoff;
8421556Srgrimes			ifs = ifsp->nulonly? nullstr : ifsval();
84317987Speter			ifsspc = strchr(ifs, ' ') != NULL;
8441556Srgrimes			while (p < string + ifsp->endoff) {
8451556Srgrimes				q = p;
8461556Srgrimes				if (*p == CTLESC)
8471556Srgrimes					p++;
8481556Srgrimes				if (strchr(ifs, *p++)) {
84917987Speter					if (q > start || !ifsspc) {
8501556Srgrimes						*q = '\0';
8511556Srgrimes						sp = (struct strlist *)stalloc(sizeof *sp);
8521556Srgrimes						sp->text = start;
8531556Srgrimes						*arglist->lastp = sp;
8541556Srgrimes						arglist->lastp = &sp->next;
8551556Srgrimes					}
85617987Speter					if (ifsspc) {
8571556Srgrimes						for (;;) {
8581556Srgrimes							if (p >= string + ifsp->endoff)
8591556Srgrimes								break;
8601556Srgrimes							q = p;
8611556Srgrimes							if (*p == CTLESC)
8621556Srgrimes								p++;
8631556Srgrimes							if (strchr(ifs, *p++) == NULL) {
8641556Srgrimes								p = q;
8651556Srgrimes								break;
8661556Srgrimes							}
8671556Srgrimes						}
8681556Srgrimes					}
8691556Srgrimes					start = p;
8701556Srgrimes				}
8711556Srgrimes			}
8721556Srgrimes		} while ((ifsp = ifsp->next) != NULL);
87317987Speter		if (*start || (!ifsspc && start > string)) {
8741556Srgrimes			sp = (struct strlist *)stalloc(sizeof *sp);
8751556Srgrimes			sp->text = start;
8761556Srgrimes			*arglist->lastp = sp;
8771556Srgrimes			arglist->lastp = &sp->next;
8781556Srgrimes		}
8791556Srgrimes	} else {
8801556Srgrimes		sp = (struct strlist *)stalloc(sizeof *sp);
8811556Srgrimes		sp->text = start;
8821556Srgrimes		*arglist->lastp = sp;
8831556Srgrimes		arglist->lastp = &sp->next;
8841556Srgrimes	}
8851556Srgrimes}
8861556Srgrimes
8871556Srgrimes
8881556Srgrimes
8891556Srgrimes/*
8901556Srgrimes * Expand shell metacharacters.  At this point, the only control characters
8911556Srgrimes * should be escapes.  The results are stored in the list exparg.
8921556Srgrimes */
8931556Srgrimes
8941556Srgrimeschar *expdir;
8951556Srgrimes
8961556Srgrimes
8971556SrgrimesSTATIC void
8981556Srgrimesexpandmeta(str, flag)
8991556Srgrimes	struct strlist *str;
90017987Speter	int flag;
90117987Speter{
9021556Srgrimes	char *p;
9031556Srgrimes	struct strlist **savelastp;
9041556Srgrimes	struct strlist *sp;
9051556Srgrimes	char c;
9061556Srgrimes	/* TODO - EXP_REDIR */
9071556Srgrimes
9081556Srgrimes	while (str) {
9091556Srgrimes		if (fflag)
9101556Srgrimes			goto nometa;
9111556Srgrimes		p = str->text;
9121556Srgrimes		for (;;) {			/* fast check for meta chars */
9131556Srgrimes			if ((c = *p++) == '\0')
9141556Srgrimes				goto nometa;
9151556Srgrimes			if (c == '*' || c == '?' || c == '[' || c == '!')
9161556Srgrimes				break;
9171556Srgrimes		}
9181556Srgrimes		savelastp = exparg.lastp;
9191556Srgrimes		INTOFF;
9201556Srgrimes		if (expdir == NULL) {
9211556Srgrimes			int i = strlen(str->text);
9221556Srgrimes			expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
9231556Srgrimes		}
9241556Srgrimes
9251556Srgrimes		expmeta(expdir, str->text);
9261556Srgrimes		ckfree(expdir);
9271556Srgrimes		expdir = NULL;
9281556Srgrimes		INTON;
9291556Srgrimes		if (exparg.lastp == savelastp) {
9308855Srgrimes			/*
9318855Srgrimes			 * no matches
9321556Srgrimes			 */
9331556Srgrimesnometa:
9341556Srgrimes			*exparg.lastp = str;
9351556Srgrimes			rmescapes(str->text);
9361556Srgrimes			exparg.lastp = &str->next;
9371556Srgrimes		} else {
9381556Srgrimes			*exparg.lastp = NULL;
9391556Srgrimes			*savelastp = sp = expsort(*savelastp);
9401556Srgrimes			while (sp->next != NULL)
9411556Srgrimes				sp = sp->next;
9421556Srgrimes			exparg.lastp = &sp->next;
9431556Srgrimes		}
9441556Srgrimes		str = str->next;
9451556Srgrimes	}
9461556Srgrimes}
9471556Srgrimes
9481556Srgrimes
9491556Srgrimes/*
9501556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion.
9511556Srgrimes */
9521556Srgrimes
9531556SrgrimesSTATIC void
9541556Srgrimesexpmeta(enddir, name)
9551556Srgrimes	char *enddir;
9561556Srgrimes	char *name;
9571556Srgrimes	{
9581556Srgrimes	register char *p;
9591556Srgrimes	char *q;
9601556Srgrimes	char *start;
9611556Srgrimes	char *endname;
9621556Srgrimes	int metaflag;
9631556Srgrimes	struct stat statb;
9641556Srgrimes	DIR *dirp;
9651556Srgrimes	struct dirent *dp;
9661556Srgrimes	int atend;
9671556Srgrimes	int matchdot;
9681556Srgrimes
9691556Srgrimes	metaflag = 0;
9701556Srgrimes	start = name;
9711556Srgrimes	for (p = name ; ; p++) {
9721556Srgrimes		if (*p == '*' || *p == '?')
9731556Srgrimes			metaflag = 1;
9741556Srgrimes		else if (*p == '[') {
9751556Srgrimes			q = p + 1;
9761556Srgrimes			if (*q == '!')
9771556Srgrimes				q++;
9781556Srgrimes			for (;;) {
9791556Srgrimes				if (*q == CTLESC)
9801556Srgrimes					q++;
9811556Srgrimes				if (*q == '/' || *q == '\0')
9821556Srgrimes					break;
9831556Srgrimes				if (*++q == ']') {
9841556Srgrimes					metaflag = 1;
9851556Srgrimes					break;
9861556Srgrimes				}
9871556Srgrimes			}
9881556Srgrimes		} else if (*p == '!' && p[1] == '!'	&& (p == name || p[-1] == '/')) {
9891556Srgrimes			metaflag = 1;
9901556Srgrimes		} else if (*p == '\0')
9911556Srgrimes			break;
9921556Srgrimes		else if (*p == CTLESC)
9931556Srgrimes			p++;
9941556Srgrimes		if (*p == '/') {
9951556Srgrimes			if (metaflag)
9961556Srgrimes				break;
9971556Srgrimes			start = p + 1;
9981556Srgrimes		}
9991556Srgrimes	}
10001556Srgrimes	if (metaflag == 0) {	/* we've reached the end of the file name */
10011556Srgrimes		if (enddir != expdir)
10021556Srgrimes			metaflag++;
10031556Srgrimes		for (p = name ; ; p++) {
10041556Srgrimes			if (*p == CTLESC)
10051556Srgrimes				p++;
10061556Srgrimes			*enddir++ = *p;
10071556Srgrimes			if (*p == '\0')
10081556Srgrimes				break;
10091556Srgrimes		}
10101556Srgrimes		if (metaflag == 0 || stat(expdir, &statb) >= 0)
10111556Srgrimes			addfname(expdir);
10121556Srgrimes		return;
10131556Srgrimes	}
10141556Srgrimes	endname = p;
10151556Srgrimes	if (start != name) {
10161556Srgrimes		p = name;
10171556Srgrimes		while (p < start) {
10181556Srgrimes			if (*p == CTLESC)
10191556Srgrimes				p++;
10201556Srgrimes			*enddir++ = *p++;
10211556Srgrimes		}
10221556Srgrimes	}
10231556Srgrimes	if (enddir == expdir) {
10241556Srgrimes		p = ".";
10251556Srgrimes	} else if (enddir == expdir + 1 && *expdir == '/') {
10261556Srgrimes		p = "/";
10271556Srgrimes	} else {
10281556Srgrimes		p = expdir;
10291556Srgrimes		enddir[-1] = '\0';
10301556Srgrimes	}
10311556Srgrimes	if ((dirp = opendir(p)) == NULL)
10321556Srgrimes		return;
10331556Srgrimes	if (enddir != expdir)
10341556Srgrimes		enddir[-1] = '/';
10351556Srgrimes	if (*endname == 0) {
10361556Srgrimes		atend = 1;
10371556Srgrimes	} else {
10381556Srgrimes		atend = 0;
10391556Srgrimes		*endname++ = '\0';
10401556Srgrimes	}
10411556Srgrimes	matchdot = 0;
104217987Speter	if (start[0] == '.' || (start[0] == CTLESC && start[1] == '.'))
10431556Srgrimes		matchdot++;
10441556Srgrimes	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
10451556Srgrimes		if (dp->d_name[0] == '.' && ! matchdot)
10461556Srgrimes			continue;
10471556Srgrimes		if (patmatch(start, dp->d_name)) {
10481556Srgrimes			if (atend) {
10491556Srgrimes				scopy(dp->d_name, enddir);
10501556Srgrimes				addfname(expdir);
10511556Srgrimes			} else {
10521556Srgrimes				char *q;
105317987Speter				for (p = enddir, q = dp->d_name;
105417987Speter				     (*p++ = *q++) != '\0';)
105517987Speter					continue;
10561556Srgrimes				p[-1] = '/';
10571556Srgrimes				expmeta(p, endname);
10581556Srgrimes			}
10591556Srgrimes		}
10601556Srgrimes	}
10611556Srgrimes	closedir(dirp);
10621556Srgrimes	if (! atend)
10631556Srgrimes		endname[-1] = '/';
10641556Srgrimes}
10651556Srgrimes
10661556Srgrimes
10671556Srgrimes/*
10681556Srgrimes * Add a file name to the list.
10691556Srgrimes */
10701556Srgrimes
10711556SrgrimesSTATIC void
10721556Srgrimesaddfname(name)
10731556Srgrimes	char *name;
10741556Srgrimes	{
10751556Srgrimes	char *p;
10761556Srgrimes	struct strlist *sp;
10771556Srgrimes
10781556Srgrimes	p = stalloc(strlen(name) + 1);
10791556Srgrimes	scopy(name, p);
10801556Srgrimes	sp = (struct strlist *)stalloc(sizeof *sp);
10811556Srgrimes	sp->text = p;
10821556Srgrimes	*exparg.lastp = sp;
10831556Srgrimes	exparg.lastp = &sp->next;
10841556Srgrimes}
10851556Srgrimes
10861556Srgrimes
10871556Srgrimes/*
10881556Srgrimes * Sort the results of file name expansion.  It calculates the number of
10891556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the
10901556Srgrimes * work.
10911556Srgrimes */
10921556Srgrimes
10931556SrgrimesSTATIC struct strlist *
10941556Srgrimesexpsort(str)
10951556Srgrimes	struct strlist *str;
10961556Srgrimes	{
10971556Srgrimes	int len;
10981556Srgrimes	struct strlist *sp;
10991556Srgrimes
11001556Srgrimes	len = 0;
11011556Srgrimes	for (sp = str ; sp ; sp = sp->next)
11021556Srgrimes		len++;
11031556Srgrimes	return msort(str, len);
11041556Srgrimes}
11051556Srgrimes
11061556Srgrimes
11071556SrgrimesSTATIC struct strlist *
11081556Srgrimesmsort(list, len)
11091556Srgrimes	struct strlist *list;
111017987Speter	int len;
111117987Speter{
111217987Speter	struct strlist *p, *q = NULL;
11131556Srgrimes	struct strlist **lpp;
11141556Srgrimes	int half;
11151556Srgrimes	int n;
11161556Srgrimes
11171556Srgrimes	if (len <= 1)
11181556Srgrimes		return list;
11198855Srgrimes	half = len >> 1;
11201556Srgrimes	p = list;
11211556Srgrimes	for (n = half ; --n >= 0 ; ) {
11221556Srgrimes		q = p;
11231556Srgrimes		p = p->next;
11241556Srgrimes	}
11251556Srgrimes	q->next = NULL;			/* terminate first half of list */
11261556Srgrimes	q = msort(list, half);		/* sort first half of list */
11271556Srgrimes	p = msort(p, len - half);		/* sort second half */
11281556Srgrimes	lpp = &list;
11291556Srgrimes	for (;;) {
11301556Srgrimes		if (strcmp(p->text, q->text) < 0) {
11311556Srgrimes			*lpp = p;
11321556Srgrimes			lpp = &p->next;
11331556Srgrimes			if ((p = *lpp) == NULL) {
11341556Srgrimes				*lpp = q;
11351556Srgrimes				break;
11361556Srgrimes			}
11371556Srgrimes		} else {
11381556Srgrimes			*lpp = q;
11391556Srgrimes			lpp = &q->next;
11401556Srgrimes			if ((q = *lpp) == NULL) {
11411556Srgrimes				*lpp = p;
11421556Srgrimes				break;
11431556Srgrimes			}
11441556Srgrimes		}
11451556Srgrimes	}
11461556Srgrimes	return list;
11471556Srgrimes}
11481556Srgrimes
11491556Srgrimes
11501556Srgrimes
11511556Srgrimes/*
11521556Srgrimes * Returns true if the pattern matches the string.
11531556Srgrimes */
11541556Srgrimes
11551556Srgrimesint
11561556Srgrimespatmatch(pattern, string)
11571556Srgrimes	char *pattern;
11581556Srgrimes	char *string;
11591556Srgrimes	{
11601556Srgrimes#ifdef notdef
11611556Srgrimes	if (pattern[0] == '!' && pattern[1] == '!')
11621556Srgrimes		return 1 - pmatch(pattern + 2, string);
11631556Srgrimes	else
11641556Srgrimes#endif
11651556Srgrimes		return pmatch(pattern, string);
11661556Srgrimes}
11671556Srgrimes
116817987Speter
116917525SacheSTATIC int
11701556Srgrimespmatch(pattern, string)
11711556Srgrimes	char *pattern;
11721556Srgrimes	char *string;
11731556Srgrimes	{
11741556Srgrimes	register char *p, *q;
11751556Srgrimes	register char c;
11761556Srgrimes
11771556Srgrimes	p = pattern;
11781556Srgrimes	q = string;
11791556Srgrimes	for (;;) {
11801556Srgrimes		switch (c = *p++) {
11811556Srgrimes		case '\0':
11821556Srgrimes			goto breakloop;
11831556Srgrimes		case CTLESC:
11841556Srgrimes			if (*q++ != *p++)
11851556Srgrimes				return 0;
11861556Srgrimes			break;
11871556Srgrimes		case '?':
11881556Srgrimes			if (*q++ == '\0')
11891556Srgrimes				return 0;
11901556Srgrimes			break;
11911556Srgrimes		case '*':
11921556Srgrimes			c = *p;
11931556Srgrimes			if (c != CTLESC && c != '?' && c != '*' && c != '[') {
11941556Srgrimes				while (*q != c) {
11951556Srgrimes					if (*q == '\0')
11961556Srgrimes						return 0;
11971556Srgrimes					q++;
11981556Srgrimes				}
11991556Srgrimes			}
12001556Srgrimes			do {
12011556Srgrimes				if (pmatch(p, q))
12021556Srgrimes					return 1;
12031556Srgrimes			} while (*q++ != '\0');
12041556Srgrimes			return 0;
12051556Srgrimes		case '[': {
12061556Srgrimes			char *endp;
12071556Srgrimes			int invert, found;
12081556Srgrimes			char chr;
12091556Srgrimes
12101556Srgrimes			endp = p;
12111556Srgrimes			if (*endp == '!')
12121556Srgrimes				endp++;
12131556Srgrimes			for (;;) {
12141556Srgrimes				if (*endp == '\0')
12151556Srgrimes					goto dft;		/* no matching ] */
12161556Srgrimes				if (*endp == CTLESC)
12171556Srgrimes					endp++;
12181556Srgrimes				if (*++endp == ']')
12191556Srgrimes					break;
12201556Srgrimes			}
12211556Srgrimes			invert = 0;
12221556Srgrimes			if (*p == '!') {
12231556Srgrimes				invert++;
12241556Srgrimes				p++;
12251556Srgrimes			}
12261556Srgrimes			found = 0;
12271556Srgrimes			chr = *q++;
122817987Speter			if (chr == '\0')
122917987Speter				return 0;
12301556Srgrimes			c = *p++;
12311556Srgrimes			do {
12321556Srgrimes				if (c == CTLESC)
12331556Srgrimes					c = *p++;
12341556Srgrimes				if (*p == '-' && p[1] != ']') {
12351556Srgrimes					p++;
12361556Srgrimes					if (*p == CTLESC)
12371556Srgrimes						p++;
123817557Sache					if (   collate_range_cmp(chr, c) >= 0
123917557Sache					    && collate_range_cmp(chr, *p) <= 0
124017525Sache					   )
12411556Srgrimes						found = 1;
12421556Srgrimes					p++;
12431556Srgrimes				} else {
12441556Srgrimes					if (chr == c)
12451556Srgrimes						found = 1;
12461556Srgrimes				}
12471556Srgrimes			} while ((c = *p++) != ']');
12481556Srgrimes			if (found == invert)
12491556Srgrimes				return 0;
12501556Srgrimes			break;
12511556Srgrimes		}
12521556Srgrimesdft:	        default:
12531556Srgrimes			if (*q++ != c)
12541556Srgrimes				return 0;
12551556Srgrimes			break;
12561556Srgrimes		}
12571556Srgrimes	}
12581556Srgrimesbreakloop:
12591556Srgrimes	if (*q != '\0')
12601556Srgrimes		return 0;
12611556Srgrimes	return 1;
12621556Srgrimes}
12631556Srgrimes
12641556Srgrimes
12651556Srgrimes
12661556Srgrimes/*
12671556Srgrimes * Remove any CTLESC characters from a string.
12681556Srgrimes */
12691556Srgrimes
12701556Srgrimesvoid
12711556Srgrimesrmescapes(str)
12721556Srgrimes	char *str;
12731556Srgrimes	{
12741556Srgrimes	register char *p, *q;
12751556Srgrimes
12761556Srgrimes	p = str;
12771556Srgrimes	while (*p != CTLESC) {
12781556Srgrimes		if (*p++ == '\0')
12791556Srgrimes			return;
12801556Srgrimes	}
12811556Srgrimes	q = p;
12821556Srgrimes	while (*p) {
12831556Srgrimes		if (*p == CTLESC)
12841556Srgrimes			p++;
12851556Srgrimes		*q++ = *p++;
12861556Srgrimes	}
12871556Srgrimes	*q = '\0';
12881556Srgrimes}
12891556Srgrimes
12901556Srgrimes
12911556Srgrimes
12921556Srgrimes/*
12931556Srgrimes * See if a pattern matches in a case statement.
12941556Srgrimes */
12951556Srgrimes
12961556Srgrimesint
12971556Srgrimescasematch(pattern, val)
12981556Srgrimes	union node *pattern;
12991556Srgrimes	char *val;
13001556Srgrimes	{
13011556Srgrimes	struct stackmark smark;
13021556Srgrimes	int result;
13031556Srgrimes	char *p;
13041556Srgrimes
13051556Srgrimes	setstackmark(&smark);
13061556Srgrimes	argbackq = pattern->narg.backquote;
13071556Srgrimes	STARTSTACKSTR(expdest);
13081556Srgrimes	ifslastp = NULL;
13091556Srgrimes	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
13101556Srgrimes	STPUTC('\0', expdest);
13111556Srgrimes	p = grabstackstr(expdest);
13121556Srgrimes	result = patmatch(p, val);
13131556Srgrimes	popstackmark(&smark);
13141556Srgrimes	return result;
13151556Srgrimes}
131617987Speter
131717987Speter/*
131817987Speter * Our own itoa().
131917987Speter */
132017987Speter
132117987SpeterSTATIC char *
132217987Spetercvtnum(num, buf)
132317987Speter	int num;
132417987Speter	char *buf;
132517987Speter	{
132617987Speter	char temp[32];
132717987Speter	int neg = num < 0;
132817987Speter	char *p = temp + 31;
132917987Speter
133017987Speter	temp[31] = '\0';
133117987Speter
133217987Speter	do {
133317987Speter		*--p = num % 10 + '0';
133417987Speter	} while ((num /= 10) != 0);
133517987Speter
133617987Speter	if (neg)
133717987Speter		*--p = '-';
133817987Speter
133917987Speter	while (*p)
134017987Speter		STPUTC(*p++, buf);
134117987Speter	return buf;
134217987Speter}
1343