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