aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2020-02-24 10:02:50 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2020-02-24 19:20:51 +0100
commit823318822ccb0e0910abbd4e1f983637dfd9d3c0 (patch)
tree79623a9926f3e3ac979029d66b30f22f8c488fdc
parent6cda0b04a3d3c36312adf7a9aa608306f6a5704a (diff)
downloadbusybox-w32-823318822ccb0e0910abbd4e1f983637dfd9d3c0.tar.gz
busybox-w32-823318822ccb0e0910abbd4e1f983637dfd9d3c0.tar.bz2
busybox-w32-823318822ccb0e0910abbd4e1f983637dfd9d3c0.zip
ash: expand: Do not reprocess data when expanding words
Upstream patch: Date: Wed, 30 May 2018 02:06:03 +0800 expand: Do not reprocess data when expanding words Currently various paths will reprocess data when performing word expansion. For example, expari will skip backwards looking for the start of the arithmetic expansion, while evalvar will skip unexpanded words manually. This is cumbersome and error-prone. This patch fixes this by making word expansions proceed in a linear fashion. This means changing argstr and the various expansion functions such as expari and subevalvar to return the next character to be expanded. This is inspired by similar code from FreeBSD. However, we take things one step further and completely remove the manual word skipping in evalvar. This is accomplished by introducing a new EXP_DISCARD flag that tells argstr to only parse and not produce any actual expansions. Incidentally, argstr will now always NUL-terminate the expansion unless the EXP_WORD flag is set. This is because all but one caller of argstr wants the result to be NUL-termianted. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Also includes two one-line follow-up fixes: expand: Eat closing brace for length parameter if (subtype == VSLENGTH) { + p++; if (flag & EXP_DISCARD) expand: Fix double-decrement in argstr - newloc = expdest - (char *)stackblock() - end; + newloc = q - (char *)stackblock() - end; and changes in code for bash substring extensions. Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/ash.c332
1 files changed, 154 insertions, 178 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 03864c667..425da6bb6 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -6035,6 +6035,7 @@ static int substr_atoi(const char *s)
6035#define EXP_WORD 0x40 /* expand word in parameter expansion */ 6035#define EXP_WORD 0x40 /* expand word in parameter expansion */
6036#define EXP_QUOTED 0x100 /* expand word in double quotes */ 6036#define EXP_QUOTED 0x100 /* expand word in double quotes */
6037#define EXP_KEEPNUL 0x200 /* do not skip NUL characters */ 6037#define EXP_KEEPNUL 0x200 /* do not skip NUL characters */
6038#define EXP_DISCARD 0x400 /* discard result of expansion */
6038 6039
6039/* 6040/*
6040 * rmescape() flags 6041 * rmescape() flags
@@ -6452,13 +6453,15 @@ removerecordregions(int endoff)
6452} 6453}
6453 6454
6454static char * 6455static char *
6455exptilde(char *startp, char *p, int flag) 6456exptilde(char *startp, int flag)
6456{ 6457{
6457 unsigned char c; 6458 unsigned char c;
6458 char *name; 6459 char *name;
6459 struct passwd *pw; 6460 struct passwd *pw;
6460 const char *home; 6461 const char *home;
6462 char *p;
6461 6463
6464 p = startp;
6462 name = p + 1; 6465 name = p + 1;
6463 6466
6464 while ((c = *++p) != '\0') { 6467 while ((c = *++p) != '\0') {
@@ -6477,6 +6480,8 @@ exptilde(char *startp, char *p, int flag)
6477 } 6480 }
6478 } 6481 }
6479 done: 6482 done:
6483 if (flag & EXP_DISCARD)
6484 goto out;
6480 *p = '\0'; 6485 *p = '\0';
6481 if (*name == '\0') { 6486 if (*name == '\0') {
6482 home = lookupvar("HOME"); 6487 home = lookupvar("HOME");
@@ -6486,13 +6491,13 @@ exptilde(char *startp, char *p, int flag)
6486 goto lose; 6491 goto lose;
6487 home = pw->pw_dir; 6492 home = pw->pw_dir;
6488 } 6493 }
6494 *p = c;
6489 if (!home) 6495 if (!home)
6490 goto lose; 6496 goto lose;
6491 *p = c;
6492 strtodest(home, flag | EXP_QUOTED); 6497 strtodest(home, flag | EXP_QUOTED);
6498 out:
6493 return p; 6499 return p;
6494 lose: 6500 lose:
6495 *p = c;
6496 return startp; 6501 return startp;
6497} 6502}
6498 6503
@@ -6591,6 +6596,9 @@ expbackq(union node *cmd, int flag)
6591 int startloc; 6596 int startloc;
6592 struct stackmark smark; 6597 struct stackmark smark;
6593 6598
6599 if (flag & EXP_DISCARD)
6600 goto out;
6601
6594 INT_OFF; 6602 INT_OFF;
6595 startloc = expdest - (char *)stackblock(); 6603 startloc = expdest - (char *)stackblock();
6596 pushstackmark(&smark, startloc); 6604 pushstackmark(&smark, startloc);
@@ -6632,64 +6640,57 @@ expbackq(union node *cmd, int flag)
6632 (int)((dest - (char *)stackblock()) - startloc), 6640 (int)((dest - (char *)stackblock()) - startloc),
6633 (int)((dest - (char *)stackblock()) - startloc), 6641 (int)((dest - (char *)stackblock()) - startloc),
6634 stackblock() + startloc)); 6642 stackblock() + startloc));
6643
6644 out:
6645 argbackq = argbackq->next;
6635} 6646}
6636 6647
6648/* expari needs it */
6649static char *argstr(char *p, int flag);
6650
6637#if ENABLE_FEATURE_SH_MATH 6651#if ENABLE_FEATURE_SH_MATH
6638/* 6652/*
6639 * Expand arithmetic expression. Backup to start of expression, 6653 * Expand arithmetic expression. Backup to start of expression,
6640 * evaluate, place result in (backed up) result, adjust string position. 6654 * evaluate, place result in (backed up) result, adjust string position.
6641 */ 6655 */
6642static void 6656static char *
6643expari(int flag) 6657expari(char *start, int flag)
6644{ 6658{
6645 char *p, *start; 6659 struct stackmark sm;
6646 int begoff; 6660 int begoff;
6661 int endoff;
6647 int len; 6662 int len;
6663 arith_t result;
6664 char *p;
6648 6665
6649 /* ifsfree(); */ 6666 p = stackblock();
6650 6667 begoff = expdest - p;
6651 /* 6668 p = argstr(start, flag & EXP_DISCARD);
6652 * This routine is slightly over-complicated for
6653 * efficiency. Next we scan backwards looking for the
6654 * start of arithmetic.
6655 */
6656 start = stackblock();
6657 p = expdest - 1;
6658 *p = '\0';
6659 p--;
6660 while (1) {
6661 int esc;
6662
6663 while ((unsigned char)*p != CTLARI) {
6664 p--;
6665#if DEBUG
6666 if (p < start) {
6667 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
6668 }
6669#endif
6670 }
6671
6672 esc = esclen(start, p);
6673 if (!(esc % 2)) {
6674 break;
6675 }
6676 6669
6677 p -= esc + 1; 6670 if (flag & EXP_DISCARD)
6678 } 6671 goto out;
6679 6672
6680 begoff = p - start; 6673 start = stackblock();
6674 endoff = expdest - start;
6675 start += begoff;
6676 STADJUST(start - expdest, expdest);
6681 6677
6682 removerecordregions(begoff); 6678 removerecordregions(begoff);
6683 6679
6684 expdest = p;
6685
6686 if (flag & QUOTES_ESC) 6680 if (flag & QUOTES_ESC)
6687 rmescapes(p + 1, 0, NULL); 6681 rmescapes(start, 0, NULL);
6682
6683 pushstackmark(&sm, endoff);
6684 result = ash_arith(start);
6685 popstackmark(&sm);
6688 6686
6689 len = cvtnum(ash_arith(p + 1), flag); 6687 len = cvtnum(result, flag);
6690 6688
6691 if (!(flag & EXP_QUOTED)) 6689 if (!(flag & EXP_QUOTED))
6692 recordregion(begoff, begoff + len, 0); 6690 recordregion(begoff, begoff + len, 0);
6691
6692 out:
6693 return p;
6693} 6694}
6694#endif 6695#endif
6695 6696
@@ -6701,7 +6702,7 @@ static char *evalvar(char *p, int flags);
6701 * characters to allow for further processing. Otherwise treat 6702 * characters to allow for further processing. Otherwise treat
6702 * $@ like $* since no splitting will be performed. 6703 * $@ like $* since no splitting will be performed.
6703 */ 6704 */
6704static void 6705static char *
6705argstr(char *p, int flag) 6706argstr(char *p, int flag)
6706{ 6707{
6707 static const char spclchars[] ALIGN1 = { 6708 static const char spclchars[] ALIGN1 = {
@@ -6713,6 +6714,7 @@ argstr(char *p, int flag)
6713 CTLVAR, 6714 CTLVAR,
6714 CTLBACKQ, 6715 CTLBACKQ,
6715#if ENABLE_FEATURE_SH_MATH 6716#if ENABLE_FEATURE_SH_MATH
6717 CTLARI,
6716 CTLENDARI, 6718 CTLENDARI,
6717#endif 6719#endif
6718 '\0' 6720 '\0'
@@ -6723,41 +6725,45 @@ argstr(char *p, int flag)
6723 size_t length; 6725 size_t length;
6724 int startloc; 6726 int startloc;
6725 6727
6726 if (!(flag & EXP_VARTILDE)) { 6728 reject += !!(flag & EXP_VARTILDE2);
6727 reject += 2; 6729 reject += flag & EXP_VARTILDE ? 0 : 2;
6728 } else if (flag & EXP_VARTILDE2) {
6729 reject++;
6730 }
6731 inquotes = 0; 6730 inquotes = 0;
6732 length = 0; 6731 length = 0;
6733 if (flag & EXP_TILDE) { 6732 if (flag & EXP_TILDE) {
6734 char *q;
6735
6736 flag &= ~EXP_TILDE; 6733 flag &= ~EXP_TILDE;
6737 tilde: 6734 tilde:
6738 q = p; 6735 if (*p == '~')
6739 if (*q == '~') 6736 p = exptilde(p, flag);
6740 p = exptilde(p, q, flag);
6741 } 6737 }
6742 start: 6738 start:
6743 startloc = expdest - (char *)stackblock(); 6739 startloc = expdest - (char *)stackblock();
6744 for (;;) { 6740 for (;;) {
6741 int end;
6745 unsigned char c; 6742 unsigned char c;
6746 6743
6747 length += strcspn(p + length, reject); 6744 length += strcspn(p + length, reject);
6745 end = 0;
6748 c = p[length]; 6746 c = p[length];
6749 if (c) { 6747 if (!(c & 0x80)
6750 if (!(c & 0x80) 6748 IF_FEATURE_SH_MATH(|| c == CTLENDARI)
6751 IF_FEATURE_SH_MATH(|| c == CTLENDARI) 6749 || c == CTLENDVAR
6752 ) { 6750 ) {
6753 /* c == '=' || c == ':' || c == CTLENDARI */ 6751 /*
6754 length++; 6752 * c == '=' || c == ':' || c == '\0' ||
6755 } 6753 * c == CTLENDARI || c == CTLENDVAR
6754 */
6755 length++;
6756 /* c == '\0' || c == CTLENDARI || c == CTLENDVAR */
6757 end = !!((c - 1) & 0x80);
6756 } 6758 }
6757 if (length > 0) { 6759 if (length > 0 && !(flag & EXP_DISCARD)) {
6758 int newloc; 6760 int newloc;
6759 expdest = stnputs(p, length, expdest); 6761 char *q;
6760 newloc = expdest - (char *)stackblock(); 6762
6763 q = stnputs(p, length, expdest);
6764 q[-1] &= end - 1;
6765 expdest = q - (flag & EXP_WORD ? end : 0);
6766 newloc = q - (char *)stackblock() - end;
6761 if (breakall && !inquotes && newloc > startloc) { 6767 if (breakall && !inquotes && newloc > startloc) {
6762 recordregion(startloc, newloc, 0); 6768 recordregion(startloc, newloc, 0);
6763 } 6769 }
@@ -6766,14 +6772,11 @@ argstr(char *p, int flag)
6766 p += length + 1; 6772 p += length + 1;
6767 length = 0; 6773 length = 0;
6768 6774
6775 if (end)
6776 break;
6777
6769 switch (c) { 6778 switch (c) {
6770 case '\0':
6771 goto breakloop;
6772 case '=': 6779 case '=':
6773 if (flag & EXP_VARTILDE2) {
6774 p--;
6775 continue;
6776 }
6777 flag |= EXP_VARTILDE2; 6780 flag |= EXP_VARTILDE2;
6778 reject++; 6781 reject++;
6779 /* fall through */ 6782 /* fall through */
@@ -6786,11 +6789,6 @@ argstr(char *p, int flag)
6786 goto tilde; 6789 goto tilde;
6787 } 6790 }
6788 continue; 6791 continue;
6789 }
6790
6791 switch (c) {
6792 case CTLENDVAR: /* ??? */
6793 goto breakloop;
6794 case CTLQUOTEMARK: 6792 case CTLQUOTEMARK:
6795 /* "$@" syntax adherence hack */ 6793 /* "$@" syntax adherence hack */
6796 if (!inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) { 6794 if (!inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) {
@@ -6816,17 +6814,15 @@ argstr(char *p, int flag)
6816 goto start; 6814 goto start;
6817 case CTLBACKQ: 6815 case CTLBACKQ:
6818 expbackq(argbackq->n, flag | inquotes); 6816 expbackq(argbackq->n, flag | inquotes);
6819 argbackq = argbackq->next;
6820 goto start; 6817 goto start;
6821#if ENABLE_FEATURE_SH_MATH 6818#if ENABLE_FEATURE_SH_MATH
6822 case CTLENDARI: 6819 case CTLARI:
6823 p--; 6820 p = expari(p, flag | inquotes);
6824 expari(flag | inquotes);
6825 goto start; 6821 goto start;
6826#endif 6822#endif
6827 } 6823 }
6828 } 6824 }
6829 breakloop: ; 6825 return p - 1;
6830} 6826}
6831 6827
6832static char * 6828static char *
@@ -6951,25 +6947,27 @@ varunset(const char *end, const char *var, const char *umsg, int varflags)
6951 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail); 6947 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
6952} 6948}
6953 6949
6954static const char * 6950static char *
6955subevalvar(char *p, char *str, int strloc, int subtype, 6951subevalvar(char *start, char *str, int strloc,
6956 int startloc, int varflags, int flag) 6952 int startloc, int varflags, int flag)
6957{ 6953{
6958 struct nodelist *saveargbackq = argbackq; 6954 int subtype = varflags & VSTYPE;
6959 int quotes = flag & QUOTES_ESC; 6955 int quotes = flag & QUOTES_ESC;
6960 char *startp; 6956 char *startp;
6961 char *loc; 6957 char *loc;
6962 char *rmesc, *rmescend; 6958 char *rmesc, *rmescend;
6963 int amount, resetloc; 6959 long amount;
6960 int resetloc;
6964 int argstr_flags; 6961 int argstr_flags;
6965 IF_BASH_PATTERN_SUBST(int workloc;) 6962 IF_BASH_PATTERN_SUBST(int workloc;)
6966 IF_BASH_PATTERN_SUBST(int slash_pos;) 6963 IF_BASH_PATTERN_SUBST(int slash_pos;)
6967 IF_BASH_PATTERN_SUBST(char *repl;) 6964 IF_BASH_PATTERN_SUBST(char *repl;)
6968 int zero; 6965 int zero;
6969 char *(*scan)(char*, char*, char*, char*, int, int); 6966 char *(*scan)(char*, char*, char*, char*, int, int);
6967 char *p;
6970 6968
6971 //bb_error_msg("subevalvar(p:'%s',str:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)", 6969 //bb_error_msg("subevalvar(start:'%s',str:'%s',strloc:%d,startloc:%d,varflags:%x,quotes:%d)",
6972 // p, str, strloc, subtype, startloc, varflags, quotes); 6970 // start, str, strloc, startloc, varflags, quotes);
6973 6971
6974#if BASH_PATTERN_SUBST 6972#if BASH_PATTERN_SUBST
6975 /* For "${v/pattern/repl}", we must find the delimiter _before_ 6973 /* For "${v/pattern/repl}", we must find the delimiter _before_
@@ -6979,7 +6977,7 @@ subevalvar(char *p, char *str, int strloc, int subtype,
6979 repl = NULL; 6977 repl = NULL;
6980 if (subtype == VSREPLACE || subtype == VSREPLACEALL) { 6978 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6981 /* Find '/' and replace with NUL */ 6979 /* Find '/' and replace with NUL */
6982 repl = p; 6980 repl = start;
6983 /* The pattern can't be empty. 6981 /* The pattern can't be empty.
6984 * IOW: if the first char after "${v//" is a slash, 6982 * IOW: if the first char after "${v//" is a slash,
6985 * it does not terminate the pattern - it's the first char of the pattern: 6983 * it does not terminate the pattern - it's the first char of the pattern:
@@ -7004,17 +7002,17 @@ subevalvar(char *p, char *str, int strloc, int subtype,
7004 } 7002 }
7005 } 7003 }
7006#endif 7004#endif
7007 argstr_flags = EXP_TILDE; 7005 argstr_flags = (flag & EXP_DISCARD) | EXP_TILDE;
7008 if (subtype != VSASSIGN 7006 if (!str
7009 && subtype != VSQUESTION
7010#if BASH_SUBSTR 7007#if BASH_SUBSTR
7011 && subtype != VSSUBSTR 7008 && subtype != VSSUBSTR
7012#endif 7009#endif
7013 ) { 7010 ) {
7014 /* EXP_CASE keeps CTLESC's */ 7011 /* EXP_CASE keeps CTLESC's */
7015 argstr_flags = EXP_TILDE | EXP_CASE; 7012 argstr_flags |= EXP_CASE;
7016 } 7013 }
7017 argstr(p, argstr_flags); 7014 p = argstr(start, argstr_flags);
7015
7018 //bb_error_msg("str0:'%s'", (char *)stackblock() + strloc); 7016 //bb_error_msg("str0:'%s'", (char *)stackblock() + strloc);
7019#if BASH_PATTERN_SUBST 7017#if BASH_PATTERN_SUBST
7020 slash_pos = -1; 7018 slash_pos = -1;
@@ -7022,24 +7020,25 @@ subevalvar(char *p, char *str, int strloc, int subtype,
7022 slash_pos = expdest - ((char *)stackblock() + strloc); 7020 slash_pos = expdest - ((char *)stackblock() + strloc);
7023 STPUTC('/', expdest); 7021 STPUTC('/', expdest);
7024 //bb_error_msg("repl+1:'%s'", repl + 1); 7022 //bb_error_msg("repl+1:'%s'", repl + 1);
7025 argstr(repl + 1, EXP_TILDE); /* EXP_TILDE: echo "${v/x/~}" expands ~ ! */ 7023 p = argstr(repl + 1, (flag & EXP_DISCARD) | EXP_TILDE); /* EXP_TILDE: echo "${v/x/~}" expands ~ ! */
7026 *repl = '/'; 7024 *repl = '/';
7027 } 7025 }
7028#endif 7026#endif
7029 STPUTC('\0', expdest); 7027 if (flag & EXP_DISCARD)
7030 argbackq = saveargbackq; 7028 return p;
7029
7031 startp = (char *)stackblock() + startloc; 7030 startp = (char *)stackblock() + startloc;
7032 //bb_error_msg("str1:'%s'", (char *)stackblock() + strloc); 7031 //bb_error_msg("str1:'%s'", (char *)stackblock() + strloc);
7033 7032
7034 switch (subtype) { 7033 switch (subtype) {
7035 case VSASSIGN: 7034 case VSASSIGN:
7036 setvar0(str, startp); 7035 setvar0(str, startp);
7037 amount = startp - expdest; 7036
7038 STADJUST(amount, expdest); 7037 loc = startp;
7039 return startp; 7038 goto out;
7040 7039
7041 case VSQUESTION: 7040 case VSQUESTION:
7042 varunset(p, str, startp, varflags); 7041 varunset(start, str, startp, varflags);
7043 /* NOTREACHED */ 7042 /* NOTREACHED */
7044 7043
7045#if BASH_SUBSTR 7044#if BASH_SUBSTR
@@ -7110,9 +7109,7 @@ subevalvar(char *p, char *str, int strloc, int subtype,
7110 *loc++ = *vstr++; 7109 *loc++ = *vstr++;
7111 } 7110 }
7112 *loc = '\0'; 7111 *loc = '\0';
7113 amount = loc - expdest; 7112 goto out;
7114 STADJUST(amount, expdest);
7115 return loc;
7116 } 7113 }
7117#endif /* BASH_SUBSTR */ 7114#endif /* BASH_SUBSTR */
7118 } 7115 }
@@ -7178,7 +7175,7 @@ subevalvar(char *p, char *str, int strloc, int subtype,
7178 7175
7179 /* If there's no pattern to match, return the expansion unmolested */ 7176 /* If there's no pattern to match, return the expansion unmolested */
7180 if (str[0] == '\0') 7177 if (str[0] == '\0')
7181 return NULL; 7178 goto out1;
7182 7179
7183 len = 0; 7180 len = 0;
7184 idx = startp; 7181 idx = startp;
@@ -7259,9 +7256,8 @@ subevalvar(char *p, char *str, int strloc, int subtype,
7259 startp = (char *)stackblock() + startloc; 7256 startp = (char *)stackblock() + startloc;
7260 memmove(startp, (char *)stackblock() + workloc, len + 1); 7257 memmove(startp, (char *)stackblock() + workloc, len + 1);
7261 //bb_error_msg("startp:'%s'", startp); 7258 //bb_error_msg("startp:'%s'", startp);
7262 amount = expdest - (startp + len); 7259 loc = startp + len;
7263 STADJUST(-amount, expdest); 7260 goto out;
7264 return startp;
7265 } 7261 }
7266#endif /* BASH_PATTERN_SUBST */ 7262#endif /* BASH_PATTERN_SUBST */
7267 7263
@@ -7282,10 +7278,17 @@ subevalvar(char *p, char *str, int strloc, int subtype,
7282 loc = startp + (str - loc) - 1; 7278 loc = startp + (str - loc) - 1;
7283 } 7279 }
7284 *loc = '\0'; 7280 *loc = '\0';
7285 amount = loc - expdest; 7281 } else
7286 STADJUST(amount, expdest); 7282 loc = str - 1;
7287 } 7283
7288 return loc; 7284 out:
7285 amount = loc - expdest;
7286 STADJUST(amount, expdest);
7287 out1:
7288 /* Remove any recorded regions beyond start of variable */
7289 removerecordregions(startloc);
7290
7291 return p;
7289} 7292}
7290 7293
7291/* 7294/*
@@ -7310,7 +7313,14 @@ varvalue(char *name, int varflags, int flags, int quoted)
7310 ssize_t len = 0; 7313 ssize_t len = 0;
7311 int sep; 7314 int sep;
7312 int subtype = varflags & VSTYPE; 7315 int subtype = varflags & VSTYPE;
7313 int discard = subtype == VSPLUS || subtype == VSLENGTH; 7316 int discard = (subtype == VSPLUS || subtype == VSLENGTH) | (flags & EXP_DISCARD);
7317
7318 if (!subtype) {
7319 if (discard)
7320 return -1;
7321
7322 raise_error_syntax("bad substitution");
7323 }
7314 7324
7315 flags |= EXP_KEEPNUL; 7325 flags |= EXP_KEEPNUL;
7316 flags &= discard ? ~QUOTES_ESC : ~0; 7326 flags &= discard ? ~QUOTES_ESC : ~0;
@@ -7427,6 +7437,7 @@ varvalue(char *name, int varflags, int flags, int quoted)
7427 7437
7428 if (discard) 7438 if (discard)
7429 STADJUST(-len, expdest); 7439 STADJUST(-len, expdest);
7440
7430 return len; 7441 return len;
7431} 7442}
7432 7443
@@ -7439,18 +7450,15 @@ evalvar(char *p, int flag)
7439{ 7450{
7440 char varflags; 7451 char varflags;
7441 char subtype; 7452 char subtype;
7442 int quoted;
7443 char *var; 7453 char *var;
7444 int patloc; 7454 int patloc;
7445 int startloc; 7455 int startloc;
7446 ssize_t varlen; 7456 ssize_t varlen;
7457 int quoted;
7447 7458
7448 varflags = (unsigned char) *p++; 7459 varflags = (unsigned char) *p++;
7449 subtype = varflags & VSTYPE; 7460 subtype = varflags & VSTYPE;
7450 7461
7451 if (!subtype)
7452 raise_error_syntax("bad substitution");
7453
7454 quoted = flag & EXP_QUOTED; 7462 quoted = flag & EXP_QUOTED;
7455 var = p; 7463 var = p;
7456 startloc = expdest - (char *)stackblock(); 7464 startloc = expdest - (char *)stackblock();
@@ -7461,35 +7469,29 @@ evalvar(char *p, int flag)
7461 if (varflags & VSNUL) 7469 if (varflags & VSNUL)
7462 varlen--; 7470 varlen--;
7463 7471
7464 if (subtype == VSPLUS) { 7472 switch (subtype) {
7473 case VSPLUS:
7465 varlen = -1 - varlen; 7474 varlen = -1 - varlen;
7466 goto vsplus; 7475 /* fall through */
7467 } 7476 case 0:
7468 7477 case VSMINUS:
7469 if (subtype == VSMINUS) { 7478 p = argstr(p, flag | EXP_TILDE | EXP_WORD);
7470 vsplus: 7479 if (varlen < 0)
7471 if (varlen < 0) { 7480 return p;
7472 argstr(
7473 p,
7474 flag | EXP_TILDE | EXP_WORD
7475 );
7476 goto end;
7477 }
7478 goto record; 7481 goto record;
7479 }
7480 7482
7481 if (subtype == VSASSIGN || subtype == VSQUESTION) { 7483 case VSASSIGN:
7484 case VSQUESTION:
7482 if (varlen >= 0) 7485 if (varlen >= 0)
7483 goto record; 7486 goto record;
7484 7487
7485 subevalvar(p, var, 0, subtype, startloc, varflags, 7488 p = subevalvar(p, var, 0, startloc, varflags,
7486 flag & ~QUOTES_ESC); 7489 flag & ~QUOTES_ESC);
7490
7491 if (flag & EXP_DISCARD)
7492 return p;
7493
7487 varflags &= ~VSNUL; 7494 varflags &= ~VSNUL;
7488 /*
7489 * Remove any recorded regions beyond
7490 * start of variable
7491 */
7492 removerecordregions(startloc);
7493 goto again; 7495 goto again;
7494 } 7496 }
7495 7497
@@ -7497,20 +7499,15 @@ evalvar(char *p, int flag)
7497 varunset(p, var, 0, 0); 7499 varunset(p, var, 0, 0);
7498 7500
7499 if (subtype == VSLENGTH) { 7501 if (subtype == VSLENGTH) {
7502 p++;
7503 if (flag & EXP_DISCARD)
7504 return p;
7500 cvtnum(varlen > 0 ? varlen : 0, flag); 7505 cvtnum(varlen > 0 ? varlen : 0, flag);
7501 goto record; 7506 goto record;
7502 } 7507 }
7503 7508
7504 if (subtype == VSNORMAL) { 7509 if (subtype == VSNORMAL)
7505 record: 7510 goto record;
7506 if (quoted) {
7507 quoted = *var == '@' && shellparam.nparam;
7508 if (!quoted)
7509 goto end;
7510 }
7511 recordregion(startloc, expdest - (char *)stackblock(), quoted);
7512 goto end;
7513 }
7514 7511
7515#if DEBUG 7512#if DEBUG
7516 switch (subtype) { 7513 switch (subtype) {
@@ -7531,46 +7528,28 @@ evalvar(char *p, int flag)
7531 } 7528 }
7532#endif 7529#endif
7533 7530
7534 if (varlen >= 0) { 7531 flag |= varlen < 0 ? EXP_DISCARD : 0;
7532 if (!(flag & EXP_DISCARD)) {
7535 /* 7533 /*
7536 * Terminate the string and start recording the pattern 7534 * Terminate the string and start recording the pattern
7537 * right after it 7535 * right after it
7538 */ 7536 */
7539 STPUTC('\0', expdest); 7537 STPUTC('\0', expdest);
7540 patloc = expdest - (char *)stackblock();
7541 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
7542 startloc, varflags, flag)) {
7543 int amount = expdest - (
7544 (char *)stackblock() + patloc - 1
7545 );
7546 STADJUST(-amount, expdest);
7547 }
7548 /* Remove any recorded regions beyond start of variable */
7549 removerecordregions(startloc);
7550 goto record;
7551 } 7538 }
7552 7539
7553 varlen = 0; 7540 patloc = expdest - (char *)stackblock();
7541 p = subevalvar(p, NULL, patloc, startloc, varflags, flag);
7554 7542
7555 end: 7543 record:
7556 if (subtype != VSNORMAL) { /* skip to end of alternative */ 7544 if (flag & EXP_DISCARD)
7557 int nesting = 1; 7545 return p;
7558 for (;;) { 7546
7559 unsigned char c = *p++; 7547 if (quoted) {
7560 if (c == CTLESC) 7548 quoted = *var == '@' && shellparam.nparam;
7561 p++; 7549 if (!quoted)
7562 else if (c == CTLBACKQ) { 7550 return p;
7563 if (varlen >= 0)
7564 argbackq = argbackq->next;
7565 } else if (c == CTLVAR) {
7566 if ((*p++ & VSTYPE) != VSNORMAL)
7567 nesting++;
7568 } else if (c == CTLENDVAR) {
7569 if (--nesting == 0)
7570 break;
7571 }
7572 }
7573 } 7551 }
7552 recordregion(startloc, expdest - (char *)stackblock(), quoted);
7574 return p; 7553 return p;
7575} 7554}
7576 7555
@@ -7983,13 +7962,11 @@ expandarg(union node *arg, struct arglist *arglist, int flag)
7983 STARTSTACKSTR(expdest); 7962 STARTSTACKSTR(expdest);
7984 TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag)); 7963 TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag));
7985 argstr(arg->narg.text, flag); 7964 argstr(arg->narg.text, flag);
7986 p = _STPUTC('\0', expdest);
7987 expdest = p - 1;
7988 if (arglist == NULL) { 7965 if (arglist == NULL) {
7989 /* here document expanded */ 7966 /* here document expanded */
7990 goto out; 7967 goto out;
7991 } 7968 }
7992 p = grabstackstr(p); 7969 p = grabstackstr(expdest);
7993 TRACE(("expandarg: p:'%s'\n", p)); 7970 TRACE(("expandarg: p:'%s'\n", p));
7994 exparg.lastp = &exparg.list; 7971 exparg.lastp = &exparg.list;
7995 /* 7972 /*
@@ -8050,7 +8027,6 @@ casematch(union node *pattern, char *val)
8050 argbackq = pattern->narg.backquote; 8027 argbackq = pattern->narg.backquote;
8051 STARTSTACKSTR(expdest); 8028 STARTSTACKSTR(expdest);
8052 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 8029 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
8053 STACKSTRNUL(expdest);
8054 ifsfree(); 8030 ifsfree();
8055 result = patmatch(stackblock(), val); 8031 result = patmatch(stackblock(), val);
8056 popstackmark(&smark); 8032 popstackmark(&smark);