expand.c revision 17525
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 *
3617525Sache *	$Id: expand.c,v 1.4 1995/05/30 00:07:13 rgrimes Exp $
371556Srgrimes */
381556Srgrimes
391556Srgrimes#ifndef lint
401556Srgrimesstatic char sccsid[] = "@(#)expand.c	8.2 (Berkeley) 10/22/93";
411556Srgrimes#endif /* not lint */
421556Srgrimes
431556Srgrimes/*
441556Srgrimes * Routines to expand arguments to commands.  We have to deal with
451556Srgrimes * backquotes, shell variables, and file metacharacters.
461556Srgrimes */
471556Srgrimes
481556Srgrimes#include "shell.h"
491556Srgrimes#include "main.h"
501556Srgrimes#include "nodes.h"
511556Srgrimes#include "eval.h"
521556Srgrimes#include "expand.h"
531556Srgrimes#include "syntax.h"
541556Srgrimes#include "parser.h"
551556Srgrimes#include "jobs.h"
561556Srgrimes#include "options.h"
571556Srgrimes#include "var.h"
581556Srgrimes#include "input.h"
591556Srgrimes#include "output.h"
601556Srgrimes#include "memalloc.h"
611556Srgrimes#include "error.h"
621556Srgrimes#include "mystring.h"
631556Srgrimes#include <sys/types.h>
641556Srgrimes#include <sys/time.h>
651556Srgrimes#include <sys/stat.h>
661556Srgrimes#include <errno.h>
671556Srgrimes#include <dirent.h>
681556Srgrimes#include <pwd.h>
691556Srgrimes
701556Srgrimes/*
711556Srgrimes * Structure specifying which parts of the string should be searched
721556Srgrimes * for IFS characters.
731556Srgrimes */
741556Srgrimes
751556Srgrimesstruct ifsregion {
761556Srgrimes	struct ifsregion *next;	/* next region in list */
771556Srgrimes	int begoff;		/* offset of start of region */
781556Srgrimes	int endoff;		/* offset of end of region */
791556Srgrimes	int nulonly;		/* search for nul bytes only */
801556Srgrimes};
811556Srgrimes
821556Srgrimes
831556Srgrimeschar *expdest;			/* output of current string */
841556Srgrimesstruct nodelist *argbackq;	/* list of back quote expressions */
851556Srgrimesstruct ifsregion ifsfirst;	/* first struct in list of ifs regions */
861556Srgrimesstruct ifsregion *ifslastp;	/* last struct in list */
871556Srgrimesstruct arglist exparg;		/* holds expanded arg list */
881556Srgrimes
891556Srgrimes#ifdef __STDC__
901556SrgrimesSTATIC void argstr(char *, int);
911556SrgrimesSTATIC void expbackq(union node *, int, int);
921556SrgrimesSTATIC char *evalvar(char *, int);
931556SrgrimesSTATIC int varisset(int);
941556SrgrimesSTATIC void varvalue(int, int, int);
951556SrgrimesSTATIC void recordregion(int, int, int);
961556SrgrimesSTATIC void ifsbreakup(char *, struct arglist *);
971556SrgrimesSTATIC void expandmeta(struct strlist *, int);
981556SrgrimesSTATIC void expmeta(char *, char *);
991556SrgrimesSTATIC void expari(int);
1001556SrgrimesSTATIC void addfname(char *);
1011556SrgrimesSTATIC struct strlist *expsort(struct strlist *);
1021556SrgrimesSTATIC struct strlist *msort(struct strlist *, int);
1031556SrgrimesSTATIC int pmatch(char *, char *);
1041556SrgrimesSTATIC char *exptilde(char *, int);
10517525SacheSTATIC int collcmp (int, int);
1061556Srgrimes#else
1071556SrgrimesSTATIC void argstr();
1081556SrgrimesSTATIC void expbackq();
1091556SrgrimesSTATIC char *evalvar();
1101556SrgrimesSTATIC int varisset();
1111556SrgrimesSTATIC void varvalue();
1121556SrgrimesSTATIC void recordregion();
1131556SrgrimesSTATIC void ifsbreakup();
1141556SrgrimesSTATIC void expandmeta();
1151556SrgrimesSTATIC void expmeta();
1161556SrgrimesSTATIC void expari();
1171556SrgrimesSTATIC void addfname();
1181556SrgrimesSTATIC struct strlist *expsort();
1191556SrgrimesSTATIC struct strlist *msort();
1201556SrgrimesSTATIC int pmatch();
1211556SrgrimesSTATIC char *exptilde();
12217525SacheSTATIC int collcmp ();
1231556Srgrimes#endif
1241556Srgrimes
1251556Srgrimes/*
1261556Srgrimes * Expand shell variables and backquotes inside a here document.
1271556Srgrimes */
1281556Srgrimes
1291556Srgrimesvoid
1301556Srgrimesexpandhere(arg, fd)
1311556Srgrimes	union node *arg;	/* the document */
1321556Srgrimes	int fd;			/* where to write the expanded version */
1331556Srgrimes	{
1341556Srgrimes	herefd = fd;
1351556Srgrimes	expandarg(arg, (struct arglist *)NULL, 0);
1361556Srgrimes	xwrite(fd, stackblock(), expdest - stackblock());
1371556Srgrimes}
1381556Srgrimes
1391556Srgrimes
1401556Srgrimes/*
1411556Srgrimes * Perform variable substitution and command substitution on an argument,
1421556Srgrimes * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
1431556Srgrimes * perform splitting and file name expansion.  When arglist is NULL, perform
1441556Srgrimes * here document expansion.
1451556Srgrimes */
1461556Srgrimes
1471556Srgrimesvoid
1481556Srgrimesexpandarg(arg, arglist, flag)
1491556Srgrimes	union node *arg;
1501556Srgrimes	struct arglist *arglist;
1511556Srgrimes	{
1521556Srgrimes	struct strlist *sp;
1531556Srgrimes	char *p;
1541556Srgrimes
1551556Srgrimes	argbackq = arg->narg.backquote;
1561556Srgrimes	STARTSTACKSTR(expdest);
1571556Srgrimes	ifsfirst.next = NULL;
1581556Srgrimes	ifslastp = NULL;
1591556Srgrimes	argstr(arg->narg.text, flag);
1601556Srgrimes	if (arglist == NULL) {
1611556Srgrimes		return;			/* here document expanded */
1621556Srgrimes	}
1631556Srgrimes	STPUTC('\0', expdest);
1641556Srgrimes	p = grabstackstr(expdest);
1651556Srgrimes	exparg.lastp = &exparg.list;
1661556Srgrimes	/*
1671556Srgrimes	 * TODO - EXP_REDIR
1681556Srgrimes	 */
1691556Srgrimes	if (flag & EXP_FULL) {
1701556Srgrimes		ifsbreakup(p, &exparg);
1711556Srgrimes		*exparg.lastp = NULL;
1721556Srgrimes		exparg.lastp = &exparg.list;
1731556Srgrimes		expandmeta(exparg.list, flag);
1741556Srgrimes	} else {
1751556Srgrimes		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
1761556Srgrimes			rmescapes(p);
1771556Srgrimes		sp = (struct strlist *)stalloc(sizeof (struct strlist));
1781556Srgrimes		sp->text = p;
1791556Srgrimes		*exparg.lastp = sp;
1801556Srgrimes		exparg.lastp = &sp->next;
1811556Srgrimes	}
1821556Srgrimes	while (ifsfirst.next != NULL) {
1831556Srgrimes		struct ifsregion *ifsp;
1841556Srgrimes		INTOFF;
1851556Srgrimes		ifsp = ifsfirst.next->next;
1861556Srgrimes		ckfree(ifsfirst.next);
1871556Srgrimes		ifsfirst.next = ifsp;
1881556Srgrimes		INTON;
1891556Srgrimes	}
1901556Srgrimes	*exparg.lastp = NULL;
1911556Srgrimes	if (exparg.list) {
1921556Srgrimes		*arglist->lastp = exparg.list;
1931556Srgrimes		arglist->lastp = exparg.lastp;
1941556Srgrimes	}
1951556Srgrimes}
1961556Srgrimes
1971556Srgrimes
1981556Srgrimes
1991556Srgrimes/*
2001556Srgrimes * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
2011556Srgrimes * characters to allow for further processing.  Otherwise treat
2021556Srgrimes * $@ like $* since no splitting will be performed.
2031556Srgrimes */
2041556Srgrimes
2051556SrgrimesSTATIC void
2061556Srgrimesargstr(p, flag)
2071556Srgrimes	register char *p;
2081556Srgrimes	{
2091556Srgrimes	register char c;
2101556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);	/* do CTLESC */
2111556Srgrimes	int firsteq = 1;
2121556Srgrimes
2131556Srgrimes	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
2141556Srgrimes		p = exptilde(p, flag);
2151556Srgrimes	for (;;) {
2161556Srgrimes		switch (c = *p++) {
2171556Srgrimes		case '\0':
2181556Srgrimes		case CTLENDVAR: /* ??? */
2191556Srgrimes			goto breakloop;
2201556Srgrimes		case CTLESC:
2211556Srgrimes			if (quotes)
2221556Srgrimes				STPUTC(c, expdest);
2231556Srgrimes			c = *p++;
2241556Srgrimes			STPUTC(c, expdest);
2251556Srgrimes			break;
2261556Srgrimes		case CTLVAR:
2271556Srgrimes			p = evalvar(p, flag);
2281556Srgrimes			break;
2291556Srgrimes		case CTLBACKQ:
2301556Srgrimes		case CTLBACKQ|CTLQUOTE:
2311556Srgrimes			expbackq(argbackq->n, c & CTLQUOTE, flag);
2321556Srgrimes			argbackq = argbackq->next;
2331556Srgrimes			break;
2341556Srgrimes		case CTLENDARI:
2351556Srgrimes			expari(flag);
2361556Srgrimes			break;
2371556Srgrimes		case ':':
2381556Srgrimes		case '=':
2391556Srgrimes			/*
2401556Srgrimes			 * sort of a hack - expand tildes in variable
2411556Srgrimes			 * assignments (after the first '=' and after ':'s).
2421556Srgrimes			 */
2431556Srgrimes			STPUTC(c, expdest);
2441556Srgrimes			if (flag & EXP_VARTILDE && *p == '~') {
2451556Srgrimes				if (c == '=') {
2461556Srgrimes					if (firsteq)
2471556Srgrimes						firsteq = 0;
2481556Srgrimes					else
2491556Srgrimes						break;
2501556Srgrimes				}
2511556Srgrimes				p = exptilde(p, flag);
2521556Srgrimes			}
2531556Srgrimes			break;
2541556Srgrimes		default:
2551556Srgrimes			STPUTC(c, expdest);
2561556Srgrimes		}
2571556Srgrimes	}
2581556Srgrimesbreakloop:;
2591556Srgrimes}
2601556Srgrimes
2611556SrgrimesSTATIC char *
2621556Srgrimesexptilde(p, flag)
2631556Srgrimes	char *p;
2641556Srgrimes	{
2651556Srgrimes	char c, *startp = p;
2661556Srgrimes	struct passwd *pw;
2671556Srgrimes	char *home;
2681556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
2691556Srgrimes
2701556Srgrimes	while (c = *p) {
2711556Srgrimes		switch(c) {
2721556Srgrimes		case CTLESC:
2731556Srgrimes			return (startp);
2741556Srgrimes		case ':':
2751556Srgrimes			if (flag & EXP_VARTILDE)
2761556Srgrimes				goto done;
2771556Srgrimes			break;
2781556Srgrimes		case '/':
2791556Srgrimes			goto done;
2801556Srgrimes		}
2811556Srgrimes		p++;
2821556Srgrimes	}
2831556Srgrimesdone:
2841556Srgrimes	*p = '\0';
2851556Srgrimes	if (*(startp+1) == '\0') {
2861556Srgrimes		if ((home = lookupvar("HOME")) == NULL)
2871556Srgrimes			goto lose;
2881556Srgrimes	} else {
2891556Srgrimes		if ((pw = getpwnam(startp+1)) == NULL)
2901556Srgrimes			goto lose;
2911556Srgrimes		home = pw->pw_dir;
2921556Srgrimes	}
2931556Srgrimes	if (*home == '\0')
2941556Srgrimes		goto lose;
2951556Srgrimes	*p = c;
2961556Srgrimes	while (c = *home++) {
2971556Srgrimes		if (quotes && SQSYNTAX[c] == CCTL)
2981556Srgrimes			STPUTC(CTLESC, expdest);
2991556Srgrimes		STPUTC(c, expdest);
3001556Srgrimes	}
3011556Srgrimes	return (p);
3021556Srgrimeslose:
3031556Srgrimes	*p = c;
3041556Srgrimes	return (startp);
3051556Srgrimes}
3061556Srgrimes
3071556Srgrimes
3081556Srgrimes/*
3091556Srgrimes * Expand arithmetic expression.  Backup to start of expression,
3101556Srgrimes * evaluate, place result in (backed up) result, adjust string position.
3111556Srgrimes */
3121556Srgrimesvoid
3131556Srgrimesexpari(flag)
3141556Srgrimes	{
3151556Srgrimes	char *p, *start;
3161556Srgrimes	int result;
3171556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
3181556Srgrimes
3191556Srgrimes	/*
3201556Srgrimes	 * This routine is slightly over-compilcated for
3211556Srgrimes	 * efficiency.  First we make sure there is
3221556Srgrimes	 * enough space for the result, which may be bigger
3231556Srgrimes	 * than the expression if we add exponentation.  Next we
3241556Srgrimes	 * scan backwards looking for the start of arithmetic.  If the
3251556Srgrimes	 * next previous character is a CTLESC character, then we
3261556Srgrimes	 * have to rescan starting from the beginning since CTLESC
3278855Srgrimes	 * characters have to be processed left to right.
3281556Srgrimes	 */
3291556Srgrimes	CHECKSTRSPACE(8, expdest);
3308855Srgrimes	USTPUTC('\0', expdest);
3311556Srgrimes	start = stackblock();
3321556Srgrimes	p = expdest;
3331556Srgrimes	while (*p != CTLARI && p >= start)
3341556Srgrimes		--p;
3351556Srgrimes	if (*p != CTLARI)
3361556Srgrimes		error("missing CTLARI (shouldn't happen)");
3371556Srgrimes	if (p > start && *(p-1) == CTLESC)
3381556Srgrimes		for (p = start; *p != CTLARI; p++)
3391556Srgrimes			if (*p == CTLESC)
3401556Srgrimes				p++;
3411556Srgrimes	if (quotes)
3421556Srgrimes		rmescapes(p+1);
3431556Srgrimes	result = arith(p+1);
3441556Srgrimes	fmtstr(p, 10, "%d", result);
3451556Srgrimes	while (*p++)
3461556Srgrimes		;
3471556Srgrimes	result = expdest - p + 1;
3481556Srgrimes	STADJUST(-result, expdest);
3491556Srgrimes}
3501556Srgrimes
3511556Srgrimes
3521556Srgrimes/*
3531556Srgrimes * Expand stuff in backwards quotes.
3541556Srgrimes */
3551556Srgrimes
3561556SrgrimesSTATIC void
3571556Srgrimesexpbackq(cmd, quoted, flag)
3581556Srgrimes	union node *cmd;
3591556Srgrimes	{
3601556Srgrimes	struct backcmd in;
3611556Srgrimes	int i;
3621556Srgrimes	char buf[128];
3631556Srgrimes	char *p;
3641556Srgrimes	char *dest = expdest;
3651556Srgrimes	struct ifsregion saveifs, *savelastp;
3661556Srgrimes	struct nodelist *saveargbackq;
3671556Srgrimes	char lastc;
3681556Srgrimes	int startloc = dest - stackblock();
3691556Srgrimes	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
3701556Srgrimes	int saveherefd;
3711556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
3721556Srgrimes
3731556Srgrimes	INTOFF;
3741556Srgrimes	saveifs = ifsfirst;
3751556Srgrimes	savelastp = ifslastp;
3761556Srgrimes	saveargbackq = argbackq;
3778855Srgrimes	saveherefd = herefd;
3781556Srgrimes	herefd = -1;
3791556Srgrimes	p = grabstackstr(dest);
3801556Srgrimes	evalbackcmd(cmd, &in);
3811556Srgrimes	ungrabstackstr(p, dest);
3821556Srgrimes	ifsfirst = saveifs;
3831556Srgrimes	ifslastp = savelastp;
3841556Srgrimes	argbackq = saveargbackq;
3851556Srgrimes	herefd = saveherefd;
3861556Srgrimes
3871556Srgrimes	p = in.buf;
3881556Srgrimes	lastc = '\0';
3891556Srgrimes	for (;;) {
3901556Srgrimes		if (--in.nleft < 0) {
3911556Srgrimes			if (in.fd < 0)
3921556Srgrimes				break;
3931556Srgrimes			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
3941556Srgrimes			TRACE(("expbackq: read returns %d\n", i));
3951556Srgrimes			if (i <= 0)
3961556Srgrimes				break;
3971556Srgrimes			p = buf;
3981556Srgrimes			in.nleft = i - 1;
3991556Srgrimes		}
4001556Srgrimes		lastc = *p++;
4011556Srgrimes		if (lastc != '\0') {
4021556Srgrimes			if (quotes && syntax[lastc] == CCTL)
4031556Srgrimes				STPUTC(CTLESC, dest);
4041556Srgrimes			STPUTC(lastc, dest);
4051556Srgrimes		}
4061556Srgrimes	}
4076804Sguido	p--;
4086804Sguido	while (lastc == '\n') {
4091556Srgrimes		STUNPUTC(dest);
4106804Sguido		lastc = *--p;
4111556Srgrimes	}
4121556Srgrimes	if (in.fd >= 0)
4131556Srgrimes		close(in.fd);
4141556Srgrimes	if (in.buf)
4151556Srgrimes		ckfree(in.buf);
4161556Srgrimes	if (in.jp)
4171556Srgrimes		exitstatus = waitforjob(in.jp);
4181556Srgrimes	if (quoted == 0)
4191556Srgrimes		recordregion(startloc, dest - stackblock(), 0);
4201556Srgrimes	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
4211556Srgrimes		(dest - stackblock()) - startloc,
4221556Srgrimes		(dest - stackblock()) - startloc,
4231556Srgrimes		stackblock() + startloc));
4241556Srgrimes	expdest = dest;
4251556Srgrimes	INTON;
4261556Srgrimes}
4271556Srgrimes
4281556Srgrimes
4291556Srgrimes
4301556Srgrimes/*
4311556Srgrimes * Expand a variable, and return a pointer to the next character in the
4321556Srgrimes * input string.
4331556Srgrimes */
4341556Srgrimes
4351556SrgrimesSTATIC char *
4361556Srgrimesevalvar(p, flag)
4371556Srgrimes	char *p;
4381556Srgrimes	{
4391556Srgrimes	int subtype;
4401556Srgrimes	int varflags;
4411556Srgrimes	char *var;
4421556Srgrimes	char *val;
4431556Srgrimes	int c;
4441556Srgrimes	int set;
4451556Srgrimes	int special;
4461556Srgrimes	int startloc;
4471556Srgrimes	int quotes = flag & (EXP_FULL | EXP_CASE);
4481556Srgrimes
4491556Srgrimes	varflags = *p++;
4501556Srgrimes	subtype = varflags & VSTYPE;
4511556Srgrimes	var = p;
4521556Srgrimes	special = 0;
4531556Srgrimes	if (! is_name(*p))
4541556Srgrimes		special = 1;
4551556Srgrimes	p = strchr(p, '=') + 1;
4561556Srgrimesagain: /* jump here after setting a variable with ${var=text} */
4571556Srgrimes	if (special) {
4581556Srgrimes		set = varisset(*var);
4591556Srgrimes		val = NULL;
4601556Srgrimes	} else {
4611556Srgrimes		val = lookupvar(var);
4621556Srgrimes		if (val == NULL || (varflags & VSNUL) && val[0] == '\0') {
4631556Srgrimes			val = NULL;
4641556Srgrimes			set = 0;
4651556Srgrimes		} else
4661556Srgrimes			set = 1;
4671556Srgrimes	}
4681556Srgrimes	startloc = expdest - stackblock();
4691556Srgrimes	if (set && subtype != VSPLUS) {
4701556Srgrimes		/* insert the value of the variable */
4711556Srgrimes		if (special) {
4721556Srgrimes			varvalue(*var, varflags & VSQUOTE, flag & EXP_FULL);
4731556Srgrimes		} else {
4741556Srgrimes			char const *syntax = (varflags & VSQUOTE)? DQSYNTAX : BASESYNTAX;
4751556Srgrimes
4761556Srgrimes			while (*val) {
4771556Srgrimes				if (quotes && syntax[*val] == CCTL)
4781556Srgrimes					STPUTC(CTLESC, expdest);
4791556Srgrimes				STPUTC(*val++, expdest);
4801556Srgrimes			}
4811556Srgrimes		}
4821556Srgrimes	}
4831556Srgrimes	if (subtype == VSPLUS)
4841556Srgrimes		set = ! set;
4851556Srgrimes	if (((varflags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1))
4861556Srgrimes	 && (set || subtype == VSNORMAL))
4871556Srgrimes		recordregion(startloc, expdest - stackblock(), varflags & VSQUOTE);
4881556Srgrimes	if (! set && subtype != VSNORMAL) {
4891556Srgrimes		if (subtype == VSPLUS || subtype == VSMINUS) {
4901556Srgrimes			argstr(p, flag);
4911556Srgrimes		} else {
4921556Srgrimes			char *startp;
4931556Srgrimes			int saveherefd = herefd;
4941556Srgrimes			struct nodelist *saveargbackq = argbackq;
4951556Srgrimes			herefd = -1;
4961556Srgrimes			argstr(p, 0);
4971556Srgrimes			STACKSTRNUL(expdest);
4981556Srgrimes			herefd = saveherefd;
4991556Srgrimes			argbackq = saveargbackq;
5001556Srgrimes			startp = stackblock() + startloc;
5011556Srgrimes			if (subtype == VSASSIGN) {
5021556Srgrimes				setvar(var, startp, 0);
5031556Srgrimes				STADJUST(startp - expdest, expdest);
5041556Srgrimes				varflags &=~ VSNUL;
5051556Srgrimes				goto again;
5061556Srgrimes			}
5071556Srgrimes			/* subtype == VSQUESTION */
5081556Srgrimes			if (*p != CTLENDVAR) {
5091556Srgrimes				outfmt(&errout, "%s\n", startp);
5101556Srgrimes				error((char *)NULL);
5111556Srgrimes			}
5121556Srgrimes			error("%.*s: parameter %snot set", p - var - 1,
5131556Srgrimes				var, (varflags & VSNUL)? "null or " : nullstr);
5141556Srgrimes		}
5151556Srgrimes	}
5161556Srgrimes	if (subtype != VSNORMAL) {	/* skip to end of alternative */
5171556Srgrimes		int nesting = 1;
5181556Srgrimes		for (;;) {
5191556Srgrimes			if ((c = *p++) == CTLESC)
5201556Srgrimes				p++;
5211556Srgrimes			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
5221556Srgrimes				if (set)
5231556Srgrimes					argbackq = argbackq->next;
5241556Srgrimes			} else if (c == CTLVAR) {
5251556Srgrimes				if ((*p++ & VSTYPE) != VSNORMAL)
5261556Srgrimes					nesting++;
5271556Srgrimes			} else if (c == CTLENDVAR) {
5281556Srgrimes				if (--nesting == 0)
5291556Srgrimes					break;
5301556Srgrimes			}
5311556Srgrimes		}
5321556Srgrimes	}
5331556Srgrimes	return p;
5341556Srgrimes}
5351556Srgrimes
5361556Srgrimes
5371556Srgrimes
5381556Srgrimes/*
5391556Srgrimes * Test whether a specialized variable is set.
5401556Srgrimes */
5411556Srgrimes
5421556SrgrimesSTATIC int
5431556Srgrimesvarisset(name)
5441556Srgrimes	char name;
5451556Srgrimes	{
5461556Srgrimes	char **ap;
5471556Srgrimes
5481556Srgrimes	if (name == '!') {
5491556Srgrimes		if (backgndpid == -1)
5501556Srgrimes			return 0;
5511556Srgrimes	} else if (name == '@' || name == '*') {
5521556Srgrimes		if (*shellparam.p == NULL)
5531556Srgrimes			return 0;
5541556Srgrimes	} else if ((unsigned)(name -= '1') <= '9' - '1') {
5551556Srgrimes		ap = shellparam.p;
5561556Srgrimes		do {
5571556Srgrimes			if (*ap++ == NULL)
5581556Srgrimes				return 0;
5591556Srgrimes		} while (--name >= 0);
5601556Srgrimes	}
5611556Srgrimes	return 1;
5621556Srgrimes}
5631556Srgrimes
5641556Srgrimes
5651556Srgrimes
5661556Srgrimes/*
5671556Srgrimes * Add the value of a specialized variable to the stack string.
5681556Srgrimes */
5691556Srgrimes
5701556SrgrimesSTATIC void
5711556Srgrimesvarvalue(name, quoted, allow_split)
5721556Srgrimes	char name;
5731556Srgrimes	{
5741556Srgrimes	int num;
5751556Srgrimes	char temp[32];
5761556Srgrimes	char *p;
5771556Srgrimes	int i;
5781556Srgrimes	extern int exitstatus;
5791556Srgrimes	char sep;
5801556Srgrimes	char **ap;
5811556Srgrimes	char const *syntax;
5821556Srgrimes
5831556Srgrimes#define STRTODEST(p) \
5841556Srgrimes	do {\
5851556Srgrimes	if (allow_split) { \
5861556Srgrimes		syntax = quoted? DQSYNTAX : BASESYNTAX; \
5871556Srgrimes		while (*p) { \
5881556Srgrimes			if (syntax[*p] == CCTL) \
5891556Srgrimes				STPUTC(CTLESC, expdest); \
5901556Srgrimes			STPUTC(*p++, expdest); \
5911556Srgrimes		} \
5921556Srgrimes	} else \
5931556Srgrimes		while (*p) \
5941556Srgrimes			STPUTC(*p++, expdest); \
5951556Srgrimes	} while (0)
5961556Srgrimes
5971556Srgrimes
5981556Srgrimes	switch (name) {
5991556Srgrimes	case '$':
6001556Srgrimes		num = rootpid;
6011556Srgrimes		goto numvar;
6021556Srgrimes	case '?':
6031556Srgrimes		num = exitstatus;
6041556Srgrimes		goto numvar;
6051556Srgrimes	case '#':
6061556Srgrimes		num = shellparam.nparam;
6071556Srgrimes		goto numvar;
6081556Srgrimes	case '!':
6091556Srgrimes		num = backgndpid;
6101556Srgrimesnumvar:
6111556Srgrimes		p = temp + 31;
6121556Srgrimes		temp[31] = '\0';
6131556Srgrimes		do {
6141556Srgrimes			*--p = num % 10 + '0';
6151556Srgrimes		} while ((num /= 10) != 0);
6161556Srgrimes		while (*p)
6171556Srgrimes			STPUTC(*p++, expdest);
6181556Srgrimes		break;
6191556Srgrimes	case '-':
6201556Srgrimes		for (i = 0 ; i < NOPTS ; i++) {
6211556Srgrimes			if (optlist[i].val)
6221556Srgrimes				STPUTC(optlist[i].letter, expdest);
6231556Srgrimes		}
6241556Srgrimes		break;
6251556Srgrimes	case '@':
6261556Srgrimes		if (allow_split) {
6271556Srgrimes			sep = '\0';
6281556Srgrimes			goto allargs;
6291556Srgrimes		}
6308855Srgrimes		/* fall through */
6311556Srgrimes	case '*':
6321556Srgrimes		sep = ' ';
6331556Srgrimesallargs:
6341556Srgrimes		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
6351556Srgrimes			STRTODEST(p);
6361556Srgrimes			if (*ap)
6371556Srgrimes				STPUTC(sep, expdest);
6381556Srgrimes		}
6391556Srgrimes		break;
6401556Srgrimes	case '0':
6411556Srgrimes		p = arg0;
6421556Srgrimes		STRTODEST(p);
6431556Srgrimes		break;
6441556Srgrimes	default:
6451556Srgrimes		if ((unsigned)(name -= '1') <= '9' - '1') {
6461556Srgrimes			p = shellparam.p[name];
6471556Srgrimes			STRTODEST(p);
6481556Srgrimes		}
6491556Srgrimes		break;
6501556Srgrimes	}
6511556Srgrimes}
6521556Srgrimes
6531556Srgrimes
6541556Srgrimes
6551556Srgrimes/*
6561556Srgrimes * Record the the fact that we have to scan this region of the
6571556Srgrimes * string for IFS characters.
6581556Srgrimes */
6591556Srgrimes
6601556SrgrimesSTATIC void
6611556Srgrimesrecordregion(start, end, nulonly) {
6621556Srgrimes	register struct ifsregion *ifsp;
6631556Srgrimes
6641556Srgrimes	if (ifslastp == NULL) {
6651556Srgrimes		ifsp = &ifsfirst;
6661556Srgrimes	} else {
6671556Srgrimes		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
6681556Srgrimes		ifslastp->next = ifsp;
6691556Srgrimes	}
6701556Srgrimes	ifslastp = ifsp;
6711556Srgrimes	ifslastp->next = NULL;
6721556Srgrimes	ifslastp->begoff = start;
6731556Srgrimes	ifslastp->endoff = end;
6741556Srgrimes	ifslastp->nulonly = nulonly;
6751556Srgrimes}
6761556Srgrimes
6771556Srgrimes
6781556Srgrimes
6791556Srgrimes/*
6801556Srgrimes * Break the argument string into pieces based upon IFS and add the
6811556Srgrimes * strings to the argument list.  The regions of the string to be
6821556Srgrimes * searched for IFS characters have been stored by recordregion.
6831556Srgrimes */
6841556Srgrimes
6851556SrgrimesSTATIC void
6861556Srgrimesifsbreakup(string, arglist)
6871556Srgrimes	char *string;
6881556Srgrimes	struct arglist *arglist;
6891556Srgrimes	{
6901556Srgrimes	struct ifsregion *ifsp;
6911556Srgrimes	struct strlist *sp;
6921556Srgrimes	char *start;
6931556Srgrimes	register char *p;
6941556Srgrimes	char *q;
6951556Srgrimes	char *ifs;
6961556Srgrimes
6971556Srgrimes	start = string;
6981556Srgrimes	if (ifslastp != NULL) {
6991556Srgrimes		ifsp = &ifsfirst;
7001556Srgrimes		do {
7011556Srgrimes			p = string + ifsp->begoff;
7021556Srgrimes			ifs = ifsp->nulonly? nullstr : ifsval();
7031556Srgrimes			while (p < string + ifsp->endoff) {
7041556Srgrimes				q = p;
7051556Srgrimes				if (*p == CTLESC)
7061556Srgrimes					p++;
7071556Srgrimes				if (strchr(ifs, *p++)) {
7081556Srgrimes					if (q > start || *ifs != ' ') {
7091556Srgrimes						*q = '\0';
7101556Srgrimes						sp = (struct strlist *)stalloc(sizeof *sp);
7111556Srgrimes						sp->text = start;
7121556Srgrimes						*arglist->lastp = sp;
7131556Srgrimes						arglist->lastp = &sp->next;
7141556Srgrimes					}
7151556Srgrimes					if (*ifs == ' ') {
7161556Srgrimes						for (;;) {
7171556Srgrimes							if (p >= string + ifsp->endoff)
7181556Srgrimes								break;
7191556Srgrimes							q = p;
7201556Srgrimes							if (*p == CTLESC)
7211556Srgrimes								p++;
7221556Srgrimes							if (strchr(ifs, *p++) == NULL) {
7231556Srgrimes								p = q;
7241556Srgrimes								break;
7251556Srgrimes							}
7261556Srgrimes						}
7271556Srgrimes					}
7281556Srgrimes					start = p;
7291556Srgrimes				}
7301556Srgrimes			}
7311556Srgrimes		} while ((ifsp = ifsp->next) != NULL);
7321556Srgrimes		if (*start || (*ifs != ' ' && start > string)) {
7331556Srgrimes			sp = (struct strlist *)stalloc(sizeof *sp);
7341556Srgrimes			sp->text = start;
7351556Srgrimes			*arglist->lastp = sp;
7361556Srgrimes			arglist->lastp = &sp->next;
7371556Srgrimes		}
7381556Srgrimes	} else {
7391556Srgrimes		sp = (struct strlist *)stalloc(sizeof *sp);
7401556Srgrimes		sp->text = start;
7411556Srgrimes		*arglist->lastp = sp;
7421556Srgrimes		arglist->lastp = &sp->next;
7431556Srgrimes	}
7441556Srgrimes}
7451556Srgrimes
7461556Srgrimes
7471556Srgrimes
7481556Srgrimes/*
7491556Srgrimes * Expand shell metacharacters.  At this point, the only control characters
7501556Srgrimes * should be escapes.  The results are stored in the list exparg.
7511556Srgrimes */
7521556Srgrimes
7531556Srgrimeschar *expdir;
7541556Srgrimes
7551556Srgrimes
7561556SrgrimesSTATIC void
7571556Srgrimesexpandmeta(str, flag)
7581556Srgrimes	struct strlist *str;
7591556Srgrimes	{
7601556Srgrimes	char *p;
7611556Srgrimes	struct strlist **savelastp;
7621556Srgrimes	struct strlist *sp;
7631556Srgrimes	char c;
7641556Srgrimes	/* TODO - EXP_REDIR */
7651556Srgrimes
7661556Srgrimes	while (str) {
7671556Srgrimes		if (fflag)
7681556Srgrimes			goto nometa;
7691556Srgrimes		p = str->text;
7701556Srgrimes		for (;;) {			/* fast check for meta chars */
7711556Srgrimes			if ((c = *p++) == '\0')
7721556Srgrimes				goto nometa;
7731556Srgrimes			if (c == '*' || c == '?' || c == '[' || c == '!')
7741556Srgrimes				break;
7751556Srgrimes		}
7761556Srgrimes		savelastp = exparg.lastp;
7771556Srgrimes		INTOFF;
7781556Srgrimes		if (expdir == NULL) {
7791556Srgrimes			int i = strlen(str->text);
7801556Srgrimes			expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
7811556Srgrimes		}
7821556Srgrimes
7831556Srgrimes		expmeta(expdir, str->text);
7841556Srgrimes		ckfree(expdir);
7851556Srgrimes		expdir = NULL;
7861556Srgrimes		INTON;
7871556Srgrimes		if (exparg.lastp == savelastp) {
7888855Srgrimes			/*
7898855Srgrimes			 * no matches
7901556Srgrimes			 */
7911556Srgrimesnometa:
7921556Srgrimes			*exparg.lastp = str;
7931556Srgrimes			rmescapes(str->text);
7941556Srgrimes			exparg.lastp = &str->next;
7951556Srgrimes		} else {
7961556Srgrimes			*exparg.lastp = NULL;
7971556Srgrimes			*savelastp = sp = expsort(*savelastp);
7981556Srgrimes			while (sp->next != NULL)
7991556Srgrimes				sp = sp->next;
8001556Srgrimes			exparg.lastp = &sp->next;
8011556Srgrimes		}
8021556Srgrimes		str = str->next;
8031556Srgrimes	}
8041556Srgrimes}
8051556Srgrimes
8061556Srgrimes
8071556Srgrimes/*
8081556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion.
8091556Srgrimes */
8101556Srgrimes
8111556SrgrimesSTATIC void
8121556Srgrimesexpmeta(enddir, name)
8131556Srgrimes	char *enddir;
8141556Srgrimes	char *name;
8151556Srgrimes	{
8161556Srgrimes	register char *p;
8171556Srgrimes	char *q;
8181556Srgrimes	char *start;
8191556Srgrimes	char *endname;
8201556Srgrimes	int metaflag;
8211556Srgrimes	struct stat statb;
8221556Srgrimes	DIR *dirp;
8231556Srgrimes	struct dirent *dp;
8241556Srgrimes	int atend;
8251556Srgrimes	int matchdot;
8261556Srgrimes
8271556Srgrimes	metaflag = 0;
8281556Srgrimes	start = name;
8291556Srgrimes	for (p = name ; ; p++) {
8301556Srgrimes		if (*p == '*' || *p == '?')
8311556Srgrimes			metaflag = 1;
8321556Srgrimes		else if (*p == '[') {
8331556Srgrimes			q = p + 1;
8341556Srgrimes			if (*q == '!')
8351556Srgrimes				q++;
8361556Srgrimes			for (;;) {
8371556Srgrimes				if (*q == CTLESC)
8381556Srgrimes					q++;
8391556Srgrimes				if (*q == '/' || *q == '\0')
8401556Srgrimes					break;
8411556Srgrimes				if (*++q == ']') {
8421556Srgrimes					metaflag = 1;
8431556Srgrimes					break;
8441556Srgrimes				}
8451556Srgrimes			}
8461556Srgrimes		} else if (*p == '!' && p[1] == '!'	&& (p == name || p[-1] == '/')) {
8471556Srgrimes			metaflag = 1;
8481556Srgrimes		} else if (*p == '\0')
8491556Srgrimes			break;
8501556Srgrimes		else if (*p == CTLESC)
8511556Srgrimes			p++;
8521556Srgrimes		if (*p == '/') {
8531556Srgrimes			if (metaflag)
8541556Srgrimes				break;
8551556Srgrimes			start = p + 1;
8561556Srgrimes		}
8571556Srgrimes	}
8581556Srgrimes	if (metaflag == 0) {	/* we've reached the end of the file name */
8591556Srgrimes		if (enddir != expdir)
8601556Srgrimes			metaflag++;
8611556Srgrimes		for (p = name ; ; p++) {
8621556Srgrimes			if (*p == CTLESC)
8631556Srgrimes				p++;
8641556Srgrimes			*enddir++ = *p;
8651556Srgrimes			if (*p == '\0')
8661556Srgrimes				break;
8671556Srgrimes		}
8681556Srgrimes		if (metaflag == 0 || stat(expdir, &statb) >= 0)
8691556Srgrimes			addfname(expdir);
8701556Srgrimes		return;
8711556Srgrimes	}
8721556Srgrimes	endname = p;
8731556Srgrimes	if (start != name) {
8741556Srgrimes		p = name;
8751556Srgrimes		while (p < start) {
8761556Srgrimes			if (*p == CTLESC)
8771556Srgrimes				p++;
8781556Srgrimes			*enddir++ = *p++;
8791556Srgrimes		}
8801556Srgrimes	}
8811556Srgrimes	if (enddir == expdir) {
8821556Srgrimes		p = ".";
8831556Srgrimes	} else if (enddir == expdir + 1 && *expdir == '/') {
8841556Srgrimes		p = "/";
8851556Srgrimes	} else {
8861556Srgrimes		p = expdir;
8871556Srgrimes		enddir[-1] = '\0';
8881556Srgrimes	}
8891556Srgrimes	if ((dirp = opendir(p)) == NULL)
8901556Srgrimes		return;
8911556Srgrimes	if (enddir != expdir)
8921556Srgrimes		enddir[-1] = '/';
8931556Srgrimes	if (*endname == 0) {
8941556Srgrimes		atend = 1;
8951556Srgrimes	} else {
8961556Srgrimes		atend = 0;
8971556Srgrimes		*endname++ = '\0';
8981556Srgrimes	}
8991556Srgrimes	matchdot = 0;
9001556Srgrimes	if (start[0] == '.' || start[0] == CTLESC && start[1] == '.')
9011556Srgrimes		matchdot++;
9021556Srgrimes	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
9031556Srgrimes		if (dp->d_name[0] == '.' && ! matchdot)
9041556Srgrimes			continue;
9051556Srgrimes		if (patmatch(start, dp->d_name)) {
9061556Srgrimes			if (atend) {
9071556Srgrimes				scopy(dp->d_name, enddir);
9081556Srgrimes				addfname(expdir);
9091556Srgrimes			} else {
9101556Srgrimes				char *q;
9111556Srgrimes				for (p = enddir, q = dp->d_name ; *p++ = *q++ ;);
9121556Srgrimes				p[-1] = '/';
9131556Srgrimes				expmeta(p, endname);
9141556Srgrimes			}
9151556Srgrimes		}
9161556Srgrimes	}
9171556Srgrimes	closedir(dirp);
9181556Srgrimes	if (! atend)
9191556Srgrimes		endname[-1] = '/';
9201556Srgrimes}
9211556Srgrimes
9221556Srgrimes
9231556Srgrimes/*
9241556Srgrimes * Add a file name to the list.
9251556Srgrimes */
9261556Srgrimes
9271556SrgrimesSTATIC void
9281556Srgrimesaddfname(name)
9291556Srgrimes	char *name;
9301556Srgrimes	{
9311556Srgrimes	char *p;
9321556Srgrimes	struct strlist *sp;
9331556Srgrimes
9341556Srgrimes	p = stalloc(strlen(name) + 1);
9351556Srgrimes	scopy(name, p);
9361556Srgrimes	sp = (struct strlist *)stalloc(sizeof *sp);
9371556Srgrimes	sp->text = p;
9381556Srgrimes	*exparg.lastp = sp;
9391556Srgrimes	exparg.lastp = &sp->next;
9401556Srgrimes}
9411556Srgrimes
9421556Srgrimes
9431556Srgrimes/*
9441556Srgrimes * Sort the results of file name expansion.  It calculates the number of
9451556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the
9461556Srgrimes * work.
9471556Srgrimes */
9481556Srgrimes
9491556SrgrimesSTATIC struct strlist *
9501556Srgrimesexpsort(str)
9511556Srgrimes	struct strlist *str;
9521556Srgrimes	{
9531556Srgrimes	int len;
9541556Srgrimes	struct strlist *sp;
9551556Srgrimes
9561556Srgrimes	len = 0;
9571556Srgrimes	for (sp = str ; sp ; sp = sp->next)
9581556Srgrimes		len++;
9591556Srgrimes	return msort(str, len);
9601556Srgrimes}
9611556Srgrimes
9621556Srgrimes
9631556SrgrimesSTATIC struct strlist *
9641556Srgrimesmsort(list, len)
9651556Srgrimes	struct strlist *list;
9661556Srgrimes	{
9671556Srgrimes	struct strlist *p, *q;
9681556Srgrimes	struct strlist **lpp;
9691556Srgrimes	int half;
9701556Srgrimes	int n;
9711556Srgrimes
9721556Srgrimes	if (len <= 1)
9731556Srgrimes		return list;
9748855Srgrimes	half = len >> 1;
9751556Srgrimes	p = list;
9761556Srgrimes	for (n = half ; --n >= 0 ; ) {
9771556Srgrimes		q = p;
9781556Srgrimes		p = p->next;
9791556Srgrimes	}
9801556Srgrimes	q->next = NULL;			/* terminate first half of list */
9811556Srgrimes	q = msort(list, half);		/* sort first half of list */
9821556Srgrimes	p = msort(p, len - half);		/* sort second half */
9831556Srgrimes	lpp = &list;
9841556Srgrimes	for (;;) {
9851556Srgrimes		if (strcmp(p->text, q->text) < 0) {
9861556Srgrimes			*lpp = p;
9871556Srgrimes			lpp = &p->next;
9881556Srgrimes			if ((p = *lpp) == NULL) {
9891556Srgrimes				*lpp = q;
9901556Srgrimes				break;
9911556Srgrimes			}
9921556Srgrimes		} else {
9931556Srgrimes			*lpp = q;
9941556Srgrimes			lpp = &q->next;
9951556Srgrimes			if ((q = *lpp) == NULL) {
9961556Srgrimes				*lpp = p;
9971556Srgrimes				break;
9981556Srgrimes			}
9991556Srgrimes		}
10001556Srgrimes	}
10011556Srgrimes	return list;
10021556Srgrimes}
10031556Srgrimes
10041556Srgrimes
10051556Srgrimes
10061556Srgrimes/*
10071556Srgrimes * Returns true if the pattern matches the string.
10081556Srgrimes */
10091556Srgrimes
10101556Srgrimesint
10111556Srgrimespatmatch(pattern, string)
10121556Srgrimes	char *pattern;
10131556Srgrimes	char *string;
10141556Srgrimes	{
10151556Srgrimes#ifdef notdef
10161556Srgrimes	if (pattern[0] == '!' && pattern[1] == '!')
10171556Srgrimes		return 1 - pmatch(pattern + 2, string);
10181556Srgrimes	else
10191556Srgrimes#endif
10201556Srgrimes		return pmatch(pattern, string);
10211556Srgrimes}
10221556Srgrimes
102317525SacheSTATIC int
102417525Sachecollcmp (c1, c2)
102517525Sacheint c1, c2;
102617525Sache{
102717525Sache	static char s1[2], s2[2];
10281556Srgrimes
102917525Sache	c1 &= 0xFF;
103017525Sache	c2 &= 0xFF;
103117525Sache	if (c1 == c2)
103217525Sache		return (0);
103317525Sache	if (   (isascii(c1) && isascii(c2))
103417525Sache	    || (!isalpha(c1) && !isalpha(c2))
103517525Sache	   )
103617525Sache		return (c1 - c2);
103717525Sache	if (isalpha(c1) && !isalpha(c2)) {
103817525Sache		if (isupper(c1))
103917525Sache			return ('A' - c2);
104017525Sache		else
104117525Sache			return ('a' - c2);
104217525Sache	} else if (isalpha(c2) && !isalpha(c1)) {
104317525Sache		if (isupper(c2))
104417525Sache			return (c1 - 'A');
104517525Sache		else
104617525Sache			return (c1 - 'a');
104717525Sache	}
104817525Sache	if (isupper(c1) && islower(c2))
104917525Sache		return (-1);
105017525Sache	else if (islower(c1) && isupper(c2))
105117525Sache		return (1);
105217525Sache	s1[0] = c1;
105317525Sache	s2[0] = c2;
105417525Sache	return strcoll(s1, s2);
105517525Sache}
105617525Sache
105717525Sache
10581556SrgrimesSTATIC int
10591556Srgrimespmatch(pattern, string)
10601556Srgrimes	char *pattern;
10611556Srgrimes	char *string;
10621556Srgrimes	{
10631556Srgrimes	register char *p, *q;
10641556Srgrimes	register char c;
10651556Srgrimes
10661556Srgrimes	p = pattern;
10671556Srgrimes	q = string;
10681556Srgrimes	for (;;) {
10691556Srgrimes		switch (c = *p++) {
10701556Srgrimes		case '\0':
10711556Srgrimes			goto breakloop;
10721556Srgrimes		case CTLESC:
10731556Srgrimes			if (*q++ != *p++)
10741556Srgrimes				return 0;
10751556Srgrimes			break;
10761556Srgrimes		case '?':
10771556Srgrimes			if (*q++ == '\0')
10781556Srgrimes				return 0;
10791556Srgrimes			break;
10801556Srgrimes		case '*':
10811556Srgrimes			c = *p;
10821556Srgrimes			if (c != CTLESC && c != '?' && c != '*' && c != '[') {
10831556Srgrimes				while (*q != c) {
10841556Srgrimes					if (*q == '\0')
10851556Srgrimes						return 0;
10861556Srgrimes					q++;
10871556Srgrimes				}
10881556Srgrimes			}
10891556Srgrimes			do {
10901556Srgrimes				if (pmatch(p, q))
10911556Srgrimes					return 1;
10921556Srgrimes			} while (*q++ != '\0');
10931556Srgrimes			return 0;
10941556Srgrimes		case '[': {
10951556Srgrimes			char *endp;
10961556Srgrimes			int invert, found;
10971556Srgrimes			char chr;
10981556Srgrimes
10991556Srgrimes			endp = p;
11001556Srgrimes			if (*endp == '!')
11011556Srgrimes				endp++;
11021556Srgrimes			for (;;) {
11031556Srgrimes				if (*endp == '\0')
11041556Srgrimes					goto dft;		/* no matching ] */
11051556Srgrimes				if (*endp == CTLESC)
11061556Srgrimes					endp++;
11071556Srgrimes				if (*++endp == ']')
11081556Srgrimes					break;
11091556Srgrimes			}
11101556Srgrimes			invert = 0;
11111556Srgrimes			if (*p == '!') {
11121556Srgrimes				invert++;
11131556Srgrimes				p++;
11141556Srgrimes			}
11151556Srgrimes			found = 0;
11161556Srgrimes			chr = *q++;
11171556Srgrimes			c = *p++;
11181556Srgrimes			do {
11191556Srgrimes				if (c == CTLESC)
11201556Srgrimes					c = *p++;
11211556Srgrimes				if (*p == '-' && p[1] != ']') {
11221556Srgrimes					p++;
11231556Srgrimes					if (*p == CTLESC)
11241556Srgrimes						p++;
112517525Sache					if (   collcmp(chr, c) >= 0
112617525Sache					    && collcmp(chr, *p) <= 0
112717525Sache					   )
11281556Srgrimes						found = 1;
11291556Srgrimes					p++;
11301556Srgrimes				} else {
11311556Srgrimes					if (chr == c)
11321556Srgrimes						found = 1;
11331556Srgrimes				}
11341556Srgrimes			} while ((c = *p++) != ']');
11351556Srgrimes			if (found == invert)
11361556Srgrimes				return 0;
11371556Srgrimes			break;
11381556Srgrimes		}
11391556Srgrimesdft:	        default:
11401556Srgrimes			if (*q++ != c)
11411556Srgrimes				return 0;
11421556Srgrimes			break;
11431556Srgrimes		}
11441556Srgrimes	}
11451556Srgrimesbreakloop:
11461556Srgrimes	if (*q != '\0')
11471556Srgrimes		return 0;
11481556Srgrimes	return 1;
11491556Srgrimes}
11501556Srgrimes
11511556Srgrimes
11521556Srgrimes
11531556Srgrimes/*
11541556Srgrimes * Remove any CTLESC characters from a string.
11551556Srgrimes */
11561556Srgrimes
11571556Srgrimesvoid
11581556Srgrimesrmescapes(str)
11591556Srgrimes	char *str;
11601556Srgrimes	{
11611556Srgrimes	register char *p, *q;
11621556Srgrimes
11631556Srgrimes	p = str;
11641556Srgrimes	while (*p != CTLESC) {
11651556Srgrimes		if (*p++ == '\0')
11661556Srgrimes			return;
11671556Srgrimes	}
11681556Srgrimes	q = p;
11691556Srgrimes	while (*p) {
11701556Srgrimes		if (*p == CTLESC)
11711556Srgrimes			p++;
11721556Srgrimes		*q++ = *p++;
11731556Srgrimes	}
11741556Srgrimes	*q = '\0';
11751556Srgrimes}
11761556Srgrimes
11771556Srgrimes
11781556Srgrimes
11791556Srgrimes/*
11801556Srgrimes * See if a pattern matches in a case statement.
11811556Srgrimes */
11821556Srgrimes
11831556Srgrimesint
11841556Srgrimescasematch(pattern, val)
11851556Srgrimes	union node *pattern;
11861556Srgrimes	char *val;
11871556Srgrimes	{
11881556Srgrimes	struct stackmark smark;
11891556Srgrimes	int result;
11901556Srgrimes	char *p;
11911556Srgrimes
11921556Srgrimes	setstackmark(&smark);
11931556Srgrimes	argbackq = pattern->narg.backquote;
11941556Srgrimes	STARTSTACKSTR(expdest);
11951556Srgrimes	ifslastp = NULL;
11961556Srgrimes	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
11971556Srgrimes	STPUTC('\0', expdest);
11981556Srgrimes	p = grabstackstr(expdest);
11991556Srgrimes	result = patmatch(p, val);
12001556Srgrimes	popstackmark(&smark);
12011556Srgrimes	return result;
12021556Srgrimes}
1203