aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2016-10-26 01:55:56 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2016-10-26 01:55:56 +0200
commit88e15703acdbfb182440cf35fdb8972fc4931dd2 (patch)
treeabdeba10ffe2446c197bdb2d539fb1c4f3fb5277
parenteaf9436b08f68be8b96730307fe1da1faf374ee0 (diff)
downloadbusybox-w32-88e15703acdbfb182440cf35fdb8972fc4931dd2.tar.gz
busybox-w32-88e15703acdbfb182440cf35fdb8972fc4931dd2.tar.bz2
busybox-w32-88e15703acdbfb182440cf35fdb8972fc4931dd2.zip
ash: [PARSER] Report substition errors at expansion time
Upstreams commit: Date: Mon, 8 Oct 2007 21:32:25 +0800 [PARSER] Report substition errors at expansion time On Wed, Apr 11, 2007 at 01:24:21PM -0700, Micah Cowan wrote: > This operation fails on Ubuntu: > > $ /bin/sh -c 'if false; then d="${foo/bar}"; fi' > /bin/sh: Syntax error: Bad substitution > > When used with other POSIX shells it succeeds. While semantically the > variable reference ${foo/bar} is not valid, this is not a syntax error > according to POSIX, and since the variable assignment expression is > never invoked (because it's within an "if false") it should not be seen > as an error. > > I ran into this because after restarting my system I could no longer log > in. It turns out that the problem was (a) I had edited .gnomerc to > source my .bashrc file so that my environment would be set properly, and > (b) I had added some new code to my .bashrc WITHIN A CHECK FOR BASH! > that used bash's ${var/match/sub} feature. Even though this code was > within a "case $BASH_VERSION; in *[0-9]*) ... esac (so dash would never > execute it since that variable is not set), it still caused dash to > throw up. > > FYI, some relevant details from POSIX: > > Section 2.3, Token Recognition: > > 5. If the current character is an unquoted '$' or '`', the shell shall > identify the start of any candidates for parameter expansion ( Parameter > Expansion), command substitution ( Command Substitution), or arithmetic > expansion ( Arithmetic Expansion) from their introductory unquoted > character sequences: '$' or "${", "$(" or '`', and "$((", respectively. > The shell shall read sufficient input to determine the end of the unit > to be expanded (as explained in the cited sections). > > Section 2.6.2, Parameter Expansion: > > The format for parameter expansion is as follows: > > ${expression} > > where expression consists of all characters until the matching '}'. Any > '}' escaped by a backslash or within a quoted string, and characters in > embedded arithmetic expansions, command substitutions, and variable > expansions, shall not be examined in determining the matching '}'. > [...] > > The parameter name or symbol can be enclosed in braces, which are > optional except for positional parameters with more than one digit or > when parameter is followed by a character that could be interpreted as > part of the name. The matching closing brace shall be determined by > counting brace levels, skipping over enclosed quoted strings, and > command substitutions. > --- > In addition to bash I've checked Solaris /bin/sh and ksh and they don't > report an error. > > ----- > Micah Cowan: > > The applicable portion of POSIX is in XCU 2.10.1: > > "The WORD tokens shall have the word expansion rules applied to them > immediately before the associated command is executed, not at the time > the command is parsed." > > This seems fairly clear to me. This patch moves the error detection to expansion time. Test case: if false; then echo ${a!7} fi echo OK Old result: dash: Syntax error: Bad substitution New result: OK function old new delta evalvar 574 585 +11 readtoken1 2763 2750 -13 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/ash.c21
1 files changed, 12 insertions, 9 deletions
diff --git a/shell/ash.c b/shell/ash.c
index dc26bc142..e30d7fe67 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -6752,6 +6752,10 @@ evalvar(char *p, int flag, struct strlist *var_str_list)
6752 6752
6753 varflags = (unsigned char) *p++; 6753 varflags = (unsigned char) *p++;
6754 subtype = varflags & VSTYPE; 6754 subtype = varflags & VSTYPE;
6755
6756 if (!subtype)
6757 raise_error_syntax("bad substitution");
6758
6755 quoted = flag & EXP_QUOTED; 6759 quoted = flag & EXP_QUOTED;
6756 var = p; 6760 var = p;
6757 easy = (!quoted || (*var == '@' && shellparam.nparam)); 6761 easy = (!quoted || (*var == '@' && shellparam.nparam));
@@ -11699,7 +11703,7 @@ parseredir: {
11699parsesub: { 11703parsesub: {
11700 unsigned char subtype; 11704 unsigned char subtype;
11701 int typeloc; 11705 int typeloc;
11702 int flags; 11706 int flags = 0;
11703 11707
11704 c = pgetc_eatbnl(); 11708 c = pgetc_eatbnl();
11705 if (c > 255 /* PEOA or PEOF */ 11709 if (c > 255 /* PEOA or PEOF */
@@ -11759,15 +11763,13 @@ parsesub: {
11759 USTPUTC(c, out); 11763 USTPUTC(c, out);
11760 c = pgetc_eatbnl(); 11764 c = pgetc_eatbnl();
11761 } else { 11765 } else {
11762 badsub: 11766 goto badsub;
11763 raise_error_syntax("bad substitution");
11764 } 11767 }
11765 if (c != '}' && subtype == VSLENGTH) { 11768 if (c != '}' && subtype == VSLENGTH) {
11766 /* ${#VAR didn't end with } */ 11769 /* ${#VAR didn't end with } */
11767 goto badsub; 11770 goto badsub;
11768 } 11771 }
11769 11772
11770 STPUTC('=', out);
11771 flags = 0; 11773 flags = 0;
11772 if (subtype == 0) { 11774 if (subtype == 0) {
11773 static const char types[] ALIGN1 = "}-+?="; 11775 static const char types[] ALIGN1 = "}-+?=";
@@ -11784,7 +11786,7 @@ parsesub: {
11784 if (!strchr(types, c)) { 11786 if (!strchr(types, c)) {
11785 subtype = VSSUBSTR; 11787 subtype = VSSUBSTR;
11786 pungetc(); 11788 pungetc();
11787 break; /* "goto do_pungetc" is bigger (!) */ 11789 break; /* "goto badsub" is bigger (!) */
11788 } 11790 }
11789#endif 11791#endif
11790 flags = VSNUL; 11792 flags = VSNUL;
@@ -11792,7 +11794,7 @@ parsesub: {
11792 default: { 11794 default: {
11793 const char *p = strchr(types, c); 11795 const char *p = strchr(types, c);
11794 if (p == NULL) 11796 if (p == NULL)
11795 goto badsub; 11797 break;
11796 subtype = p - types + VSNORMAL; 11798 subtype = p - types + VSNORMAL;
11797 break; 11799 break;
11798 } 11800 }
@@ -11802,7 +11804,7 @@ parsesub: {
11802 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT); 11804 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
11803 c = pgetc_eatbnl(); 11805 c = pgetc_eatbnl();
11804 if (c != cc) 11806 if (c != cc)
11805 goto do_pungetc; 11807 goto badsub;
11806 subtype++; 11808 subtype++;
11807 break; 11809 break;
11808 } 11810 }
@@ -11814,13 +11816,13 @@ parsesub: {
11814 subtype = VSREPLACE; 11816 subtype = VSREPLACE;
11815 c = pgetc_eatbnl(); 11817 c = pgetc_eatbnl();
11816 if (c != '/') 11818 if (c != '/')
11817 goto do_pungetc; 11819 goto badsub;
11818 subtype++; /* VSREPLACEALL */ 11820 subtype++; /* VSREPLACEALL */
11819 break; 11821 break;
11820#endif 11822#endif
11821 } 11823 }
11822 } else { 11824 } else {
11823 do_pungetc: 11825 badsub:
11824 pungetc(); 11826 pungetc();
11825 } 11827 }
11826 ((unsigned char *)stackblock())[typeloc] = subtype | flags; 11828 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
@@ -11830,6 +11832,7 @@ parsesub: {
11830 dqvarnest++; 11832 dqvarnest++;
11831 } 11833 }
11832 } 11834 }
11835 STPUTC('=', out);
11833 } 11836 }
11834 goto parsesub_return; 11837 goto parsesub_return;
11835} 11838}