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