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