aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2018-04-02 12:35:04 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2018-04-02 13:15:37 +0200
commit216913c290fd2b88b744c04c0a2ef21fd1410ba9 (patch)
tree906139aa9c6d05a091c55ab83d1bf547fe839f31
parente84212f8346741a2d4a04b40639c44fe519cf5a7 (diff)
downloadbusybox-w32-216913c290fd2b88b744c04c0a2ef21fd1410ba9.tar.gz
busybox-w32-216913c290fd2b88b744c04c0a2ef21fd1410ba9.tar.bz2
busybox-w32-216913c290fd2b88b744c04c0a2ef21fd1410ba9.zip
ash: parser: Add syntax stack for recursive parsing
This closes 10821. Upstream patch: From: Herbert Xu <herbert@gondor.apana.org.au> Date: Fri, 9 Mar 2018 00:14:02 +0800 parser: Add syntax stack for recursive parsing Without a stack of syntaxes we cannot correctly these two cases together: "${a#'$$'}" "${a#"${b-'$$'}"}" A recursive parser also helps in some other corner cases such as nested arithmetic expansion with paratheses. This patch adds a syntax stack allocated from the stack using alloca. As a side-effect this allows us to remove the naked backslashes for patterns within double-quotes, which means that EXP_QPAT also has to go. This patch also fixes removes any backslashes that precede right braces when they are present within a parameter expansion context, and backslashes that precede double quotes within inner double quotes inside a parameter expansion in a here-document context. The idea of a recursive parser is based on a patch by Harald van Dijk. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> var_bash3, var_bash4 and var_bash6 tests are updated with the output given by bash-4.3.43 With this patch, the following tests now pass for ash: dollar_repl_slash_bash2.tests squote_in_varexp2.tests squote_in_varexp.tests var_bash4.tests function old new delta readtoken1 2615 2874 +259 synstack_push - 54 +54 evalvar 574 571 -3 rmescapes 330 310 -20 subevalvar 1279 1258 -21 argstr 1146 1107 -39 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 1/4 up/down: 313/-83) Total: 230 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/ash.c231
-rw-r--r--shell/ash_test/ash-arith/arith_nested1.right1
-rwxr-xr-xshell/ash_test/ash-arith/arith_nested1.tests1
-rw-r--r--shell/ash_test/ash-quoting/squote_in_varexp3.right1
-rwxr-xr-xshell/ash_test/ash-quoting/squote_in_varexp3.tests1
-rw-r--r--shell/ash_test/ash-vars/var_bash3.right4
-rw-r--r--shell/ash_test/ash-vars/var_bash4.right16
-rw-r--r--shell/ash_test/ash-vars/var_bash6.right2
-rwxr-xr-xshell/ash_test/ash-vars/var_bash6.tests2
-rw-r--r--shell/ash_test/ash-vars/var_bash7.right1
-rwxr-xr-xshell/ash_test/ash-vars/var_bash7.tests1
-rw-r--r--shell/hush_test/hush-arith/arith_nested1.right1
-rwxr-xr-xshell/hush_test/hush-arith/arith_nested1.tests1
-rw-r--r--shell/hush_test/hush-quoting/squote_in_varexp3.right1
-rwxr-xr-xshell/hush_test/hush-quoting/squote_in_varexp3.tests1
-rw-r--r--shell/hush_test/hush-vars/var_bash3.right4
-rw-r--r--shell/hush_test/hush-vars/var_bash4.right16
-rw-r--r--shell/hush_test/hush-vars/var_bash6.right2
-rwxr-xr-xshell/hush_test/hush-vars/var_bash6.tests2
19 files changed, 168 insertions, 121 deletions
diff --git a/shell/ash.c b/shell/ash.c
index cf1d062fb..97379cd92 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -5888,10 +5888,9 @@ static int substr_atoi(const char *s)
5888 * performs globbing, and thus diverges from what we do). 5888 * performs globbing, and thus diverges from what we do).
5889 */ 5889 */
5890#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ 5890#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5891#define EXP_QPAT 0x20 /* pattern in quoted parameter expansion */ 5891#define EXP_VARTILDE2 0x20 /* expand tildes after colons only */
5892#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */ 5892#define EXP_WORD 0x40 /* expand word in parameter expansion */
5893#define EXP_WORD 0x80 /* expand word in parameter expansion */ 5893#define EXP_QUOTED 0x80 /* expand word in double quotes */
5894#define EXP_QUOTED 0x100 /* expand word in double quotes */
5895/* 5894/*
5896 * rmescape() flags 5895 * rmescape() flags
5897 */ 5896 */
@@ -5901,7 +5900,7 @@ static int substr_atoi(const char *s)
5901#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ 5900#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5902 5901
5903/* Add CTLESC when necessary. */ 5902/* Add CTLESC when necessary. */
5904#define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT) 5903#define QUOTES_ESC (EXP_FULL | EXP_CASE)
5905/* Do not skip NUL characters. */ 5904/* Do not skip NUL characters. */
5906#define QUOTES_KEEPNUL EXP_TILDE 5905#define QUOTES_KEEPNUL EXP_TILDE
5907 5906
@@ -6090,7 +6089,6 @@ rmescapes(char *str, int flag, int *slash_position)
6090 IF_BASH_PATTERN_SUBST('/',) CTLESC, CTLQUOTEMARK, '\0' }; 6089 IF_BASH_PATTERN_SUBST('/',) CTLESC, CTLQUOTEMARK, '\0' };
6091 6090
6092 char *p, *q, *r; 6091 char *p, *q, *r;
6093 unsigned inquotes;
6094 unsigned protect_against_glob; 6092 unsigned protect_against_glob;
6095 unsigned globbing; 6093 unsigned globbing;
6096 6094
@@ -6121,18 +6119,21 @@ rmescapes(char *str, int flag, int *slash_position)
6121 } 6119 }
6122 } 6120 }
6123 6121
6124 inquotes = 0;
6125 globbing = flag & RMESCAPE_GLOB; 6122 globbing = flag & RMESCAPE_GLOB;
6126 protect_against_glob = globbing; 6123 protect_against_glob = globbing;
6127 while (*p) { 6124 while (*p) {
6128 if ((unsigned char)*p == CTLQUOTEMARK) { 6125 if ((unsigned char)*p == CTLQUOTEMARK) {
6129// Note: both inquotes and protect_against_glob only affect whether 6126// Note: protect_against_glob only affect whether
6130// CTLESC,<ch> gets converted to <ch> or to \<ch> 6127// CTLESC,<ch> gets converted to <ch> or to \<ch>
6131 inquotes = ~inquotes;
6132 p++; 6128 p++;
6133 protect_against_glob = globbing; 6129 protect_against_glob = globbing;
6134 continue; 6130 continue;
6135 } 6131 }
6132 if (*p == '\\') {
6133 /* naked back slash */
6134 protect_against_glob = 0;
6135 goto copy;
6136 }
6136 if ((unsigned char)*p == CTLESC) { 6137 if ((unsigned char)*p == CTLESC) {
6137 p++; 6138 p++;
6138#if DEBUG 6139#if DEBUG
@@ -6168,10 +6169,6 @@ rmescapes(char *str, int flag, int *slash_position)
6168 *q++ = '\\'; 6169 *q++ = '\\';
6169 } 6170 }
6170 } 6171 }
6171 } else if (*p == '\\' && !inquotes) {
6172 /* naked back slash */
6173 protect_against_glob = 0;
6174 goto copy;
6175 } 6172 }
6176#if BASH_PATTERN_SUBST 6173#if BASH_PATTERN_SUBST
6177 else if (slash_position && p == str + *slash_position) { 6174 else if (slash_position && p == str + *slash_position) {
@@ -6669,16 +6666,6 @@ argstr(char *p, int flags)
6669 case CTLESC: 6666 case CTLESC:
6670 startloc++; 6667 startloc++;
6671 length++; 6668 length++;
6672
6673 /*
6674 * Quoted parameter expansion pattern: remove quote
6675 * unless inside inner quotes or we have a literal
6676 * backslash.
6677 */
6678 if (((flags | inquotes) & (EXP_QPAT | EXP_QUOTED)) ==
6679 EXP_QPAT && *p != '\\')
6680 break;
6681
6682 goto addquote; 6669 goto addquote;
6683 case CTLVAR: 6670 case CTLVAR:
6684 TRACE(("argstr: evalvar('%s')\n", p)); 6671 TRACE(("argstr: evalvar('%s')\n", p));
@@ -6869,15 +6856,24 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
6869 } 6856 }
6870#endif 6857#endif
6871 argstr_flags = EXP_TILDE; 6858 argstr_flags = EXP_TILDE;
6872 if (subtype != VSASSIGN && subtype != VSQUESTION) 6859 if (subtype != VSASSIGN
6873 argstr_flags |= (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE); 6860 && subtype != VSQUESTION
6861#if BASH_SUBSTR
6862 && subtype != VSSUBSTR
6863#endif
6864 ) {
6865 /* EXP_CASE keeps CTLESC's */
6866 argstr_flags = EXP_TILDE | EXP_CASE;
6867 }
6874 argstr(p, argstr_flags); 6868 argstr(p, argstr_flags);
6869 //bb_error_msg("str0:'%s'", (char *)stackblock() + strloc);
6875#if BASH_PATTERN_SUBST 6870#if BASH_PATTERN_SUBST
6876 slash_pos = -1; 6871 slash_pos = -1;
6877 if (repl) { 6872 if (repl) {
6878 slash_pos = expdest - ((char *)stackblock() + strloc); 6873 slash_pos = expdest - ((char *)stackblock() + strloc);
6879 STPUTC('/', expdest); 6874 STPUTC('/', expdest);
6880 argstr(repl + 1, argstr_flags); 6875 //bb_error_msg("repl+1:'%s'", repl + 1);
6876 argstr(repl + 1, EXP_TILDE); /* EXP_TILDE: echo "${v/x/~}" expands ~ ! */
6881 *repl = '/'; 6877 *repl = '/';
6882 } 6878 }
6883#endif 6879#endif
@@ -10669,6 +10665,34 @@ pgetc_eatbnl(void)
10669 return c; 10665 return c;
10670} 10666}
10671 10667
10668struct synstack {
10669 smalluint syntax;
10670 uint8_t innerdq :1;
10671 uint8_t varpushed :1;
10672 uint8_t dblquote :1;
10673 int varnest; /* levels of variables expansion */
10674 int dqvarnest; /* levels of variables expansion within double quotes */
10675 int parenlevel; /* levels of parens in arithmetic */
10676 struct synstack *prev;
10677 struct synstack *next;
10678};
10679
10680static void
10681synstack_push(struct synstack **stack, struct synstack *next, int syntax)
10682{
10683 memset(next, 0, sizeof(*next));
10684 next->syntax = syntax;
10685 next->next = *stack;
10686 (*stack)->prev = next;
10687 *stack = next;
10688}
10689
10690static ALWAYS_INLINE void
10691synstack_pop(struct synstack **stack)
10692{
10693 *stack = (*stack)->next;
10694}
10695
10672/* 10696/*
10673 * To handle the "." command, a stack of input files is used. Pushfile 10697 * To handle the "." command, a stack of input files is used. Pushfile
10674 * adds a new entry to the stack and popfile restores the previous level. 10698 * adds a new entry to the stack and popfile restores the previous level.
@@ -11928,19 +11952,13 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11928 size_t len; 11952 size_t len;
11929 struct nodelist *bqlist; 11953 struct nodelist *bqlist;
11930 smallint quotef; 11954 smallint quotef;
11931 smallint dblquote;
11932 smallint oldstyle; 11955 smallint oldstyle;
11933 IF_FEATURE_SH_MATH(smallint prevsyntax;) /* syntax before arithmetic */
11934 smallint pssyntax; /* we are expanding a prompt string */ 11956 smallint pssyntax; /* we are expanding a prompt string */
11935 int varnest; /* levels of variables expansion */
11936 IF_FEATURE_SH_MATH(int arinest;) /* levels of arithmetic expansion */
11937 IF_FEATURE_SH_MATH(int parenlevel;) /* levels of parens in arithmetic */
11938 int dqvarnest; /* levels of variables expansion within double quotes */
11939 IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;) 11957 IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;)
11958 /* syntax stack */
11959 struct synstack synbase = { .syntax = syntax };
11960 struct synstack *synstack = &synbase;
11940 11961
11941 bqlist = NULL;
11942 quotef = 0;
11943 IF_FEATURE_SH_MATH(prevsyntax = 0;)
11944#if ENABLE_ASH_EXPAND_PRMT 11962#if ENABLE_ASH_EXPAND_PRMT
11945 pssyntax = (syntax == PSSYNTAX); 11963 pssyntax = (syntax == PSSYNTAX);
11946 if (pssyntax) 11964 if (pssyntax)
@@ -11948,11 +11966,10 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11948#else 11966#else
11949 pssyntax = 0; /* constant */ 11967 pssyntax = 0; /* constant */
11950#endif 11968#endif
11951 dblquote = (syntax == DQSYNTAX); 11969 if (syntax == DQSYNTAX)
11952 varnest = 0; 11970 synstack->dblquote = 1;
11953 IF_FEATURE_SH_MATH(arinest = 0;) 11971 quotef = 0;
11954 IF_FEATURE_SH_MATH(parenlevel = 0;) 11972 bqlist = NULL;
11955 dqvarnest = 0;
11956 11973
11957 STARTSTACKSTR(out); 11974 STARTSTACKSTR(out);
11958 loop: 11975 loop:
@@ -11960,9 +11977,9 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11960 CHECKEND(); /* set c to PEOF if at end of here document */ 11977 CHECKEND(); /* set c to PEOF if at end of here document */
11961 for (;;) { /* until end of line or end of word */ 11978 for (;;) { /* until end of line or end of word */
11962 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */ 11979 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
11963 switch (SIT(c, syntax)) { 11980 switch (SIT(c, synstack->syntax)) {
11964 case CNL: /* '\n' */ 11981 case CNL: /* '\n' */
11965 if (syntax == BASESYNTAX) 11982 if (synstack->syntax == BASESYNTAX)
11966 goto endword; /* exit outer loop */ 11983 goto endword; /* exit outer loop */
11967 USTPUTC(c, out); 11984 USTPUTC(c, out);
11968 nlprompt(); 11985 nlprompt();
@@ -11982,13 +11999,13 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11982 if (c & 0x100) { 11999 if (c & 0x100) {
11983 /* Unknown escape. Encode as '\z' */ 12000 /* Unknown escape. Encode as '\z' */
11984 c = (unsigned char)c; 12001 c = (unsigned char)c;
11985 if (eofmark == NULL || dblquote) 12002 if (eofmark == NULL || synstack->dblquote)
11986 USTPUTC(CTLESC, out); 12003 USTPUTC(CTLESC, out);
11987 USTPUTC('\\', out); 12004 USTPUTC('\\', out);
11988 } 12005 }
11989 } 12006 }
11990#endif 12007#endif
11991 if (eofmark == NULL || dblquote) 12008 if (eofmark == NULL || synstack->dblquote)
11992 USTPUTC(CTLESC, out); 12009 USTPUTC(CTLESC, out);
11993 USTPUTC(c, out); 12010 USTPUTC(c, out);
11994 break; 12011 break;
@@ -12008,20 +12025,13 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12008 /* Backslash is retained if we are in "str" 12025 /* Backslash is retained if we are in "str"
12009 * and next char isn't dquote-special. 12026 * and next char isn't dquote-special.
12010 */ 12027 */
12011 if (dblquote 12028 if (synstack->dblquote
12012 && c != '\\' 12029 && c != '\\'
12013 && c != '`' 12030 && c != '`'
12014 && c != '$' 12031 && c != '$'
12015 && (c != '"' || eofmark != NULL) 12032 && (c != '"' || (eofmark != NULL && !synstack->varnest))
12033 && (c != '}' || !synstack->varnest)
12016 ) { 12034 ) {
12017//dash survives not doing USTPUTC(CTLESC), but merely by chance:
12018//Example: "\z" gets encoded as "\<CTLESC>z".
12019//rmescapes() then emits "\", "\z", protecting z from globbing.
12020//But it's wrong, should protect _both_ from globbing:
12021//everything in double quotes is not globbed.
12022//Unlike dash, we have a fix in rmescapes() which emits bare "z"
12023//for "<CTLESC>z" since "z" is not glob-special (else unicode may break),
12024//and glob would see "\z" and eat "\". Thus:
12025 USTPUTC(CTLESC, out); /* protect '\' from glob */ 12035 USTPUTC(CTLESC, out); /* protect '\' from glob */
12026 USTPUTC('\\', out); 12036 USTPUTC('\\', out);
12027 } 12037 }
@@ -12031,56 +12041,62 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12031 } 12041 }
12032 break; 12042 break;
12033 case CSQUOTE: 12043 case CSQUOTE:
12034 syntax = SQSYNTAX; 12044 synstack->syntax = SQSYNTAX;
12035 quotemark: 12045 quotemark:
12036 if (eofmark == NULL) { 12046 if (eofmark == NULL) {
12037 USTPUTC(CTLQUOTEMARK, out); 12047 USTPUTC(CTLQUOTEMARK, out);
12038 } 12048 }
12039 break; 12049 break;
12040 case CDQUOTE: 12050 case CDQUOTE:
12041 syntax = DQSYNTAX; 12051 synstack->syntax = DQSYNTAX;
12042 dblquote = 1; 12052 synstack->dblquote = 1;
12053 toggledq:
12054 if (synstack->varnest)
12055 synstack->innerdq ^= 1;
12043 goto quotemark; 12056 goto quotemark;
12044 case CENDQUOTE: 12057 case CENDQUOTE:
12045 IF_BASH_DOLLAR_SQUOTE(bash_dollar_squote = 0;) 12058 IF_BASH_DOLLAR_SQUOTE(bash_dollar_squote = 0;)
12046 if (eofmark != NULL && varnest == 0) { 12059 if (eofmark != NULL && synstack->varnest == 0) {
12047 USTPUTC(c, out); 12060 USTPUTC(c, out);
12048 } else { 12061 break;
12049 if (dqvarnest == 0) {
12050 syntax = BASESYNTAX;
12051 dblquote = 0;
12052 }
12053 quotef = 1;
12054 goto quotemark;
12055 } 12062 }
12056 break; 12063
12064 if (synstack->dqvarnest == 0) {
12065 synstack->syntax = BASESYNTAX;
12066 synstack->dblquote = 0;
12067 }
12068
12069 quotef = 1;
12070
12071 if (c == '"')
12072 goto toggledq;
12073
12074 goto quotemark;
12057 case CVAR: /* '$' */ 12075 case CVAR: /* '$' */
12058 PARSESUB(); /* parse substitution */ 12076 PARSESUB(); /* parse substitution */
12059 break; 12077 break;
12060 case CENDVAR: /* '}' */ 12078 case CENDVAR: /* '}' */
12061 if (varnest > 0) { 12079 if (!synstack->innerdq && synstack->varnest > 0) {
12062 varnest--; 12080 if (!--synstack->varnest && synstack->varpushed)
12063 if (dqvarnest > 0) { 12081 synstack_pop(&synstack);
12064 dqvarnest--; 12082 else if (synstack->dqvarnest > 0)
12065 } 12083 synstack->dqvarnest--;
12066 c = CTLENDVAR; 12084 c = CTLENDVAR;
12067 } 12085 }
12068 USTPUTC(c, out); 12086 USTPUTC(c, out);
12069 break; 12087 break;
12070#if ENABLE_FEATURE_SH_MATH 12088#if ENABLE_FEATURE_SH_MATH
12071 case CLP: /* '(' in arithmetic */ 12089 case CLP: /* '(' in arithmetic */
12072 parenlevel++; 12090 synstack->parenlevel++;
12073 USTPUTC(c, out); 12091 USTPUTC(c, out);
12074 break; 12092 break;
12075 case CRP: /* ')' in arithmetic */ 12093 case CRP: /* ')' in arithmetic */
12076 if (parenlevel > 0) { 12094 if (synstack->parenlevel > 0) {
12077 parenlevel--; 12095 synstack->parenlevel--;
12078 } else { 12096 } else {
12079 if (pgetc_eatbnl() == ')') { 12097 if (pgetc_eatbnl() == ')') {
12080 c = CTLENDARI; 12098 c = CTLENDARI;
12081 if (--arinest == 0) { 12099 synstack_pop(&synstack);
12082 syntax = prevsyntax;
12083 }
12084 } else { 12100 } else {
12085 /* 12101 /*
12086 * unbalanced parens 12102 * unbalanced parens
@@ -12106,7 +12122,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12106 case CIGN: 12122 case CIGN:
12107 break; 12123 break;
12108 default: 12124 default:
12109 if (varnest == 0) { 12125 if (synstack->varnest == 0) {
12110#if BASH_REDIR_OUTPUT 12126#if BASH_REDIR_OUTPUT
12111 if (c == '&') { 12127 if (c == '&') {
12112//Can't call pgetc_eatbnl() here, this requires three-deep pungetc() 12128//Can't call pgetc_eatbnl() here, this requires three-deep pungetc()
@@ -12125,12 +12141,12 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12125 endword: 12141 endword:
12126 12142
12127#if ENABLE_FEATURE_SH_MATH 12143#if ENABLE_FEATURE_SH_MATH
12128 if (syntax == ARISYNTAX) 12144 if (synstack->syntax == ARISYNTAX)
12129 raise_error_syntax("missing '))'"); 12145 raise_error_syntax("missing '))'");
12130#endif 12146#endif
12131 if (syntax != BASESYNTAX && eofmark == NULL) 12147 if (synstack->syntax != BASESYNTAX && eofmark == NULL)
12132 raise_error_syntax("unterminated quoted string"); 12148 raise_error_syntax("unterminated quoted string");
12133 if (varnest != 0) { 12149 if (synstack->varnest != 0) {
12134 /* { */ 12150 /* { */
12135 raise_error_syntax("missing '}'"); 12151 raise_error_syntax("missing '}'");
12136 } 12152 }
@@ -12312,7 +12328,7 @@ parsesub: {
12312 || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) 12328 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
12313 ) { 12329 ) {
12314#if BASH_DOLLAR_SQUOTE 12330#if BASH_DOLLAR_SQUOTE
12315 if (syntax != DQSYNTAX && c == '\'') 12331 if (synstack->syntax != DQSYNTAX && c == '\'')
12316 bash_dollar_squote = 1; 12332 bash_dollar_squote = 1;
12317 else 12333 else
12318#endif 12334#endif
@@ -12332,6 +12348,8 @@ parsesub: {
12332 } 12348 }
12333 } else { 12349 } else {
12334 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */ 12350 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
12351 smalluint newsyn = synstack->syntax;
12352
12335 USTPUTC(CTLVAR, out); 12353 USTPUTC(CTLVAR, out);
12336 typeloc = out - (char *)stackblock(); 12354 typeloc = out - (char *)stackblock();
12337 STADJUST(1, out); 12355 STADJUST(1, out);
@@ -12390,6 +12408,8 @@ parsesub: {
12390 static const char types[] ALIGN1 = "}-+?="; 12408 static const char types[] ALIGN1 = "}-+?=";
12391 /* ${VAR...} but not $VAR or ${#VAR} */ 12409 /* ${VAR...} but not $VAR or ${#VAR} */
12392 /* c == first char after VAR */ 12410 /* c == first char after VAR */
12411 int cc = c;
12412
12393 switch (c) { 12413 switch (c) {
12394 case ':': 12414 case ':':
12395 c = pgetc_eatbnl(); 12415 c = pgetc_eatbnl();
@@ -12414,21 +12434,24 @@ parsesub: {
12414 break; 12434 break;
12415 } 12435 }
12416 case '%': 12436 case '%':
12417 case '#': { 12437 case '#':
12418 int cc = c;
12419 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT); 12438 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
12420 c = pgetc_eatbnl(); 12439 c = pgetc_eatbnl();
12421 if (c != cc) 12440 if (c == cc)
12422 goto badsub; 12441 subtype++;
12423 subtype++; 12442 else
12443 pungetc();
12444
12445 newsyn = BASESYNTAX;
12424 break; 12446 break;
12425 }
12426#if BASH_PATTERN_SUBST 12447#if BASH_PATTERN_SUBST
12427 case '/': 12448 case '/':
12428 /* ${v/[/]pattern/repl} */ 12449 /* ${v/[/]pattern/repl} */
12429//TODO: encode pattern and repl separately. 12450//TODO: encode pattern and repl separately.
12430// Currently ${v/$var_with_slash/repl} is horribly broken 12451// Currently cases like: v=1;echo ${v/$((1/1))/ONE}
12452// are broken (should print "ONE")
12431 subtype = VSREPLACE; 12453 subtype = VSREPLACE;
12454 newsyn = BASESYNTAX;
12432 c = pgetc_eatbnl(); 12455 c = pgetc_eatbnl();
12433 if (c != '/') 12456 if (c != '/')
12434 goto badsub; 12457 goto badsub;
@@ -12440,11 +12463,24 @@ parsesub: {
12440 badsub: 12463 badsub:
12441 pungetc(); 12464 pungetc();
12442 } 12465 }
12466
12467 if (newsyn == ARISYNTAX && subtype > VSNORMAL)
12468 newsyn = DQSYNTAX;
12469
12470 if (newsyn != synstack->syntax) {
12471 synstack_push(&synstack,
12472 synstack->prev ?: alloca(sizeof(*synstack)),
12473 newsyn);
12474
12475 synstack->varpushed = 1;
12476 synstack->dblquote = newsyn != BASESYNTAX;
12477 }
12478
12443 ((unsigned char *)stackblock())[typeloc] = subtype; 12479 ((unsigned char *)stackblock())[typeloc] = subtype;
12444 if (subtype != VSNORMAL) { 12480 if (subtype != VSNORMAL) {
12445 varnest++; 12481 synstack->varnest++;
12446 if (dblquote) 12482 if (synstack->dblquote)
12447 dqvarnest++; 12483 synstack->dqvarnest++;
12448 } 12484 }
12449 STPUTC('=', out); 12485 STPUTC('=', out);
12450 } 12486 }
@@ -12501,7 +12537,7 @@ parsebackq: {
12501 case '\\': 12537 case '\\':
12502 pc = pgetc(); /* or pgetc_eatbnl()? why (example)? */ 12538 pc = pgetc(); /* or pgetc_eatbnl()? why (example)? */
12503 if (pc != '\\' && pc != '`' && pc != '$' 12539 if (pc != '\\' && pc != '`' && pc != '$'
12504 && (!dblquote || pc != '"') 12540 && (!synstack->dblquote || pc != '"')
12505 ) { 12541 ) {
12506 STPUTC('\\', pout); 12542 STPUTC('\\', pout);
12507 } 12543 }
@@ -12576,10 +12612,11 @@ parsebackq: {
12576 * Parse an arithmetic expansion (indicate start of one and set state) 12612 * Parse an arithmetic expansion (indicate start of one and set state)
12577 */ 12613 */
12578parsearith: { 12614parsearith: {
12579 if (++arinest == 1) { 12615
12580 prevsyntax = syntax; 12616 synstack_push(&synstack,
12581 syntax = ARISYNTAX; 12617 synstack->prev ?: alloca(sizeof(*synstack)),
12582 } 12618 ARISYNTAX);
12619 synstack->dblquote = 1;
12583 USTPUTC(CTLARI, out); 12620 USTPUTC(CTLARI, out);
12584 goto parsearith_return; 12621 goto parsearith_return;
12585} 12622}
diff --git a/shell/ash_test/ash-arith/arith_nested1.right b/shell/ash_test/ash-arith/arith_nested1.right
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/shell/ash_test/ash-arith/arith_nested1.right
@@ -0,0 +1 @@
1
diff --git a/shell/ash_test/ash-arith/arith_nested1.tests b/shell/ash_test/ash-arith/arith_nested1.tests
new file mode 100755
index 000000000..28571b833
--- /dev/null
+++ b/shell/ash_test/ash-arith/arith_nested1.tests
@@ -0,0 +1 @@
echo $(( ( $((1)) ) ))
diff --git a/shell/ash_test/ash-quoting/squote_in_varexp3.right b/shell/ash_test/ash-quoting/squote_in_varexp3.right
new file mode 100644
index 000000000..223b7836f
--- /dev/null
+++ b/shell/ash_test/ash-quoting/squote_in_varexp3.right
@@ -0,0 +1 @@
B
diff --git a/shell/ash_test/ash-quoting/squote_in_varexp3.tests b/shell/ash_test/ash-quoting/squote_in_varexp3.tests
new file mode 100755
index 000000000..028a88fd9
--- /dev/null
+++ b/shell/ash_test/ash-quoting/squote_in_varexp3.tests
@@ -0,0 +1 @@
x=\'B; echo "${x#\'}"
diff --git a/shell/ash_test/ash-vars/var_bash3.right b/shell/ash_test/ash-vars/var_bash3.right
index a97c850ea..8899d981c 100644
--- a/shell/ash_test/ash-vars/var_bash3.right
+++ b/shell/ash_test/ash-vars/var_bash3.right
@@ -1,6 +1,6 @@
11 a041#c 11 a041#c
22 a041#c 22 a041#c
33 a\041#c 33 a041#c
44 a\041#c 44 a\041#c
55 a\041#c 55 a\041#c
66 a\041#c 66 a\041#c
@@ -17,4 +17,4 @@
1717 a\tc 1717 a\tc
1818 a\tc 1818 a\tc
1919 atc 1919 atc
2020 a\tc 2020 atc
diff --git a/shell/ash_test/ash-vars/var_bash4.right b/shell/ash_test/ash-vars/var_bash4.right
index 0ef1bf661..9067e58e6 100644
--- a/shell/ash_test/ash-vars/var_bash4.right
+++ b/shell/ash_test/ash-vars/var_bash4.right
@@ -3,26 +3,26 @@ Replace str: _\\_\z_
3Pattern: single backslash and star: "replace literal star" 3Pattern: single backslash and star: "replace literal star"
4Unquoted: a_\_z_b\*c 4Unquoted: a_\_z_b\*c
5Unquoted =: a_\_z_b\*c 5Unquoted =: a_\_z_b\*c
6Quoted: a_\_\z_b\*c 6Quoted: a_\_z_b\*c
7Quoted =: a_\_\z_b\*c 7Quoted =: a_\_z_b\*c
8Pattern: double backslash and star: "replace backslash and everything after it" 8Pattern: double backslash and star: "replace backslash and everything after it"
9Unquoted: a*b_\_z_ 9Unquoted: a*b_\_z_
10Unquoted =: a*b_\_z_ 10Unquoted =: a*b_\_z_
11Quoted: a*b_\_\z_ 11Quoted: a*b_\_z_
12Quoted =: a*b_\_\z_ 12Quoted =: a*b_\_z_
13 13
14Source: a\bc 14Source: a\bc
15Replace str: _\\_\z_ 15Replace str: _\\_\z_
16Pattern: single backslash and b: "replace b" 16Pattern: single backslash and b: "replace b"
17Unquoted: a\_\_z_c 17Unquoted: a\_\_z_c
18Unquoted =: a\_\_z_c 18Unquoted =: a\_\_z_c
19Quoted: a\_\_\z_c 19Quoted: a\_\_z_c
20Quoted =: a\_\_\z_c 20Quoted =: a\_\_z_c
21Pattern: double backslash and b: "replace backslash and b" 21Pattern: double backslash and b: "replace backslash and b"
22Unquoted: a_\_z_c 22Unquoted: a_\_z_c
23Unquoted =: a_\_z_c 23Unquoted =: a_\_z_c
24Quoted: a_\_\z_c 24Quoted: a_\_z_c
25Quoted =: a_\_\z_c 25Quoted =: a_\_z_c
26 26
27Source: a\bc 27Source: a\bc
28Replace str: _\\_\z_ (as variable $s) 28Replace str: _\\_\z_ (as variable $s)
diff --git a/shell/ash_test/ash-vars/var_bash6.right b/shell/ash_test/ash-vars/var_bash6.right
index 63fc23df8..115ff8b04 100644
--- a/shell/ash_test/ash-vars/var_bash6.right
+++ b/shell/ash_test/ash-vars/var_bash6.right
@@ -1,5 +1,5 @@
1Expected Actual 1Expected Actual
2a*z : a*z 2a*z : a*z
3\z : \z 3z : z
4a1z a2z: a1z a2z 4a1z a2z: a1z a2z
5z : z 5z : z
diff --git a/shell/ash_test/ash-vars/var_bash6.tests b/shell/ash_test/ash-vars/var_bash6.tests
index cf2e4f020..686834177 100755
--- a/shell/ash_test/ash-vars/var_bash6.tests
+++ b/shell/ash_test/ash-vars/var_bash6.tests
@@ -3,7 +3,7 @@
3>a1z; >a2z; 3>a1z; >a2z;
4 echo 'Expected' 'Actual' 4 echo 'Expected' 'Actual'
5v='a bz'; echo 'a*z :' "${v/a*z/a*z}" 5v='a bz'; echo 'a*z :' "${v/a*z/a*z}"
6v='a bz'; echo '\z :' "${v/a*z/\z}" 6v='a bz'; echo 'z :' "${v/a*z/\z}"
7v='a bz'; echo 'a1z a2z:' ${v/a*z/a*z} 7v='a bz'; echo 'a1z a2z:' ${v/a*z/a*z}
8v='a bz'; echo 'z :' ${v/a*z/\z} 8v='a bz'; echo 'z :' ${v/a*z/\z}
9rm a1z a2z 9rm a1z a2z
diff --git a/shell/ash_test/ash-vars/var_bash7.right b/shell/ash_test/ash-vars/var_bash7.right
new file mode 100644
index 000000000..223b7836f
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_bash7.right
@@ -0,0 +1 @@
B
diff --git a/shell/ash_test/ash-vars/var_bash7.tests b/shell/ash_test/ash-vars/var_bash7.tests
new file mode 100755
index 000000000..c4ce03f7f
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_bash7.tests
@@ -0,0 +1 @@
x=AB; echo "${x#$'\x41'}"
diff --git a/shell/hush_test/hush-arith/arith_nested1.right b/shell/hush_test/hush-arith/arith_nested1.right
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/shell/hush_test/hush-arith/arith_nested1.right
@@ -0,0 +1 @@
1
diff --git a/shell/hush_test/hush-arith/arith_nested1.tests b/shell/hush_test/hush-arith/arith_nested1.tests
new file mode 100755
index 000000000..28571b833
--- /dev/null
+++ b/shell/hush_test/hush-arith/arith_nested1.tests
@@ -0,0 +1 @@
echo $(( ( $((1)) ) ))
diff --git a/shell/hush_test/hush-quoting/squote_in_varexp3.right b/shell/hush_test/hush-quoting/squote_in_varexp3.right
new file mode 100644
index 000000000..223b7836f
--- /dev/null
+++ b/shell/hush_test/hush-quoting/squote_in_varexp3.right
@@ -0,0 +1 @@
B
diff --git a/shell/hush_test/hush-quoting/squote_in_varexp3.tests b/shell/hush_test/hush-quoting/squote_in_varexp3.tests
new file mode 100755
index 000000000..028a88fd9
--- /dev/null
+++ b/shell/hush_test/hush-quoting/squote_in_varexp3.tests
@@ -0,0 +1 @@
x=\'B; echo "${x#\'}"
diff --git a/shell/hush_test/hush-vars/var_bash3.right b/shell/hush_test/hush-vars/var_bash3.right
index a97c850ea..8899d981c 100644
--- a/shell/hush_test/hush-vars/var_bash3.right
+++ b/shell/hush_test/hush-vars/var_bash3.right
@@ -1,6 +1,6 @@
11 a041#c 11 a041#c
22 a041#c 22 a041#c
33 a\041#c 33 a041#c
44 a\041#c 44 a\041#c
55 a\041#c 55 a\041#c
66 a\041#c 66 a\041#c
@@ -17,4 +17,4 @@
1717 a\tc 1717 a\tc
1818 a\tc 1818 a\tc
1919 atc 1919 atc
2020 a\tc 2020 atc
diff --git a/shell/hush_test/hush-vars/var_bash4.right b/shell/hush_test/hush-vars/var_bash4.right
index 0ef1bf661..9067e58e6 100644
--- a/shell/hush_test/hush-vars/var_bash4.right
+++ b/shell/hush_test/hush-vars/var_bash4.right
@@ -3,26 +3,26 @@ Replace str: _\\_\z_
3Pattern: single backslash and star: "replace literal star" 3Pattern: single backslash and star: "replace literal star"
4Unquoted: a_\_z_b\*c 4Unquoted: a_\_z_b\*c
5Unquoted =: a_\_z_b\*c 5Unquoted =: a_\_z_b\*c
6Quoted: a_\_\z_b\*c 6Quoted: a_\_z_b\*c
7Quoted =: a_\_\z_b\*c 7Quoted =: a_\_z_b\*c
8Pattern: double backslash and star: "replace backslash and everything after it" 8Pattern: double backslash and star: "replace backslash and everything after it"
9Unquoted: a*b_\_z_ 9Unquoted: a*b_\_z_
10Unquoted =: a*b_\_z_ 10Unquoted =: a*b_\_z_
11Quoted: a*b_\_\z_ 11Quoted: a*b_\_z_
12Quoted =: a*b_\_\z_ 12Quoted =: a*b_\_z_
13 13
14Source: a\bc 14Source: a\bc
15Replace str: _\\_\z_ 15Replace str: _\\_\z_
16Pattern: single backslash and b: "replace b" 16Pattern: single backslash and b: "replace b"
17Unquoted: a\_\_z_c 17Unquoted: a\_\_z_c
18Unquoted =: a\_\_z_c 18Unquoted =: a\_\_z_c
19Quoted: a\_\_\z_c 19Quoted: a\_\_z_c
20Quoted =: a\_\_\z_c 20Quoted =: a\_\_z_c
21Pattern: double backslash and b: "replace backslash and b" 21Pattern: double backslash and b: "replace backslash and b"
22Unquoted: a_\_z_c 22Unquoted: a_\_z_c
23Unquoted =: a_\_z_c 23Unquoted =: a_\_z_c
24Quoted: a_\_\z_c 24Quoted: a_\_z_c
25Quoted =: a_\_\z_c 25Quoted =: a_\_z_c
26 26
27Source: a\bc 27Source: a\bc
28Replace str: _\\_\z_ (as variable $s) 28Replace str: _\\_\z_ (as variable $s)
diff --git a/shell/hush_test/hush-vars/var_bash6.right b/shell/hush_test/hush-vars/var_bash6.right
index 63fc23df8..115ff8b04 100644
--- a/shell/hush_test/hush-vars/var_bash6.right
+++ b/shell/hush_test/hush-vars/var_bash6.right
@@ -1,5 +1,5 @@
1Expected Actual 1Expected Actual
2a*z : a*z 2a*z : a*z
3\z : \z 3z : z
4a1z a2z: a1z a2z 4a1z a2z: a1z a2z
5z : z 5z : z
diff --git a/shell/hush_test/hush-vars/var_bash6.tests b/shell/hush_test/hush-vars/var_bash6.tests
index cf2e4f020..686834177 100755
--- a/shell/hush_test/hush-vars/var_bash6.tests
+++ b/shell/hush_test/hush-vars/var_bash6.tests
@@ -3,7 +3,7 @@
3>a1z; >a2z; 3>a1z; >a2z;
4 echo 'Expected' 'Actual' 4 echo 'Expected' 'Actual'
5v='a bz'; echo 'a*z :' "${v/a*z/a*z}" 5v='a bz'; echo 'a*z :' "${v/a*z/a*z}"
6v='a bz'; echo '\z :' "${v/a*z/\z}" 6v='a bz'; echo 'z :' "${v/a*z/\z}"
7v='a bz'; echo 'a1z a2z:' ${v/a*z/a*z} 7v='a bz'; echo 'a1z a2z:' ${v/a*z/a*z}
8v='a bz'; echo 'z :' ${v/a*z/\z} 8v='a bz'; echo 'z :' ${v/a*z/\z}
9rm a1z a2z 9rm a1z a2z