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