Magellan Linux

Annotation of /trunk/mkinitrd-magellan/klibc/usr/dash/expand.c

Parent Directory Parent Directory | Revision Log Revision Log


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