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.
7 * This code is derived from software contributed to Berkeley by
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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
35 #include <sys/types.h>
55 * Routines to expand arguments to commands. We have to deal with
56 * backquotes, shell variables, and file metacharacters.
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 */
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
90 * Structure specifying which parts of the string should be searched
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 */
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;
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);
122 STATIC void addglob(const glob_t *);
124 STATIC void expmeta(char *, char *);
125 STATIC struct strlist *expsort(struct strlist *);
126 STATIC struct strlist *msort(struct strlist *, int);
128 STATIC void addfname(char *);
129 STATIC int patmatch(char *, const char *);
131 STATIC int pmatch(const char *, const char *);
133 #define pmatch(a, b) !fnmatch((a), (b), 0)
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__));
144 * Prepare a pattern for a glob(3) call.
146 * Returns an stalloced string.
150 preglob(const char *pattern, int flag) {
151 flag |= RMESCAPE_GLOB;
152 return _rmescapes((char *)pattern, flag);
157 esclen(const char *start, const char *p) {
160 while (p > start && *--p == (char)CTLESC) {
167 static inline const char *getpwhome(const char *name)
170 struct passwd *pw = getpwnam(name);
171 return pw ? pw->pw_dir : 0;
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.
186 expandarg(union node *arg, struct arglist *arglist, int flag)
191 argbackq = arg->narg.backquote;
192 STARTSTACKSTR(expdest);
193 argstr(arg->narg.text, flag);
194 p = _STPUTC('\0', expdest);
196 if (arglist == NULL) {
197 /* here document expanded */
201 exparg.lastp = &exparg.list;
205 if (flag & EXP_FULL) {
206 ifsbreakup(p, &exparg);
207 *exparg.lastp = NULL;
208 exparg.lastp = &exparg.list;
209 expandmeta(exparg.list, flag);
211 sp = (struct strlist *)stalloc(sizeof (struct strlist));
214 exparg.lastp = &sp->next;
216 *exparg.lastp = NULL;
218 *arglist->lastp = exparg.list;
219 arglist->lastp = exparg.lastp;
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.
235 argstr(char *p, int flag)
237 static const char spclchars[] = {
248 const char *reject = spclchars;
250 int breakall = (flag & (EXP_WORD | EXP_QUOTED)) == EXP_WORD;
255 if (!(flag & EXP_VARTILDE)) {
257 } else if (flag & EXP_VARTILDE2) {
262 if (flag & EXP_TILDE) {
269 p = exptilde(p, q, flag);
272 startloc = expdest - (char *)stackblock();
274 length += strcspn(p + length, reject);
275 c = (signed char)p[length];
276 if (c && (!(c & 0x80) || c == CTLENDARI)) {
277 /* c == '=' || c == ':' || c == CTLENDARI */
282 expdest = stnputs(p, length, expdest);
283 newloc = expdest - (char *)stackblock();
284 if (breakall && !inquotes && newloc > startloc) {
285 recordregion(startloc, newloc, 0);
296 if (flag & EXP_VARTILDE2) {
300 flag |= EXP_VARTILDE2;
305 * sort of a hack - expand tildes in variable
306 * assignments (after the first '=' and after ':'s).
315 case CTLENDVAR: /* ??? */
318 inquotes ^= EXP_QUOTED;
319 /* "$@" syntax adherence hack */
320 if (inquotes && !memcmp(p, dolatstr + 1,
322 p = evalvar(p + 1, flag | inquotes) + 1;
326 if (flag & QUOTES_ESC) {
337 * Quoted parameter expansion pattern: remove quote
338 * unless inside inner quotes or we have a literal
341 if (((flag | inquotes) & (EXP_QPAT | EXP_QUOTED)) ==
342 EXP_QPAT && *p != '\\')
347 p = evalvar(p, flag | inquotes);
350 expbackq(argbackq->n, flag | inquotes);
351 argbackq = argbackq->next;
355 expari(flag | inquotes);
364 exptilde(char *startp, char *p, int flag)
369 int quotes = flag & QUOTES_ESC;
373 while ((c = *++p) != '\0') {
380 if (flag & EXP_VARTILDE)
391 home = lookupvar(homestr);
393 home = getpwhome(name);
398 strtodest(home, SQSYNTAX, quotes);
407 removerecordregions(int endoff)
409 if (ifslastp == NULL)
412 if (ifsfirst.endoff > endoff) {
413 while (ifsfirst.next != NULL) {
414 struct ifsregion *ifsp;
416 ifsp = ifsfirst.next->next;
417 ckfree(ifsfirst.next);
418 ifsfirst.next = ifsp;
421 if (ifsfirst.begoff > endoff)
424 ifslastp = &ifsfirst;
425 ifsfirst.endoff = endoff;
430 ifslastp = &ifsfirst;
431 while (ifslastp->next && ifslastp->next->begoff < endoff)
432 ifslastp=ifslastp->next;
433 while (ifslastp->next != NULL) {
434 struct ifsregion *ifsp;
436 ifsp = ifslastp->next->next;
437 ckfree(ifslastp->next);
438 ifslastp->next = ifsp;
441 if (ifslastp->endoff > endoff)
442 ifslastp->endoff = endoff;
447 * Expand arithmetic expression. Backup to start of expression,
448 * evaluate, place result in (backed up) result, adjust string position.
462 * This routine is slightly over-complicated for
463 * efficiency. Next we scan backwards looking for the
464 * start of arithmetic.
466 start = stackblock();
468 pushstackmark(&sm, p - start);
474 while (*p != (char)CTLARI) {
478 sh_error("missing CTLARI (shouldn't happen)");
483 esc = esclen(start, p);
493 removerecordregions(begoff);
497 if (likely(flag & QUOTES_ESC))
500 result = arith(p + 1);
503 len = cvtnum(result);
505 if (likely(!(flag & EXP_QUOTED)))
506 recordregion(begoff, begoff + len, 0);
511 * Expand stuff in backwards quotes.
515 expbackq(union node *cmd, int flag)
523 char const *syntax = flag & EXP_QUOTED ? DQSYNTAX : BASESYNTAX;
524 struct stackmark smark;
527 startloc = expdest - (char *)stackblock();
528 pushstackmark(&smark, startloc);
529 evalbackcmd(cmd, (struct backcmd *) &in);
530 popstackmark(&smark);
537 memtodest(p, i, syntax, flag & QUOTES_ESC);
542 i = read(in.fd, buf, sizeof buf);
543 } while (i < 0 && errno == EINTR);
544 TRACE(("expbackq: read returns %d\n", i));
554 back_exitstatus = waitforjob(in.jp);
558 /* Eat all trailing newlines */
560 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
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));
575 char *startp, char *rmesc, char *rmescend, char *str, int quotes,
586 const char *s = loc2;
592 match = pmatch(str, s);
596 if (quotes && *loc == (char)CTLESC)
607 char *startp, char *rmesc, char *rmescend, char *str, int quotes,
614 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
617 const char *s = loc2;
622 match = pmatch(str, s);
629 esc = esclen(startp, loc);
641 subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int flag)
643 int quotes = flag & QUOTES_ESC;
646 struct nodelist *saveargbackq = argbackq;
648 char *rmesc, *rmescend;
650 char *(*scan)(char *, char *, char *, char *, int , int);
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;
661 setvar(str, startp, 0);
662 amount = startp - expdest;
663 STADJUST(amount, expdest);
667 varunset(p, str, startp, varflags);
671 subtype -= VSTRIMRIGHT;
673 if (subtype < 0 || subtype > 3)
678 rmescend = stackblock() + strloc;
680 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
681 if (rmesc != startp) {
683 startp = stackblock() + startloc;
687 str = stackblock() + strloc;
690 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
692 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
693 scan = (subtype & 1) ^ zero ? scanleft : scanright;
695 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
698 memmove(startp, loc, str - loc);
699 loc = startp + (str - loc) - 1;
702 amount = loc - expdest;
703 STADJUST(amount, expdest);
710 * Expand a variable, and return a pointer to the next character in the
714 evalvar(char *p, int flag)
727 subtype = varflags & VSTYPE;
730 sh_error("Bad substitution");
732 quoted = flag & EXP_QUOTED;
734 easy = (!quoted || (*var == '@' && shellparam.nparam));
735 startloc = expdest - (char *)stackblock();
736 p = strchr(p, '=') + 1;
739 varlen = varvalue(var, varflags, flag);
740 if (varflags & VSNUL)
743 if (subtype == VSPLUS) {
744 varlen = -1 - varlen;
748 if (subtype == VSMINUS) {
751 argstr(p, flag | EXP_TILDE | EXP_WORD);
759 if (subtype == VSASSIGN || subtype == VSQUESTION) {
761 if (subevalvar(p, var, 0, subtype, startloc,
762 varflags, flag & ~QUOTES_ESC)) {
765 * Remove any recorded regions beyond
768 removerecordregions(startloc);
778 if (varlen < 0 && uflag)
779 varunset(p, var, 0, 0);
781 if (subtype == VSLENGTH) {
782 cvtnum(varlen > 0 ? varlen : 0);
786 if (subtype == VSNORMAL) {
790 recordregion(startloc, expdest - (char *)stackblock(), quoted);
808 * Terminate the string and start recording the pattern
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
818 STADJUST(-amount, expdest);
820 /* Remove any recorded regions beyond start of variable */
821 removerecordregions(startloc);
826 if (subtype != VSNORMAL) { /* skip to end of alternative */
829 if ((c = (signed char)*p++) == CTLESC)
831 else if (c == CTLBACKQ) {
833 argbackq = argbackq->next;
834 } else if (c == CTLVAR) {
835 if ((*p++ & VSTYPE) != VSNORMAL)
837 } else if (c == CTLENDVAR) {
848 * Put a string on the stack.
852 memtodest(const char *p, size_t len, const char *syntax, int quotes) {
858 q = makestrspace(len * 2, expdest);
861 int c = (signed char)*p++;
863 if ((quotes & QUOTES_ESC) &&
864 ((syntax[c] == CCTL) ||
865 (((quotes & EXP_FULL) || syntax != BASESYNTAX) &&
866 syntax[c] == CBACK)))
868 } else if (!(quotes & QUOTES_KEEPNUL))
878 strtodest(p, syntax, quotes)
883 size_t len = strlen(p);
884 memtodest(p, len, syntax, quotes);
891 * Add the value of a specialized variable to the stack string.
895 varvalue(char *name, int varflags, int flags)
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;
910 sep = quoted ? ((flags & EXP_FULL) << CHAR_BIT) : 0;
911 syntax = quoted ? DQSYNTAX : BASESYNTAX;
921 num = shellparam.nparam;
931 p = makestrspace(NOPTS, expdest);
932 for (i = NOPTS - 1; i >= 0; i--) {
934 USTPUTC(optletters[i], p);
945 sep = ifsset() ? ifsval()[0] : ' ';
947 if (!(ap = shellparam.p))
950 while ((p = *ap++)) {
951 len += strtodest(p, syntax, quotes);
955 memtodest(&sepc, 1, syntax, quotes);
970 if (num < 0 || num > shellparam.nparam)
972 p = num ? shellparam.p[num - 1] : arg0;
980 len = strtodest(p, syntax, quotes);
985 STADJUST(-len, expdest);
992 * Record the fact that we have to scan this region of the
993 * string for IFS characters.
997 recordregion(int start, int end, int nulonly)
999 struct ifsregion *ifsp;
1001 if (ifslastp == NULL) {
1005 ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
1007 ifslastp->next = ifsp;
1011 ifslastp->begoff = start;
1012 ifslastp->endoff = end;
1013 ifslastp->nulonly = nulonly;
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.
1024 ifsbreakup(char *string, struct arglist *arglist)
1026 struct ifsregion *ifsp;
1031 const char *ifs, *realifs;
1037 if (ifslastp != NULL) {
1040 realifs = ifsset() ? ifsval() : defifs;
1043 p = string + ifsp->begoff;
1044 nulonly = ifsp->nulonly;
1045 ifs = nulonly ? nullstr : realifs;
1047 while (p < string + ifsp->endoff) {
1049 if (*p == (char)CTLESC)
1051 if (strchr(ifs, *p)) {
1053 ifsspc = (strchr(defifs, *p) != NULL);
1054 /* Ignore IFS whitespace at start */
1055 if (q == start && ifsspc) {
1061 sp = (struct strlist *)stalloc(sizeof *sp);
1063 *arglist->lastp = sp;
1064 arglist->lastp = &sp->next;
1068 if (p >= string + ifsp->endoff) {
1072 if (*p == (char)CTLESC)
1074 if (strchr(ifs, *p) == NULL ) {
1077 } else if (strchr(defifs, *p) == NULL) {
1093 } while ((ifsp = ifsp->next) != NULL);
1102 sp = (struct strlist *)stalloc(sizeof *sp);
1104 *arglist->lastp = sp;
1105 arglist->lastp = &sp->next;
1110 struct ifsregion *p = ifsfirst.next;
1117 struct ifsregion *ifsp;
1122 ifsfirst.next = NULL;
1132 * Expand shell metacharacters. At this point, the only control characters
1133 * should be escapes. The results are stored in the list exparg.
1138 expandmeta(str, flag)
1139 struct strlist *str;
1142 /* TODO - EXP_REDIR */
1152 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
1153 i = glob(p, GLOB_NOMAGIC, 0, &pglob);
1158 if (!(pglob.gl_flags & GLOB_MAGCHAR))
1169 *exparg.lastp = str;
1170 rmescapes(str->text);
1171 exparg.lastp = &str->next;
1173 default: /* GLOB_NOSPACE */
1174 sh_error("Out of space");
1182 * Add the result of glob(3) to the list.
1187 const glob_t *pglob;
1189 char **p = pglob->gl_pathv;
1197 #else /* HAVE_GLOB */
1198 STATIC char *expdir;
1202 expandmeta(struct strlist *str, int flag)
1204 static const char metachars[] = {
1207 /* TODO - EXP_REDIR */
1210 struct strlist **savelastp;
1216 if (!strpbrk(str->text, metachars))
1218 savelastp = exparg.lastp;
1221 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
1223 int i = strlen(str->text);
1224 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
1232 if (exparg.lastp == savelastp) {
1237 *exparg.lastp = str;
1238 rmescapes(str->text);
1239 exparg.lastp = &str->next;
1241 *exparg.lastp = NULL;
1242 *savelastp = sp = expsort(*savelastp);
1243 while (sp->next != NULL)
1245 exparg.lastp = &sp->next;
1253 * Do metacharacter (i.e. *, ?, [...]) expansion.
1257 expmeta(char *enddir, char *name)
1264 struct stat64 statb;
1273 for (p = name; esc = 0, *p; p += esc + 1) {
1274 if (*p == '*' || *p == '?')
1276 else if (*p == '[') {
1283 if (*q == '/' || *q == '\0')
1293 if (p[esc] == '/') {
1296 start = p + esc + 1;
1300 if (metaflag == 0) { /* we've reached the end of the file name */
1301 if (enddir != expdir)
1309 if (metaflag == 0 || lstat64(expdir, &statb) >= 0)
1320 } while (p < start);
1322 if (enddir == expdir) {
1324 } else if (enddir == expdir + 1 && *expdir == '/') {
1330 if ((dirp = opendir(cp)) == NULL)
1332 if (enddir != expdir)
1334 if (*endname == 0) {
1347 while (! int_pending() && (dp = readdir(dirp)) != NULL) {
1348 if (dp->d_name[0] == '.' && ! matchdot)
1350 if (pmatch(start, dp->d_name)) {
1352 scopy(dp->d_name, enddir);
1355 for (p = enddir, cp = dp->d_name;
1356 (*p++ = *cp++) != '\0';)
1359 expmeta(p, endname);
1365 endname[-esc - 1] = esc ? '\\' : '/';
1367 #endif /* HAVE_GLOB */
1371 * Add a file name to the list.
1375 addfname(char *name)
1379 sp = (struct strlist *)stalloc(sizeof *sp);
1380 sp->text = sstrdup(name);
1382 exparg.lastp = &sp->next;
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
1393 STATIC struct strlist *
1394 expsort(struct strlist *str)
1400 for (sp = str ; sp ; sp = sp->next)
1402 return msort(str, len);
1406 STATIC struct strlist *
1407 msort(struct strlist *list, int len)
1409 struct strlist *p, *q = NULL;
1410 struct strlist **lpp;
1418 for (n = half ; --n >= 0 ; ) {
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 */
1427 if (strcmp(p->text, q->text) < 0) {
1430 if ((p = *lpp) == NULL) {
1437 if ((q = *lpp) == NULL) {
1449 * Returns true if the pattern matches the string.
1453 patmatch(char *pattern, const char *string)
1455 return pmatch(preglob(pattern, 0), string);
1459 #ifndef HAVE_FNMATCH
1460 STATIC int ccmatch(const char *p, int chr, const char **r)
1462 static const struct class {
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 },
1479 const struct class *class, *end;
1481 end = classes + sizeof(classes) / sizeof(classes[0]);
1482 for (class = classes; class < end; class++) {
1485 q = prefix(p, class->name);
1489 return class->fn(chr);
1497 pmatch(const char *pattern, const char *string)
1521 if (c != '\\' && c != '?' && c != '*' && c != '[') {
1531 } while (*q++ != '\0');
1558 found |= !!ccmatch(p, chr, &r);
1563 } else if (c == '\\')
1565 if (*p == '-' && p[1] != ']') {
1569 if (chr >= c && chr <= *p)
1576 } while ((c = *p++) != ']');
1577 if (found == invert)
1597 * Remove any CTLESC characters from a string.
1601 _rmescapes(char *str, int flag)
1608 p = strpbrk(str, qchars);
1614 if (flag & RMESCAPE_ALLOC) {
1615 size_t len = p - str;
1616 size_t fulllen = len + strlen(p) + 1;
1618 if (flag & RMESCAPE_GROW) {
1619 int strloc = str - (char *)stackblock();
1621 r = makestrspace(fulllen, expdest);
1622 str = (char *)stackblock() + strloc;
1624 } else if (flag & RMESCAPE_HEAP) {
1625 r = ckmalloc(fulllen);
1627 r = stalloc(fulllen);
1631 q = mempcpy(q, str, len);
1635 globbing = flag & RMESCAPE_GLOB;
1636 notescaped = globbing;
1638 if (*p == (char)CTLQUOTEMARK) {
1639 inquotes = ~inquotes;
1641 notescaped = globbing;
1644 if (*p == (char)CTLESC) {
1648 } else if (*p == '\\' && !inquotes) {
1649 /* naked back slash */
1653 notescaped = globbing;
1658 if (flag & RMESCAPE_GROW) {
1660 STADJUST(q - r + 1, expdest);
1668 * See if a pattern matches in a case statement.
1672 casematch(union node *pattern, char *val)
1674 struct stackmark smark;
1677 setstackmark(&smark);
1678 argbackq = pattern->narg.backquote;
1679 STARTSTACKSTR(expdest);
1680 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
1681 STACKSTRNUL(expdest);
1683 result = patmatch(stackblock(), val);
1684 popstackmark(&smark);
1693 cvtnum(intmax_t num)
1695 int len = max_int_length(sizeof(num));
1697 expdest = makestrspace(len, expdest);
1698 len = fmtstr(expdest, len, "%" PRIdMAX, num);
1699 STADJUST(len, expdest);
1704 varunset(const char *end, const char *var, const char *umsg, int varflags)
1710 msg = "parameter not set";
1712 if (*end == (char)CTLENDVAR) {
1713 if (varflags & VSNUL)
1718 sh_error("%.*s: %s%s", end - var - 1, var, msg, tail);