expand.c revision 223010
1/*-
2 * Copyright (c) 1991, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 1997-2005
5 *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 4. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#ifndef lint
36#if 0
37static char sccsid[] = "@(#)expand.c	8.5 (Berkeley) 5/15/95";
38#endif
39#endif /* not lint */
40#include <sys/cdefs.h>
41__FBSDID("$FreeBSD: head/bin/sh/expand.c 223010 2011-06-12 12:54:52Z jilles $");
42
43#include <sys/types.h>
44#include <sys/time.h>
45#include <sys/stat.h>
46#include <dirent.h>
47#include <errno.h>
48#include <inttypes.h>
49#include <limits.h>
50#include <pwd.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54#include <unistd.h>
55#include <wchar.h>
56
57/*
58 * Routines to expand arguments to commands.  We have to deal with
59 * backquotes, shell variables, and file metacharacters.
60 */
61
62#include "shell.h"
63#include "main.h"
64#include "nodes.h"
65#include "eval.h"
66#include "expand.h"
67#include "syntax.h"
68#include "parser.h"
69#include "jobs.h"
70#include "options.h"
71#include "var.h"
72#include "input.h"
73#include "output.h"
74#include "memalloc.h"
75#include "error.h"
76#include "mystring.h"
77#include "arith.h"
78#include "show.h"
79
80/*
81 * Structure specifying which parts of the string should be searched
82 * for IFS characters.
83 */
84
85struct ifsregion {
86	struct ifsregion *next;	/* next region in list */
87	int begoff;		/* offset of start of region */
88	int endoff;		/* offset of end of region */
89	int inquotes;		/* search for nul bytes only */
90};
91
92
93static char *expdest;			/* output of current string */
94static struct nodelist *argbackq;	/* list of back quote expressions */
95static struct ifsregion ifsfirst;	/* first struct in list of ifs regions */
96static struct ifsregion *ifslastp;	/* last struct in list */
97static struct arglist exparg;		/* holds expanded arg list */
98
99static void argstr(char *, int);
100static char *exptilde(char *, int);
101static void expbackq(union node *, int, int);
102static int subevalvar(char *, char *, int, int, int, int, int);
103static char *evalvar(char *, int);
104static int varisset(char *, int);
105static void varvalue(char *, int, int, int);
106static void recordregion(int, int, int);
107static void removerecordregions(int);
108static void ifsbreakup(char *, struct arglist *);
109static void expandmeta(struct strlist *, int);
110static void expmeta(char *, char *);
111static void addfname(char *);
112static struct strlist *expsort(struct strlist *);
113static struct strlist *msort(struct strlist *, int);
114static char *cvtnum(int, char *);
115static int collate_range_cmp(wchar_t, wchar_t);
116
117static int
118collate_range_cmp(wchar_t c1, wchar_t c2)
119{
120	static wchar_t s1[2], s2[2];
121
122	s1[0] = c1;
123	s2[0] = c2;
124	return (wcscoll(s1, s2));
125}
126
127/*
128 * Expand shell variables and backquotes inside a here document.
129 *	union node *arg		the document
130 *	int fd;			where to write the expanded version
131 */
132
133void
134expandhere(union node *arg, int fd)
135{
136	expandarg(arg, (struct arglist *)NULL, 0);
137	xwrite(fd, stackblock(), expdest - stackblock());
138}
139
140static char *
141stputs_quotes(const char *data, const char *syntax, char *p)
142{
143	while (*data) {
144		CHECKSTRSPACE(2, p);
145		if (syntax[(int)*data] == CCTL)
146			USTPUTC(CTLESC, p);
147		USTPUTC(*data++, p);
148	}
149	return (p);
150}
151#define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p)
152
153/*
154 * Perform expansions on an argument, placing the resulting list of arguments
155 * in arglist.  Parameter expansion, command substitution and arithmetic
156 * expansion are always performed; additional expansions can be requested
157 * via flag (EXP_*).
158 * The result is left in the stack string.
159 * When arglist is NULL, perform here document expansion.
160 *
161 * Caution: this function uses global state and is not reentrant.
162 * However, a new invocation after an interrupted invocation is safe
163 * and will reset the global state for the new call.
164 */
165void
166expandarg(union node *arg, struct arglist *arglist, int flag)
167{
168	struct strlist *sp;
169	char *p;
170
171	argbackq = arg->narg.backquote;
172	STARTSTACKSTR(expdest);
173	ifsfirst.next = NULL;
174	ifslastp = NULL;
175	argstr(arg->narg.text, flag);
176	if (arglist == NULL) {
177		STACKSTRNUL(expdest);
178		return;			/* here document expanded */
179	}
180	STPUTC('\0', expdest);
181	p = grabstackstr(expdest);
182	exparg.lastp = &exparg.list;
183	/*
184	 * TODO - EXP_REDIR
185	 */
186	if (flag & EXP_FULL) {
187		ifsbreakup(p, &exparg);
188		*exparg.lastp = NULL;
189		exparg.lastp = &exparg.list;
190		expandmeta(exparg.list, flag);
191	} else {
192		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
193			rmescapes(p);
194		sp = (struct strlist *)stalloc(sizeof (struct strlist));
195		sp->text = p;
196		*exparg.lastp = sp;
197		exparg.lastp = &sp->next;
198	}
199	while (ifsfirst.next != NULL) {
200		struct ifsregion *ifsp;
201		INTOFF;
202		ifsp = ifsfirst.next->next;
203		ckfree(ifsfirst.next);
204		ifsfirst.next = ifsp;
205		INTON;
206	}
207	*exparg.lastp = NULL;
208	if (exparg.list) {
209		*arglist->lastp = exparg.list;
210		arglist->lastp = exparg.lastp;
211	}
212}
213
214
215
216/*
217 * Perform parameter expansion, command substitution and arithmetic
218 * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE.
219 * Processing ends at a CTLENDVAR character as well as '\0'.
220 * This is used to expand word in ${var+word} etc.
221 * If EXP_FULL, EXP_CASE or EXP_REDIR are set, keep and/or generate CTLESC
222 * characters to allow for further processing.
223 * If EXP_FULL is set, also preserve CTLQUOTEMARK characters.
224 */
225static void
226argstr(char *p, int flag)
227{
228	char c;
229	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);	/* do CTLESC */
230	int firsteq = 1;
231	int split_lit;
232	int lit_quoted;
233
234	split_lit = flag & EXP_SPLIT_LIT;
235	lit_quoted = flag & EXP_LIT_QUOTED;
236	flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED);
237	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
238		p = exptilde(p, flag);
239	for (;;) {
240		CHECKSTRSPACE(2, expdest);
241		switch (c = *p++) {
242		case '\0':
243		case CTLENDVAR:
244			goto breakloop;
245		case CTLQUOTEMARK:
246			lit_quoted = 1;
247			/* "$@" syntax adherence hack */
248			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
249				break;
250			if ((flag & EXP_FULL) != 0)
251				USTPUTC(c, expdest);
252			break;
253		case CTLQUOTEEND:
254			lit_quoted = 0;
255			break;
256		case CTLESC:
257			if (quotes)
258				USTPUTC(c, expdest);
259			c = *p++;
260			USTPUTC(c, expdest);
261			if (split_lit && !lit_quoted)
262				recordregion(expdest - stackblock() -
263				    (quotes ? 2 : 1),
264				    expdest - stackblock(), 0);
265			break;
266		case CTLVAR:
267			p = evalvar(p, flag);
268			break;
269		case CTLBACKQ:
270		case CTLBACKQ|CTLQUOTE:
271			expbackq(argbackq->n, c & CTLQUOTE, flag);
272			argbackq = argbackq->next;
273			break;
274		case CTLENDARI:
275			expari(flag);
276			break;
277		case ':':
278		case '=':
279			/*
280			 * sort of a hack - expand tildes in variable
281			 * assignments (after the first '=' and after ':'s).
282			 */
283			USTPUTC(c, expdest);
284			if (split_lit && !lit_quoted)
285				recordregion(expdest - stackblock() - 1,
286				    expdest - stackblock(), 0);
287			if (flag & EXP_VARTILDE && *p == '~' &&
288			    (c != '=' || firsteq)) {
289				if (c == '=')
290					firsteq = 0;
291				p = exptilde(p, flag);
292			}
293			break;
294		default:
295			USTPUTC(c, expdest);
296			if (split_lit && !lit_quoted)
297				recordregion(expdest - stackblock() - 1,
298				    expdest - stackblock(), 0);
299		}
300	}
301breakloop:;
302}
303
304/*
305 * Perform tilde expansion, placing the result in the stack string and
306 * returning the next position in the input string to process.
307 */
308static char *
309exptilde(char *p, int flag)
310{
311	char c, *startp = p;
312	struct passwd *pw;
313	char *home;
314	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
315
316	while ((c = *p) != '\0') {
317		switch(c) {
318		case CTLESC: /* This means CTL* are always considered quoted. */
319		case CTLVAR:
320		case CTLBACKQ:
321		case CTLBACKQ | CTLQUOTE:
322		case CTLARI:
323		case CTLENDARI:
324		case CTLQUOTEMARK:
325			return (startp);
326		case ':':
327			if (flag & EXP_VARTILDE)
328				goto done;
329			break;
330		case '/':
331		case CTLENDVAR:
332			goto done;
333		}
334		p++;
335	}
336done:
337	*p = '\0';
338	if (*(startp+1) == '\0') {
339		if ((home = lookupvar("HOME")) == NULL)
340			goto lose;
341	} else {
342		if ((pw = getpwnam(startp+1)) == NULL)
343			goto lose;
344		home = pw->pw_dir;
345	}
346	if (*home == '\0')
347		goto lose;
348	*p = c;
349	if (quotes)
350		STPUTS_QUOTES(home, SQSYNTAX, expdest);
351	else
352		STPUTS(home, expdest);
353	return (p);
354lose:
355	*p = c;
356	return (startp);
357}
358
359
360static void
361removerecordregions(int endoff)
362{
363	if (ifslastp == NULL)
364		return;
365
366	if (ifsfirst.endoff > endoff) {
367		while (ifsfirst.next != NULL) {
368			struct ifsregion *ifsp;
369			INTOFF;
370			ifsp = ifsfirst.next->next;
371			ckfree(ifsfirst.next);
372			ifsfirst.next = ifsp;
373			INTON;
374		}
375		if (ifsfirst.begoff > endoff)
376			ifslastp = NULL;
377		else {
378			ifslastp = &ifsfirst;
379			ifsfirst.endoff = endoff;
380		}
381		return;
382	}
383
384	ifslastp = &ifsfirst;
385	while (ifslastp->next && ifslastp->next->begoff < endoff)
386		ifslastp=ifslastp->next;
387	while (ifslastp->next != NULL) {
388		struct ifsregion *ifsp;
389		INTOFF;
390		ifsp = ifslastp->next->next;
391		ckfree(ifslastp->next);
392		ifslastp->next = ifsp;
393		INTON;
394	}
395	if (ifslastp->endoff > endoff)
396		ifslastp->endoff = endoff;
397}
398
399/*
400 * Expand arithmetic expression.  Backup to start of expression,
401 * evaluate, place result in (backed up) result, adjust string position.
402 */
403void
404expari(int flag)
405{
406	char *p, *q, *start;
407	arith_t result;
408	int begoff;
409	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
410	int quoted;
411
412	/*
413	 * This routine is slightly over-complicated for
414	 * efficiency.  First we make sure there is
415	 * enough space for the result, which may be bigger
416	 * than the expression.  Next we
417	 * scan backwards looking for the start of arithmetic.  If the
418	 * next previous character is a CTLESC character, then we
419	 * have to rescan starting from the beginning since CTLESC
420	 * characters have to be processed left to right.
421	 */
422	CHECKSTRSPACE(DIGITS(result) - 2, expdest);
423	USTPUTC('\0', expdest);
424	start = stackblock();
425	p = expdest - 2;
426	while (p >= start && *p != CTLARI)
427		--p;
428	if (p < start || *p != CTLARI)
429		error("missing CTLARI (shouldn't happen)");
430	if (p > start && *(p - 1) == CTLESC)
431		for (p = start; *p != CTLARI; p++)
432			if (*p == CTLESC)
433				p++;
434
435	if (p[1] == '"')
436		quoted=1;
437	else
438		quoted=0;
439	begoff = p - start;
440	removerecordregions(begoff);
441	if (quotes)
442		rmescapes(p+2);
443	q = grabstackstr(expdest);
444	result = arith(p+2);
445	ungrabstackstr(q, expdest);
446	fmtstr(p, DIGITS(result), ARITH_FORMAT_STR, result);
447	while (*p++)
448		;
449	if (quoted == 0)
450		recordregion(begoff, p - 1 - start, 0);
451	result = expdest - p + 1;
452	STADJUST(-result, expdest);
453}
454
455
456/*
457 * Perform command substitution.
458 */
459static void
460expbackq(union node *cmd, int quoted, int flag)
461{
462	struct backcmd in;
463	int i;
464	char buf[128];
465	char *p;
466	char *dest = expdest;
467	struct ifsregion saveifs, *savelastp;
468	struct nodelist *saveargbackq;
469	char lastc;
470	int startloc = dest - stackblock();
471	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
472	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
473	int nnl;
474
475	INTOFF;
476	saveifs = ifsfirst;
477	savelastp = ifslastp;
478	saveargbackq = argbackq;
479	p = grabstackstr(dest);
480	evalbackcmd(cmd, &in);
481	ungrabstackstr(p, dest);
482	ifsfirst = saveifs;
483	ifslastp = savelastp;
484	argbackq = saveargbackq;
485
486	p = in.buf;
487	lastc = '\0';
488	nnl = 0;
489	/* Don't copy trailing newlines */
490	for (;;) {
491		if (--in.nleft < 0) {
492			if (in.fd < 0)
493				break;
494			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
495			TRACE(("expbackq: read returns %d\n", i));
496			if (i <= 0)
497				break;
498			p = buf;
499			in.nleft = i - 1;
500		}
501		lastc = *p++;
502		if (lastc != '\0') {
503			if (lastc == '\n') {
504				nnl++;
505			} else {
506				CHECKSTRSPACE(nnl + 2, dest);
507				while (nnl > 0) {
508					nnl--;
509					USTPUTC('\n', dest);
510				}
511				if (quotes && syntax[(int)lastc] == CCTL)
512					USTPUTC(CTLESC, dest);
513				USTPUTC(lastc, dest);
514			}
515		}
516	}
517
518	if (in.fd >= 0)
519		close(in.fd);
520	if (in.buf)
521		ckfree(in.buf);
522	if (in.jp)
523		exitstatus = waitforjob(in.jp, (int *)NULL);
524	if (quoted == 0)
525		recordregion(startloc, dest - stackblock(), 0);
526	TRACE(("expbackq: size=%td: \"%.*s\"\n",
527		((dest - stackblock()) - startloc),
528		(int)((dest - stackblock()) - startloc),
529		stackblock() + startloc));
530	expdest = dest;
531	INTON;
532}
533
534
535
536static int
537subevalvar(char *p, char *str, int strloc, int subtype, int startloc,
538  int varflags, int quotes)
539{
540	char *startp;
541	char *loc = NULL;
542	char *q;
543	int c = 0;
544	struct nodelist *saveargbackq = argbackq;
545	int amount;
546
547	argstr(p, (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX ||
548	    subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX ?
549	    EXP_CASE : 0) | EXP_TILDE);
550	STACKSTRNUL(expdest);
551	argbackq = saveargbackq;
552	startp = stackblock() + startloc;
553	if (str == NULL)
554	    str = stackblock() + strloc;
555
556	switch (subtype) {
557	case VSASSIGN:
558		setvar(str, startp, 0);
559		amount = startp - expdest;
560		STADJUST(amount, expdest);
561		varflags &= ~VSNUL;
562		return 1;
563
564	case VSQUESTION:
565		if (*p != CTLENDVAR) {
566			outfmt(out2, "%s\n", startp);
567			error((char *)NULL);
568		}
569		error("%.*s: parameter %snot set", (int)(p - str - 1),
570		      str, (varflags & VSNUL) ? "null or "
571					      : nullstr);
572		return 0;
573
574	case VSTRIMLEFT:
575		for (loc = startp; loc < str; loc++) {
576			c = *loc;
577			*loc = '\0';
578			if (patmatch(str, startp, quotes)) {
579				*loc = c;
580				goto recordleft;
581			}
582			*loc = c;
583			if (quotes && *loc == CTLESC)
584				loc++;
585		}
586		return 0;
587
588	case VSTRIMLEFTMAX:
589		for (loc = str - 1; loc >= startp;) {
590			c = *loc;
591			*loc = '\0';
592			if (patmatch(str, startp, quotes)) {
593				*loc = c;
594				goto recordleft;
595			}
596			*loc = c;
597			loc--;
598			if (quotes && loc > startp && *(loc - 1) == CTLESC) {
599				for (q = startp; q < loc; q++)
600					if (*q == CTLESC)
601						q++;
602				if (q > loc)
603					loc--;
604			}
605		}
606		return 0;
607
608	case VSTRIMRIGHT:
609		for (loc = str - 1; loc >= startp;) {
610			if (patmatch(str, loc, quotes)) {
611				amount = loc - expdest;
612				STADJUST(amount, expdest);
613				return 1;
614			}
615			loc--;
616			if (quotes && loc > startp && *(loc - 1) == CTLESC) {
617				for (q = startp; q < loc; q++)
618					if (*q == CTLESC)
619						q++;
620				if (q > loc)
621					loc--;
622			}
623		}
624		return 0;
625
626	case VSTRIMRIGHTMAX:
627		for (loc = startp; loc < str - 1; loc++) {
628			if (patmatch(str, loc, quotes)) {
629				amount = loc - expdest;
630				STADJUST(amount, expdest);
631				return 1;
632			}
633			if (quotes && *loc == CTLESC)
634				loc++;
635		}
636		return 0;
637
638
639	default:
640		abort();
641	}
642
643recordleft:
644	amount = ((str - 1) - (loc - startp)) - expdest;
645	STADJUST(amount, expdest);
646	while (loc != str - 1)
647		*startp++ = *loc++;
648	return 1;
649}
650
651
652/*
653 * Expand a variable, and return a pointer to the next character in the
654 * input string.
655 */
656
657static char *
658evalvar(char *p, int flag)
659{
660	int subtype;
661	int varflags;
662	char *var;
663	char *val;
664	int patloc;
665	int c;
666	int set;
667	int special;
668	int startloc;
669	int varlen;
670	int varlenb;
671	int easy;
672	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
673
674	varflags = (unsigned char)*p++;
675	subtype = varflags & VSTYPE;
676	var = p;
677	special = 0;
678	if (! is_name(*p))
679		special = 1;
680	p = strchr(p, '=') + 1;
681again: /* jump here after setting a variable with ${var=text} */
682	if (varflags & VSLINENO) {
683		set = 1;
684		special = 0;
685		val = var;
686		p[-1] = '\0';	/* temporarily overwrite '=' to have \0
687				   terminated string */
688	} else if (special) {
689		set = varisset(var, varflags & VSNUL);
690		val = NULL;
691	} else {
692		val = bltinlookup(var, 1);
693		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
694			val = NULL;
695			set = 0;
696		} else
697			set = 1;
698	}
699	varlen = 0;
700	startloc = expdest - stackblock();
701	if (!set && uflag && *var != '@' && *var != '*') {
702		switch (subtype) {
703		case VSNORMAL:
704		case VSTRIMLEFT:
705		case VSTRIMLEFTMAX:
706		case VSTRIMRIGHT:
707		case VSTRIMRIGHTMAX:
708		case VSLENGTH:
709			error("%.*s: parameter not set", (int)(p - var - 1),
710			    var);
711		}
712	}
713	if (set && subtype != VSPLUS) {
714		/* insert the value of the variable */
715		if (special) {
716			varvalue(var, varflags & VSQUOTE, subtype, flag);
717			if (subtype == VSLENGTH) {
718				varlenb = expdest - stackblock() - startloc;
719				varlen = varlenb;
720				if (localeisutf8) {
721					val = stackblock() + startloc;
722					for (;val != expdest; val++)
723						if ((*val & 0xC0) == 0x80)
724							varlen--;
725				}
726				STADJUST(-varlenb, expdest);
727			}
728		} else {
729			char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
730								  : BASESYNTAX;
731
732			if (subtype == VSLENGTH) {
733				for (;*val; val++)
734					if (!localeisutf8 ||
735					    (*val & 0xC0) != 0x80)
736						varlen++;
737			}
738			else {
739				if (quotes)
740					STPUTS_QUOTES(val, syntax, expdest);
741				else
742					STPUTS(val, expdest);
743
744			}
745		}
746	}
747
748	if (subtype == VSPLUS)
749		set = ! set;
750
751	easy = ((varflags & VSQUOTE) == 0 ||
752		(*var == '@' && shellparam.nparam != 1));
753
754
755	switch (subtype) {
756	case VSLENGTH:
757		expdest = cvtnum(varlen, expdest);
758		goto record;
759
760	case VSNORMAL:
761		if (!easy)
762			break;
763record:
764		recordregion(startloc, expdest - stackblock(),
765		    varflags & VSQUOTE || (ifsset() && ifsval()[0] == '\0' &&
766		    (*var == '@' || *var == '*')));
767		break;
768
769	case VSPLUS:
770	case VSMINUS:
771		if (!set) {
772			argstr(p, flag | (flag & EXP_FULL ? EXP_SPLIT_LIT : 0) |
773			    (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0));
774			break;
775		}
776		if (easy)
777			goto record;
778		break;
779
780	case VSTRIMLEFT:
781	case VSTRIMLEFTMAX:
782	case VSTRIMRIGHT:
783	case VSTRIMRIGHTMAX:
784		if (!set)
785			break;
786		/*
787		 * Terminate the string and start recording the pattern
788		 * right after it
789		 */
790		STPUTC('\0', expdest);
791		patloc = expdest - stackblock();
792		if (subevalvar(p, NULL, patloc, subtype,
793		    startloc, varflags, quotes) == 0) {
794			int amount = (expdest - stackblock() - patloc) + 1;
795			STADJUST(-amount, expdest);
796		}
797		/* Remove any recorded regions beyond start of variable */
798		removerecordregions(startloc);
799		goto record;
800
801	case VSASSIGN:
802	case VSQUESTION:
803		if (!set) {
804			if (subevalvar(p, var, 0, subtype, startloc, varflags,
805			    quotes)) {
806				varflags &= ~VSNUL;
807				/*
808				 * Remove any recorded regions beyond
809				 * start of variable
810				 */
811				removerecordregions(startloc);
812				goto again;
813			}
814			break;
815		}
816		if (easy)
817			goto record;
818		break;
819
820	case VSERROR:
821		c = p - var - 1;
822		error("${%.*s%s}: Bad substitution", c, var,
823		    (c > 0 && *p != CTLENDVAR) ? "..." : "");
824
825	default:
826		abort();
827	}
828	p[-1] = '=';	/* recover overwritten '=' */
829
830	if (subtype != VSNORMAL) {	/* skip to end of alternative */
831		int nesting = 1;
832		for (;;) {
833			if ((c = *p++) == CTLESC)
834				p++;
835			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
836				if (set)
837					argbackq = argbackq->next;
838			} else if (c == CTLVAR) {
839				if ((*p++ & VSTYPE) != VSNORMAL)
840					nesting++;
841			} else if (c == CTLENDVAR) {
842				if (--nesting == 0)
843					break;
844			}
845		}
846	}
847	return p;
848}
849
850
851
852/*
853 * Test whether a specialized variable is set.
854 */
855
856static int
857varisset(char *name, int nulok)
858{
859
860	if (*name == '!')
861		return backgndpidset();
862	else if (*name == '@' || *name == '*') {
863		if (*shellparam.p == NULL)
864			return 0;
865
866		if (nulok) {
867			char **av;
868
869			for (av = shellparam.p; *av; av++)
870				if (**av != '\0')
871					return 1;
872			return 0;
873		}
874	} else if (is_digit(*name)) {
875		char *ap;
876		int num = atoi(name);
877
878		if (num > shellparam.nparam)
879			return 0;
880
881		if (num == 0)
882			ap = arg0;
883		else
884			ap = shellparam.p[num - 1];
885
886		if (nulok && (ap == NULL || *ap == '\0'))
887			return 0;
888	}
889	return 1;
890}
891
892static void
893strtodest(const char *p, int flag, int subtype, int quoted)
894{
895	if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH)
896		STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest);
897	else
898		STPUTS(p, expdest);
899}
900
901/*
902 * Add the value of a specialized variable to the stack string.
903 */
904
905static void
906varvalue(char *name, int quoted, int subtype, int flag)
907{
908	int num;
909	char *p;
910	int i;
911	char sep;
912	char **ap;
913
914	switch (*name) {
915	case '$':
916		num = rootpid;
917		goto numvar;
918	case '?':
919		num = oexitstatus;
920		goto numvar;
921	case '#':
922		num = shellparam.nparam;
923		goto numvar;
924	case '!':
925		num = backgndpidval();
926numvar:
927		expdest = cvtnum(num, expdest);
928		break;
929	case '-':
930		for (i = 0 ; i < NOPTS ; i++) {
931			if (optlist[i].val)
932				STPUTC(optlist[i].letter, expdest);
933		}
934		break;
935	case '@':
936		if (flag & EXP_FULL && quoted) {
937			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
938				strtodest(p, flag, subtype, quoted);
939				if (*ap)
940					STPUTC('\0', expdest);
941			}
942			break;
943		}
944		/* FALLTHROUGH */
945	case '*':
946		if (ifsset())
947			sep = ifsval()[0];
948		else
949			sep = ' ';
950		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
951			strtodest(p, flag, subtype, quoted);
952			if (!*ap)
953				break;
954			if (sep || (flag & EXP_FULL && !quoted && **ap != '\0'))
955				STPUTC(sep, expdest);
956		}
957		break;
958	case '0':
959		p = arg0;
960		strtodest(p, flag, subtype, quoted);
961		break;
962	default:
963		if (is_digit(*name)) {
964			num = atoi(name);
965			if (num > 0 && num <= shellparam.nparam) {
966				p = shellparam.p[num - 1];
967				strtodest(p, flag, subtype, quoted);
968			}
969		}
970		break;
971	}
972}
973
974
975
976/*
977 * Record the fact that we have to scan this region of the
978 * string for IFS characters.
979 */
980
981static void
982recordregion(int start, int end, int inquotes)
983{
984	struct ifsregion *ifsp;
985
986	if (ifslastp == NULL) {
987		ifsp = &ifsfirst;
988	} else {
989		if (ifslastp->endoff == start
990		    && ifslastp->inquotes == inquotes) {
991			/* extend previous area */
992			ifslastp->endoff = end;
993			return;
994		}
995		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
996		ifslastp->next = ifsp;
997	}
998	ifslastp = ifsp;
999	ifslastp->next = NULL;
1000	ifslastp->begoff = start;
1001	ifslastp->endoff = end;
1002	ifslastp->inquotes = inquotes;
1003}
1004
1005
1006
1007/*
1008 * Break the argument string into pieces based upon IFS and add the
1009 * strings to the argument list.  The regions of the string to be
1010 * searched for IFS characters have been stored by recordregion.
1011 * CTLESC characters are preserved but have little effect in this pass
1012 * other than escaping CTL* characters.  In particular, they do not escape
1013 * IFS characters: that should be done with the ifsregion mechanism.
1014 * CTLQUOTEMARK characters are used to preserve empty quoted strings.
1015 * This pass treats them as a regular character, making the string non-empty.
1016 * Later, they are removed along with the other CTL* characters.
1017 */
1018static void
1019ifsbreakup(char *string, struct arglist *arglist)
1020{
1021	struct ifsregion *ifsp;
1022	struct strlist *sp;
1023	char *start;
1024	char *p;
1025	char *q;
1026	const char *ifs;
1027	const char *ifsspc;
1028	int had_param_ch = 0;
1029
1030	start = string;
1031
1032	if (ifslastp == NULL) {
1033		/* Return entire argument, IFS doesn't apply to any of it */
1034		sp = (struct strlist *)stalloc(sizeof *sp);
1035		sp->text = start;
1036		*arglist->lastp = sp;
1037		arglist->lastp = &sp->next;
1038		return;
1039	}
1040
1041	ifs = ifsset() ? ifsval() : " \t\n";
1042
1043	for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
1044		p = string + ifsp->begoff;
1045		while (p < string + ifsp->endoff) {
1046			q = p;
1047			if (*p == CTLESC)
1048				p++;
1049			if (ifsp->inquotes) {
1050				/* Only NULs (should be from "$@") end args */
1051				had_param_ch = 1;
1052				if (*p != 0) {
1053					p++;
1054					continue;
1055				}
1056				ifsspc = NULL;
1057			} else {
1058				if (!strchr(ifs, *p)) {
1059					had_param_ch = 1;
1060					p++;
1061					continue;
1062				}
1063				ifsspc = strchr(" \t\n", *p);
1064
1065				/* Ignore IFS whitespace at start */
1066				if (q == start && ifsspc != NULL) {
1067					p++;
1068					start = p;
1069					continue;
1070				}
1071				had_param_ch = 0;
1072			}
1073
1074			/* Save this argument... */
1075			*q = '\0';
1076			sp = (struct strlist *)stalloc(sizeof *sp);
1077			sp->text = start;
1078			*arglist->lastp = sp;
1079			arglist->lastp = &sp->next;
1080			p++;
1081
1082			if (ifsspc != NULL) {
1083				/* Ignore further trailing IFS whitespace */
1084				for (; p < string + ifsp->endoff; p++) {
1085					q = p;
1086					if (*p == CTLESC)
1087						p++;
1088					if (strchr(ifs, *p) == NULL) {
1089						p = q;
1090						break;
1091					}
1092					if (strchr(" \t\n", *p) == NULL) {
1093						p++;
1094						break;
1095					}
1096				}
1097			}
1098			start = p;
1099		}
1100	}
1101
1102	/*
1103	 * Save anything left as an argument.
1104	 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
1105	 * generating 2 arguments, the second of which is empty.
1106	 * Some recent clarification of the Posix spec say that it
1107	 * should only generate one....
1108	 */
1109	if (had_param_ch || *start != 0) {
1110		sp = (struct strlist *)stalloc(sizeof *sp);
1111		sp->text = start;
1112		*arglist->lastp = sp;
1113		arglist->lastp = &sp->next;
1114	}
1115}
1116
1117
1118static char expdir[PATH_MAX];
1119#define expdir_end (expdir + sizeof(expdir))
1120
1121/*
1122 * Perform pathname generation and remove control characters.
1123 * At this point, the only control characters should be CTLESC and CTLQUOTEMARK.
1124 * The results are stored in the list exparg.
1125 */
1126static void
1127expandmeta(struct strlist *str, int flag __unused)
1128{
1129	char *p;
1130	struct strlist **savelastp;
1131	struct strlist *sp;
1132	char c;
1133	/* TODO - EXP_REDIR */
1134
1135	while (str) {
1136		if (fflag)
1137			goto nometa;
1138		p = str->text;
1139		for (;;) {			/* fast check for meta chars */
1140			if ((c = *p++) == '\0')
1141				goto nometa;
1142			if (c == '*' || c == '?' || c == '[')
1143				break;
1144		}
1145		savelastp = exparg.lastp;
1146		INTOFF;
1147		expmeta(expdir, str->text);
1148		INTON;
1149		if (exparg.lastp == savelastp) {
1150			/*
1151			 * no matches
1152			 */
1153nometa:
1154			*exparg.lastp = str;
1155			rmescapes(str->text);
1156			exparg.lastp = &str->next;
1157		} else {
1158			*exparg.lastp = NULL;
1159			*savelastp = sp = expsort(*savelastp);
1160			while (sp->next != NULL)
1161				sp = sp->next;
1162			exparg.lastp = &sp->next;
1163		}
1164		str = str->next;
1165	}
1166}
1167
1168
1169/*
1170 * Do metacharacter (i.e. *, ?, [...]) expansion.
1171 */
1172
1173static void
1174expmeta(char *enddir, char *name)
1175{
1176	char *p;
1177	char *q;
1178	char *start;
1179	char *endname;
1180	int metaflag;
1181	struct stat statb;
1182	DIR *dirp;
1183	struct dirent *dp;
1184	int atend;
1185	int matchdot;
1186	int esc;
1187
1188	metaflag = 0;
1189	start = name;
1190	for (p = name; esc = 0, *p; p += esc + 1) {
1191		if (*p == '*' || *p == '?')
1192			metaflag = 1;
1193		else if (*p == '[') {
1194			q = p + 1;
1195			if (*q == '!' || *q == '^')
1196				q++;
1197			for (;;) {
1198				while (*q == CTLQUOTEMARK)
1199					q++;
1200				if (*q == CTLESC)
1201					q++;
1202				if (*q == '/' || *q == '\0')
1203					break;
1204				if (*++q == ']') {
1205					metaflag = 1;
1206					break;
1207				}
1208			}
1209		} else if (*p == '\0')
1210			break;
1211		else if (*p == CTLQUOTEMARK)
1212			continue;
1213		else {
1214			if (*p == CTLESC)
1215				esc++;
1216			if (p[esc] == '/') {
1217				if (metaflag)
1218					break;
1219				start = p + esc + 1;
1220			}
1221		}
1222	}
1223	if (metaflag == 0) {	/* we've reached the end of the file name */
1224		if (enddir != expdir)
1225			metaflag++;
1226		for (p = name ; ; p++) {
1227			if (*p == CTLQUOTEMARK)
1228				continue;
1229			if (*p == CTLESC)
1230				p++;
1231			*enddir++ = *p;
1232			if (*p == '\0')
1233				break;
1234			if (enddir == expdir_end)
1235				return;
1236		}
1237		if (metaflag == 0 || lstat(expdir, &statb) >= 0)
1238			addfname(expdir);
1239		return;
1240	}
1241	endname = p;
1242	if (start != name) {
1243		p = name;
1244		while (p < start) {
1245			while (*p == CTLQUOTEMARK)
1246				p++;
1247			if (*p == CTLESC)
1248				p++;
1249			*enddir++ = *p++;
1250			if (enddir == expdir_end)
1251				return;
1252		}
1253	}
1254	if (enddir == expdir) {
1255		p = ".";
1256	} else if (enddir == expdir + 1 && *expdir == '/') {
1257		p = "/";
1258	} else {
1259		p = expdir;
1260		enddir[-1] = '\0';
1261	}
1262	if ((dirp = opendir(p)) == NULL)
1263		return;
1264	if (enddir != expdir)
1265		enddir[-1] = '/';
1266	if (*endname == 0) {
1267		atend = 1;
1268	} else {
1269		atend = 0;
1270		*endname = '\0';
1271		endname += esc + 1;
1272	}
1273	matchdot = 0;
1274	p = start;
1275	while (*p == CTLQUOTEMARK)
1276		p++;
1277	if (*p == CTLESC)
1278		p++;
1279	if (*p == '.')
1280		matchdot++;
1281	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
1282		if (dp->d_name[0] == '.' && ! matchdot)
1283			continue;
1284		if (patmatch(start, dp->d_name, 0)) {
1285			if (enddir + dp->d_namlen + 1 > expdir_end)
1286				continue;
1287			memcpy(enddir, dp->d_name, dp->d_namlen + 1);
1288			if (atend)
1289				addfname(expdir);
1290			else {
1291				if (enddir + dp->d_namlen + 2 > expdir_end)
1292					continue;
1293				enddir[dp->d_namlen] = '/';
1294				enddir[dp->d_namlen + 1] = '\0';
1295				expmeta(enddir + dp->d_namlen + 1, endname);
1296			}
1297		}
1298	}
1299	closedir(dirp);
1300	if (! atend)
1301		endname[-esc - 1] = esc ? CTLESC : '/';
1302}
1303
1304
1305/*
1306 * Add a file name to the list.
1307 */
1308
1309static void
1310addfname(char *name)
1311{
1312	char *p;
1313	struct strlist *sp;
1314
1315	p = stalloc(strlen(name) + 1);
1316	scopy(name, p);
1317	sp = (struct strlist *)stalloc(sizeof *sp);
1318	sp->text = p;
1319	*exparg.lastp = sp;
1320	exparg.lastp = &sp->next;
1321}
1322
1323
1324/*
1325 * Sort the results of file name expansion.  It calculates the number of
1326 * strings to sort and then calls msort (short for merge sort) to do the
1327 * work.
1328 */
1329
1330static struct strlist *
1331expsort(struct strlist *str)
1332{
1333	int len;
1334	struct strlist *sp;
1335
1336	len = 0;
1337	for (sp = str ; sp ; sp = sp->next)
1338		len++;
1339	return msort(str, len);
1340}
1341
1342
1343static struct strlist *
1344msort(struct strlist *list, int len)
1345{
1346	struct strlist *p, *q = NULL;
1347	struct strlist **lpp;
1348	int half;
1349	int n;
1350
1351	if (len <= 1)
1352		return list;
1353	half = len >> 1;
1354	p = list;
1355	for (n = half ; --n >= 0 ; ) {
1356		q = p;
1357		p = p->next;
1358	}
1359	q->next = NULL;			/* terminate first half of list */
1360	q = msort(list, half);		/* sort first half of list */
1361	p = msort(p, len - half);		/* sort second half */
1362	lpp = &list;
1363	for (;;) {
1364		if (strcmp(p->text, q->text) < 0) {
1365			*lpp = p;
1366			lpp = &p->next;
1367			if ((p = *lpp) == NULL) {
1368				*lpp = q;
1369				break;
1370			}
1371		} else {
1372			*lpp = q;
1373			lpp = &q->next;
1374			if ((q = *lpp) == NULL) {
1375				*lpp = p;
1376				break;
1377			}
1378		}
1379	}
1380	return list;
1381}
1382
1383
1384
1385static wchar_t
1386get_wc(const char **p)
1387{
1388	wchar_t c;
1389	int chrlen;
1390
1391	chrlen = mbtowc(&c, *p, 4);
1392	if (chrlen == 0)
1393		return 0;
1394	else if (chrlen == -1)
1395		c = 0;
1396	else
1397		*p += chrlen;
1398	return c;
1399}
1400
1401
1402/*
1403 * Returns true if the pattern matches the string.
1404 */
1405
1406int
1407patmatch(const char *pattern, const char *string, int squoted)
1408{
1409	const char *p, *q;
1410	char c;
1411	wchar_t wc, wc2;
1412
1413	p = pattern;
1414	q = string;
1415	for (;;) {
1416		switch (c = *p++) {
1417		case '\0':
1418			goto breakloop;
1419		case CTLESC:
1420			if (squoted && *q == CTLESC)
1421				q++;
1422			if (*q++ != *p++)
1423				return 0;
1424			break;
1425		case CTLQUOTEMARK:
1426			continue;
1427		case '?':
1428			if (squoted && *q == CTLESC)
1429				q++;
1430			if (localeisutf8)
1431				wc = get_wc(&q);
1432			else
1433				wc = (unsigned char)*q++;
1434			if (wc == '\0')
1435				return 0;
1436			break;
1437		case '*':
1438			c = *p;
1439			while (c == CTLQUOTEMARK || c == '*')
1440				c = *++p;
1441			if (c != CTLESC &&  c != CTLQUOTEMARK &&
1442			    c != '?' && c != '*' && c != '[') {
1443				while (*q != c) {
1444					if (squoted && *q == CTLESC &&
1445					    q[1] == c)
1446						break;
1447					if (*q == '\0')
1448						return 0;
1449					if (squoted && *q == CTLESC)
1450						q++;
1451					q++;
1452				}
1453			}
1454			do {
1455				if (patmatch(p, q, squoted))
1456					return 1;
1457				if (squoted && *q == CTLESC)
1458					q++;
1459			} while (*q++ != '\0');
1460			return 0;
1461		case '[': {
1462			const char *endp;
1463			int invert, found;
1464			wchar_t chr;
1465
1466			endp = p;
1467			if (*endp == '!' || *endp == '^')
1468				endp++;
1469			for (;;) {
1470				while (*endp == CTLQUOTEMARK)
1471					endp++;
1472				if (*endp == '\0')
1473					goto dft;		/* no matching ] */
1474				if (*endp == CTLESC)
1475					endp++;
1476				if (*++endp == ']')
1477					break;
1478			}
1479			invert = 0;
1480			if (*p == '!' || *p == '^') {
1481				invert++;
1482				p++;
1483			}
1484			found = 0;
1485			if (squoted && *q == CTLESC)
1486				q++;
1487			if (localeisutf8)
1488				chr = get_wc(&q);
1489			else
1490				chr = (unsigned char)*q++;
1491			if (chr == '\0')
1492				return 0;
1493			c = *p++;
1494			do {
1495				if (c == CTLQUOTEMARK)
1496					continue;
1497				if (c == CTLESC)
1498					c = *p++;
1499				if (localeisutf8 && c & 0x80) {
1500					p--;
1501					wc = get_wc(&p);
1502					if (wc == 0) /* bad utf-8 */
1503						return 0;
1504				} else
1505					wc = (unsigned char)c;
1506				if (*p == '-' && p[1] != ']') {
1507					p++;
1508					while (*p == CTLQUOTEMARK)
1509						p++;
1510					if (*p == CTLESC)
1511						p++;
1512					if (localeisutf8) {
1513						wc2 = get_wc(&p);
1514						if (wc2 == 0) /* bad utf-8 */
1515							return 0;
1516					} else
1517						wc2 = (unsigned char)*p++;
1518					if (   collate_range_cmp(chr, wc) >= 0
1519					    && collate_range_cmp(chr, wc2) <= 0
1520					   )
1521						found = 1;
1522				} else {
1523					if (chr == wc)
1524						found = 1;
1525				}
1526			} while ((c = *p++) != ']');
1527			if (found == invert)
1528				return 0;
1529			break;
1530		}
1531dft:	        default:
1532			if (squoted && *q == CTLESC)
1533				q++;
1534			if (*q++ != c)
1535				return 0;
1536			break;
1537		}
1538	}
1539breakloop:
1540	if (*q != '\0')
1541		return 0;
1542	return 1;
1543}
1544
1545
1546
1547/*
1548 * Remove any CTLESC and CTLQUOTEMARK characters from a string.
1549 */
1550
1551void
1552rmescapes(char *str)
1553{
1554	char *p, *q;
1555
1556	p = str;
1557	while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) {
1558		if (*p++ == '\0')
1559			return;
1560	}
1561	q = p;
1562	while (*p) {
1563		if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) {
1564			p++;
1565			continue;
1566		}
1567		if (*p == CTLESC)
1568			p++;
1569		*q++ = *p++;
1570	}
1571	*q = '\0';
1572}
1573
1574
1575
1576/*
1577 * See if a pattern matches in a case statement.
1578 */
1579
1580int
1581casematch(union node *pattern, const char *val)
1582{
1583	struct stackmark smark;
1584	int result;
1585	char *p;
1586
1587	setstackmark(&smark);
1588	argbackq = pattern->narg.backquote;
1589	STARTSTACKSTR(expdest);
1590	ifslastp = NULL;
1591	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
1592	STPUTC('\0', expdest);
1593	p = grabstackstr(expdest);
1594	result = patmatch(p, val, 0);
1595	popstackmark(&smark);
1596	return result;
1597}
1598
1599/*
1600 * Our own itoa().
1601 */
1602
1603static char *
1604cvtnum(int num, char *buf)
1605{
1606	char temp[32];
1607	int neg = num < 0;
1608	char *p = temp + 31;
1609
1610	temp[31] = '\0';
1611
1612	do {
1613		*--p = num % 10 + '0';
1614	} while ((num /= 10) != 0);
1615
1616	if (neg)
1617		*--p = '-';
1618
1619	STPUTS(p, buf);
1620	return buf;
1621}
1622
1623/*
1624 * Check statically if expanding a string may have side effects.
1625 */
1626int
1627expandhassideeffects(const char *p)
1628{
1629	int c;
1630	int arinest;
1631
1632	arinest = 0;
1633	while ((c = *p++) != '\0') {
1634		switch (c) {
1635		case CTLESC:
1636			p++;
1637			break;
1638		case CTLVAR:
1639			c = *p++;
1640			/* Expanding $! sets the job to remembered. */
1641			if (*p == '!')
1642				return 1;
1643			if ((c & VSTYPE) == VSASSIGN)
1644				return 1;
1645			/*
1646			 * If we are in arithmetic, the parameter may contain
1647			 * '=' which may cause side effects. Exceptions are
1648			 * the length of a parameter and $$, $# and $? which
1649			 * are always numeric.
1650			 */
1651			if ((c & VSTYPE) == VSLENGTH) {
1652				while (*p != '=')
1653					p++;
1654				p++;
1655				break;
1656			}
1657			if ((*p == '$' || *p == '#' || *p == '?') &&
1658			    p[1] == '=') {
1659				p += 2;
1660				break;
1661			}
1662			if (arinest > 0)
1663				return 1;
1664			break;
1665		case CTLBACKQ:
1666		case CTLBACKQ | CTLQUOTE:
1667			if (arinest > 0)
1668				return 1;
1669			break;
1670		case CTLARI:
1671			arinest++;
1672			break;
1673		case CTLENDARI:
1674			arinest--;
1675			break;
1676		case '=':
1677			if (*p == '=') {
1678				/* Allow '==' operator. */
1679				p++;
1680				continue;
1681			}
1682			if (arinest > 0)
1683				return 1;
1684			break;
1685		case '!': case '<': case '>':
1686			/* Allow '!=', '<=', '>=' operators. */
1687			if (*p == '=')
1688				p++;
1689			break;
1690		}
1691	}
1692	return 0;
1693}
1694
1695/*
1696 * Do most of the work for wordexp(3).
1697 */
1698
1699int
1700wordexpcmd(int argc, char **argv)
1701{
1702	size_t len;
1703	int i;
1704
1705	out1fmt("%08x", argc - 1);
1706	for (i = 1, len = 0; i < argc; i++)
1707		len += strlen(argv[i]);
1708	out1fmt("%08x", (int)len);
1709	for (i = 1; i < argc; i++)
1710		outbin(argv[i], strlen(argv[i]) + 1, out1);
1711        return (0);
1712}
1713