From b278d82c61ab5125b5c7e350b264c1f3d52d682b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 26 Jul 2021 15:29:13 +0200 Subject: hush: implement $'str' bashism function old new delta parse_dollar_squote - 441 +441 encode_then_expand_vararg 359 380 +21 parse_stream 2252 2271 +19 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 2/0 up/down: 481/0) Total: 481 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 103 ++++++++++++++++++++- .../hush-quoting/dollar_squote_bash1.right | 10 ++ .../hush-quoting/dollar_squote_bash1.tests | 8 ++ .../hush-quoting/dollar_squote_bash2.right | 6 ++ .../hush-quoting/dollar_squote_bash2.tests | 10 ++ shell/hush_test/hush-vars/var_bash7.right | 1 + shell/hush_test/hush-vars/var_bash7.tests | 1 + 7 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 shell/hush_test/hush-quoting/dollar_squote_bash1.right create mode 100755 shell/hush_test/hush-quoting/dollar_squote_bash1.tests create mode 100644 shell/hush_test/hush-quoting/dollar_squote_bash2.right create mode 100755 shell/hush_test/hush-quoting/dollar_squote_bash2.tests create mode 100644 shell/hush_test/hush-vars/var_bash7.right create mode 100755 shell/hush_test/hush-vars/var_bash7.tests diff --git a/shell/hush.c b/shell/hush.c index 1aa0a40..af6a9a7 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -384,6 +384,7 @@ #define BASH_PATTERN_SUBST ENABLE_HUSH_BASH_COMPAT #define BASH_SUBSTR ENABLE_HUSH_BASH_COMPAT #define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT +#define BASH_DOLLAR_SQUOTE ENABLE_HUSH_BASH_COMPAT #define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT #define BASH_EPOCH_VARS ENABLE_HUSH_BASH_COMPAT #define BASH_TEST2 (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST) @@ -4919,6 +4920,100 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign } #endif /* ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS */ +#if BASH_DOLLAR_SQUOTE +/* Return code: 1 for "found and parsed", 0 for "seen something else" */ +#if BB_MMU +#define parse_dollar_squote(as_string, dest, input) \ + parse_dollar_squote(dest, input) +#define as_string NULL +#endif +static int parse_dollar_squote(o_string *as_string, o_string *dest, struct in_str *input) +{ + int start; + int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */ + debug_printf_parse("parse_dollar_squote entered: ch='%c'\n", ch); + if (ch != '\'') + return 0; + + dest->has_quoted_part = 1; + start = dest->length; + + ch = i_getch(input); /* eat ' */ + nommu_addchr(as_string, ch); + while (1) { + ch = i_getch(input); + nommu_addchr(as_string, ch); + if (ch == EOF) { + syntax_error_unterm_ch('\''); + return 0; + } + if (ch == '\'') + break; + if (ch == SPECIAL_VAR_SYMBOL) { + /* Convert raw ^C to corresponding special variable reference */ + o_addchr(dest, SPECIAL_VAR_SYMBOL); + o_addchr(dest, SPECIAL_VAR_QUOTED_SVS); + /* will addchr() another SPECIAL_VAR_SYMBOL (see after the if() block) */ + } else if (ch == '\\') { + static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567"; + + ch = i_getch(input); + nommu_addchr(as_string, ch); + if (strchr(C_escapes, ch)) { + char buf[4]; + char *p = buf; + int cnt = 2; + + buf[0] = ch; + if ((unsigned char)(ch - '0') <= 7) { /* \ooo */ + do { + ch = i_peek(input); + if ((unsigned char)(ch - '0') > 7) + break; + *++p = ch = i_getch(input); + nommu_addchr(as_string, ch); + } while (--cnt != 0); + } else if (ch == 'x') { /* \xHH */ + do { + ch = i_peek(input); + if (!isxdigit(ch)) + break; + *++p = ch = i_getch(input); + nommu_addchr(as_string, ch); + } while (--cnt != 0); + if (cnt == 2) { /* \x but next char is "bad" */ + ch = 'x'; + goto unrecognized; + } + } /* else simple seq like \\ or \t */ + *++p = '\0'; + p = buf; + ch = bb_process_escape_sequence((void*)&p); + //bb_error_msg("buf:'%s' ch:%x", buf, ch); + if (ch == '\0') + continue; /* bash compat: $'...\0...' emits nothing */ + } else { /* unrecognized "\z": encode both chars unless ' or " */ + if (ch != '\'' && ch != '"') { + unrecognized: + o_addqchr(dest, '\\'); + } + } + } /* if (\...) */ + o_addqchr(dest, ch); + } + + if (dest->length == start) { + /* $'', $'\0', $'\000\x00' and the like */ + o_addchr(dest, SPECIAL_VAR_SYMBOL); + o_addchr(dest, SPECIAL_VAR_SYMBOL); + } + + return 1; +} +#else +# #define parse_dollar_squote(as_string, dest, input) 0 +#endif /* BASH_DOLLAR_SQUOTE */ + /* Return code: 0 for OK, 1 for syntax error */ #if BB_MMU #define parse_dollar(as_string, dest, input, quote_mask) \ @@ -4931,7 +5026,7 @@ static int parse_dollar(o_string *as_string, { int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */ - debug_printf_parse("parse_dollar entered: ch='%c'\n", ch); + debug_printf_parse("parse_dollar entered: ch='%c' quote_mask:0x%x\n", ch, quote_mask); if (isalpha(ch)) { make_var: ch = i_getch(input); @@ -5247,6 +5342,8 @@ static int encode_string(o_string *as_string, goto again; } if (ch == '$') { + //if (parse_dollar_squote(as_string, dest, input)) + // goto again; if (!parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80)) { debug_printf_parse("encode_string return 0: " "parse_dollar returned 0 (error)\n"); @@ -5723,6 +5820,8 @@ static struct pipe *parse_stream(char **pstring, o_addchr(&ctx.word, ch); continue; /* get next char */ case '$': + if (parse_dollar_squote(&ctx.as_string, &ctx.word, input)) + continue; /* get next char */ if (!parse_dollar(&ctx.as_string, &ctx.word, input, /*quote_mask:*/ 0)) { debug_printf_parse("parse_stream parse error: " "parse_dollar returned 0 (error)\n"); @@ -6166,6 +6265,8 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int continue; } if (ch == '$') { + if (parse_dollar_squote(NULL, &dest, &input)) + continue; if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ 0x80)) { debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__); goto ret; diff --git a/shell/hush_test/hush-quoting/dollar_squote_bash1.right b/shell/hush_test/hush-quoting/dollar_squote_bash1.right new file mode 100644 index 0000000..9f4e25e --- /dev/null +++ b/shell/hush_test/hush-quoting/dollar_squote_bash1.right @@ -0,0 +1,10 @@ +a b +$'a\tb' +a +b c +def +a'b c"d e\f +a3b c3b e33f +a\80b c08b +a3b c30b +x y diff --git a/shell/hush_test/hush-quoting/dollar_squote_bash1.tests b/shell/hush_test/hush-quoting/dollar_squote_bash1.tests new file mode 100755 index 0000000..6fc411b --- /dev/null +++ b/shell/hush_test/hush-quoting/dollar_squote_bash1.tests @@ -0,0 +1,8 @@ +echo $'a\tb' +echo "$'a\tb'" +echo $'a\nb' $'c\nd''ef' +echo $'a\'b' $'c\"d' $'e\\f' +echo $'a\63b' $'c\063b' $'e\0633f' +echo $'a\80b' $'c\608b' +echo $'a\x33b' $'c\x330b' +echo $'x\x9y' diff --git a/shell/hush_test/hush-quoting/dollar_squote_bash2.right b/shell/hush_test/hush-quoting/dollar_squote_bash2.right new file mode 100644 index 0000000..f7a1731 --- /dev/null +++ b/shell/hush_test/hush-quoting/dollar_squote_bash2.right @@ -0,0 +1,6 @@ +strstrstrstrstrstrstrstrstrstrstrstrstrstrstrstrstr +strstrstrstrstrstrstrstrstrstrstrstrstrstrstrstrstr +80:\ +81:\ +82:\ +Done:0 diff --git a/shell/hush_test/hush-quoting/dollar_squote_bash2.tests b/shell/hush_test/hush-quoting/dollar_squote_bash2.tests new file mode 100755 index 0000000..4497728 --- /dev/null +++ b/shell/hush_test/hush-quoting/dollar_squote_bash2.tests @@ -0,0 +1,10 @@ +# Embedded NULs +echo $'str\x00'strstrstrstrstrstrstrstrstrstrstrstrstrstrstrstr +echo $'str\000'strstrstrstrstrstrstrstrstrstrstrstrstrstrstrstr + +# The chars after '\' are hex 0x80,81,82... +echo 80:$'\' +echo 81:$'\' +echo 82:$'\' + +echo Done:$? diff --git a/shell/hush_test/hush-vars/var_bash7.right b/shell/hush_test/hush-vars/var_bash7.right new file mode 100644 index 0000000..223b783 --- /dev/null +++ b/shell/hush_test/hush-vars/var_bash7.right @@ -0,0 +1 @@ +B diff --git a/shell/hush_test/hush-vars/var_bash7.tests b/shell/hush_test/hush-vars/var_bash7.tests new file mode 100755 index 0000000..c4ce03f --- /dev/null +++ b/shell/hush_test/hush-vars/var_bash7.tests @@ -0,0 +1 @@ +x=AB; echo "${x#$'\x41'}" -- cgit v1.1