diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2016-09-29 17:17:04 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2016-09-29 17:17:04 +0200 |
commit | 73c3e074df4de03ba1bebce09c130c8950ea5fe4 (patch) | |
tree | 68b486c969165596a922184a0dcdd59c53438589 | |
parent | 8286513838beaf9ccaab15bb5905248c3b7b8a69 (diff) | |
download | busybox-w32-73c3e074df4de03ba1bebce09c130c8950ea5fe4.tar.gz busybox-w32-73c3e074df4de03ba1bebce09c130c8950ea5fe4.tar.bz2 busybox-w32-73c3e074df4de03ba1bebce09c130c8950ea5fe4.zip |
ash: [PARSER] Handle backslash newlines properly after dollar sign
Fixes var_unbackslash1.tests failure.
Upstream commit:
[PARSER] Handle backslash newlines properly after dollar sign
On Tue, Aug 26, 2014 at 12:34:42PM +0000, Eric Blake wrote:
> On 08/26/2014 06:15 AM, Oleg Bulatov wrote:
> > While playing with sh generators I found that dash and bash have different
> > interpretations for <slash><newline> sequence.
> >
> > $ dash -c 'EDIT=xxx; echo $EDIT\
> >> OR'
> > xxxOR
>
> Buggy.
> >
> > $ dash -c 'echo "$\
> > (pwd)"'
> > $(pwd)
> >
> > Is it undefined behaviour in POSIX?
>
> No, it's well-defined, and dash is buggy.
...
I agree. This patch should resolve this problem and similar ones
affecting blackslash newlines after we encounter a dollar sign.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | shell/ash.c | 44 |
1 files changed, 29 insertions, 15 deletions
diff --git a/shell/ash.c b/shell/ash.c index a63d40d7e..b71fc0299 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -9774,11 +9774,6 @@ popstring(void) | |||
9774 | INT_ON; | 9774 | INT_ON; |
9775 | } | 9775 | } |
9776 | 9776 | ||
9777 | //FIXME: BASH_COMPAT with "...&" does TWO pungetc(): | ||
9778 | //it peeks whether it is &>, and then pushes back both chars. | ||
9779 | //This function needs to save last *next_to_pgetc to buf[0] | ||
9780 | //to make two pungetc() reliable. Currently, | ||
9781 | // pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work... | ||
9782 | static int | 9777 | static int |
9783 | preadfd(void) | 9778 | preadfd(void) |
9784 | { | 9779 | { |
@@ -10037,6 +10032,25 @@ pungetc(void) | |||
10037 | g_parsefile->unget++; | 10032 | g_parsefile->unget++; |
10038 | } | 10033 | } |
10039 | 10034 | ||
10035 | /* This one eats backslash+newline */ | ||
10036 | static int | ||
10037 | pgetc_eatbnl(void) | ||
10038 | { | ||
10039 | int c; | ||
10040 | |||
10041 | while ((c = pgetc()) == '\\') { | ||
10042 | if (pgetc() != '\n') { | ||
10043 | pungetc(); | ||
10044 | break; | ||
10045 | } | ||
10046 | |||
10047 | g_parsefile->linno++; | ||
10048 | setprompt_if(doprompt, 2); | ||
10049 | } | ||
10050 | |||
10051 | return c; | ||
10052 | } | ||
10053 | |||
10040 | /* | 10054 | /* |
10041 | * To handle the "." command, a stack of input files is used. Pushfile | 10055 | * To handle the "." command, a stack of input files is used. Pushfile |
10042 | * adds a new entry to the stack and popfile restores the previous level. | 10056 | * adds a new entry to the stack and popfile restores the previous level. |
@@ -11625,7 +11639,7 @@ parsesub: { | |||
11625 | int typeloc; | 11639 | int typeloc; |
11626 | int flags; | 11640 | int flags; |
11627 | 11641 | ||
11628 | c = pgetc(); | 11642 | c = pgetc_eatbnl(); |
11629 | if (c > 255 /* PEOA or PEOF */ | 11643 | if (c > 255 /* PEOA or PEOF */ |
11630 | || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) | 11644 | || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) |
11631 | ) { | 11645 | ) { |
@@ -11638,7 +11652,7 @@ parsesub: { | |||
11638 | pungetc(); | 11652 | pungetc(); |
11639 | } else if (c == '(') { | 11653 | } else if (c == '(') { |
11640 | /* $(command) or $((arith)) */ | 11654 | /* $(command) or $((arith)) */ |
11641 | if (pgetc() == '(') { | 11655 | if (pgetc_eatbnl() == '(') { |
11642 | #if ENABLE_SH_MATH_SUPPORT | 11656 | #if ENABLE_SH_MATH_SUPPORT |
11643 | PARSEARITH(); | 11657 | PARSEARITH(); |
11644 | #else | 11658 | #else |
@@ -11655,9 +11669,9 @@ parsesub: { | |||
11655 | USTPUTC(VSNORMAL, out); | 11669 | USTPUTC(VSNORMAL, out); |
11656 | subtype = VSNORMAL; | 11670 | subtype = VSNORMAL; |
11657 | if (c == '{') { | 11671 | if (c == '{') { |
11658 | c = pgetc(); | 11672 | c = pgetc_eatbnl(); |
11659 | if (c == '#') { | 11673 | if (c == '#') { |
11660 | c = pgetc(); | 11674 | c = pgetc_eatbnl(); |
11661 | if (c == '}') | 11675 | if (c == '}') |
11662 | c = '#'; /* ${#} - same as $# */ | 11676 | c = '#'; /* ${#} - same as $# */ |
11663 | else | 11677 | else |
@@ -11670,18 +11684,18 @@ parsesub: { | |||
11670 | /* $[{[#]]NAME[}] */ | 11684 | /* $[{[#]]NAME[}] */ |
11671 | do { | 11685 | do { |
11672 | STPUTC(c, out); | 11686 | STPUTC(c, out); |
11673 | c = pgetc(); | 11687 | c = pgetc_eatbnl(); |
11674 | } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c)); | 11688 | } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c)); |
11675 | } else if (isdigit(c)) { | 11689 | } else if (isdigit(c)) { |
11676 | /* $[{[#]]NUM[}] */ | 11690 | /* $[{[#]]NUM[}] */ |
11677 | do { | 11691 | do { |
11678 | STPUTC(c, out); | 11692 | STPUTC(c, out); |
11679 | c = pgetc(); | 11693 | c = pgetc_eatbnl(); |
11680 | } while (isdigit(c)); | 11694 | } while (isdigit(c)); |
11681 | } else if (is_special(c)) { | 11695 | } else if (is_special(c)) { |
11682 | /* $[{[#]]<specialchar>[}] */ | 11696 | /* $[{[#]]<specialchar>[}] */ |
11683 | USTPUTC(c, out); | 11697 | USTPUTC(c, out); |
11684 | c = pgetc(); | 11698 | c = pgetc_eatbnl(); |
11685 | } else { | 11699 | } else { |
11686 | badsub: | 11700 | badsub: |
11687 | raise_error_syntax("bad substitution"); | 11701 | raise_error_syntax("bad substitution"); |
@@ -11699,7 +11713,7 @@ parsesub: { | |||
11699 | /* c == first char after VAR */ | 11713 | /* c == first char after VAR */ |
11700 | switch (c) { | 11714 | switch (c) { |
11701 | case ':': | 11715 | case ':': |
11702 | c = pgetc(); | 11716 | c = pgetc_eatbnl(); |
11703 | #if ENABLE_ASH_BASH_COMPAT | 11717 | #if ENABLE_ASH_BASH_COMPAT |
11704 | /* This check is only needed to not misinterpret | 11718 | /* This check is only needed to not misinterpret |
11705 | * ${VAR:-WORD}, ${VAR:+WORD}, ${VAR:=WORD}, ${VAR:?WORD} | 11719 | * ${VAR:-WORD}, ${VAR:+WORD}, ${VAR:=WORD}, ${VAR:?WORD} |
@@ -11724,7 +11738,7 @@ parsesub: { | |||
11724 | case '#': { | 11738 | case '#': { |
11725 | int cc = c; | 11739 | int cc = c; |
11726 | subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT); | 11740 | subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT); |
11727 | c = pgetc(); | 11741 | c = pgetc_eatbnl(); |
11728 | if (c != cc) | 11742 | if (c != cc) |
11729 | goto do_pungetc; | 11743 | goto do_pungetc; |
11730 | subtype++; | 11744 | subtype++; |
@@ -11736,7 +11750,7 @@ parsesub: { | |||
11736 | //TODO: encode pattern and repl separately. | 11750 | //TODO: encode pattern and repl separately. |
11737 | // Currently ${v/$var_with_slash/repl} is horribly broken | 11751 | // Currently ${v/$var_with_slash/repl} is horribly broken |
11738 | subtype = VSREPLACE; | 11752 | subtype = VSREPLACE; |
11739 | c = pgetc(); | 11753 | c = pgetc_eatbnl(); |
11740 | if (c != '/') | 11754 | if (c != '/') |
11741 | goto do_pungetc; | 11755 | goto do_pungetc; |
11742 | subtype++; /* VSREPLACEALL */ | 11756 | subtype++; /* VSREPLACEALL */ |