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