diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2016-10-26 01:55:56 +0200 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2016-10-26 01:55:56 +0200 |
| commit | 88e15703acdbfb182440cf35fdb8972fc4931dd2 (patch) | |
| tree | abdeba10ffe2446c197bdb2d539fb1c4f3fb5277 /shell | |
| parent | eaf9436b08f68be8b96730307fe1da1faf374ee0 (diff) | |
| download | busybox-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>
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/ash.c | 21 |
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: { | |||
| 11699 | parsesub: { | 11703 | parsesub: { |
| 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 | } |
