diff options
author | zeertzjq <zeertzjq@outlook.com> | 2023-04-15 17:51:39 +0800 |
---|---|---|
committer | zeertzjq <zeertzjq@outlook.com> | 2023-04-15 18:19:17 +0800 |
commit | ef9af89da753235c64cbd8b7d700c686bc94dad7 (patch) | |
tree | 70de460c6f4c421eab66cb202aebfa2b9a166875 /src | |
parent | bacb5021d4eff33c67eb659fb01125b2abcacd79 (diff) | |
download | rneovim-ef9af89da753235c64cbd8b7d700c686bc94dad7.tar.gz rneovim-ef9af89da753235c64cbd8b7d700c686bc94dad7.tar.bz2 rneovim-ef9af89da753235c64cbd8b7d700c686bc94dad7.zip |
vim-patch:8.2.4930: interpolated string expression requires escaping
Problem: Interpolated string expression requires escaping.
Solution: Do not require escaping in the expression.
https://github.com/vim/vim/commit/0abc2871c105882ed1c1effb9a7757fad8a395bd
Co-authored-by: Bram Moolenaar <Bram@vim.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/eval.c | 144 | ||||
-rw-r--r-- | src/nvim/eval/vars.c | 70 |
2 files changed, 150 insertions, 64 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f8a9326703..da345e4b53 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3063,12 +3063,12 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan // String constant: "string". case '"': - ret = eval_string(arg, rettv, evaluate); + ret = eval_string(arg, rettv, evaluate, false); break; // Literal string constant: 'str''ing'. case '\'': - ret = eval_lit_string(arg, rettv, evaluate); + ret = eval_lit_string(arg, rettv, evaluate, false); break; // List: [expr, expr] @@ -3868,16 +3868,20 @@ static int eval_number(char **arg, typval_T *rettv, bool evaluate, bool want_str return OK; } -/// Allocate a variable for a string constant. +/// Evaluate a string constant and put the result in "rettv". +/// "*arg" points to the double quote or to after it when "interpolate" is true. +/// When "interpolate" is true reduce "{{" to "{", reduce "}}" to "}" and stop +/// at a single "{". /// /// @return OK or FAIL. -static int eval_string(char **arg, typval_T *rettv, int evaluate) +static int eval_string(char **arg, typval_T *rettv, bool evaluate, bool interpolate) { char *p; - unsigned int extra = 0; + unsigned int extra = interpolate ? 1 : 0; + const int off = interpolate ? 0 : 1; // Find the end of the string, skipping backslashed characters. - for (p = *arg + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p)) { + for (p = *arg + off; *p != NUL && *p != '"'; MB_PTR_ADV(p)) { if (*p == '\\' && p[1] != NUL) { p++; // A "\<x>" form occupies at least 4 characters, and produces up @@ -3886,17 +3890,27 @@ static int eval_string(char **arg, typval_T *rettv, int evaluate) if (*p == '<') { extra += 5; } + } else if (interpolate && (*p == '{' || *p == '}')) { + if (*p == '{' && p[1] != '{') { // start of expression + break; + } + p++; + if (p[-1] == '}' && *p != '}') { // single '}' is an error + semsg(_(e_stray_closing_curly_str), *arg); + return FAIL; + } + extra--; // "{{" becomes "{", "}}" becomes "}" } } - if (*p != '"') { + if (*p != '"' && !(interpolate && *p == '{')) { semsg(_("E114: Missing quote: %s"), *arg); return FAIL; } // If only parsing, set *arg and return here if (!evaluate) { - *arg = p + 1; + *arg = p + off; return OK; } @@ -3907,7 +3921,7 @@ static int eval_string(char **arg, typval_T *rettv, int evaluate) rettv->vval.v_string = xmalloc((size_t)len); char *end = rettv->vval.v_string; - for (p = *arg + 1; *p != NUL && *p != '"';) { + for (p = *arg + off; *p != NUL && *p != '"';) { if (*p == '\\') { switch (*++p) { case 'b': @@ -3996,11 +4010,17 @@ static int eval_string(char **arg, typval_T *rettv, int evaluate) break; } } else { + if (interpolate && (*p == '{' || *p == '}')) { + if (*p == '{' && p[1] != '{') { // start of expression + break; + } + p++; // reduce "{{" to "{" and "}}" to "}" + } mb_copy_char((const char **)&p, &end); } } *end = NUL; - if (*p != NUL) { // just in case + if (*p == '"' && !interpolate) { p++; } *arg = p; @@ -4009,79 +4029,131 @@ static int eval_string(char **arg, typval_T *rettv, int evaluate) } /// Allocate a variable for a 'str''ing' constant. +/// When "interpolate" is true reduce "{{" to "{" and stop at a single "{". /// -/// @return OK or FAIL. -static int eval_lit_string(char **arg, typval_T *rettv, int evaluate) +/// @return OK when a "rettv" was set to the string. +/// FAIL on error, "rettv" is not set. +static int eval_lit_string(char **arg, typval_T *rettv, bool evaluate, bool interpolate) { char *p; - int reduce = 0; + int reduce = interpolate ? -1 : 0; + const int off = interpolate ? 0 : 1; // Find the end of the string, skipping ''. - for (p = *arg + 1; *p != NUL; MB_PTR_ADV(p)) { + for (p = *arg + off; *p != NUL; MB_PTR_ADV(p)) { if (*p == '\'') { if (p[1] != '\'') { break; } reduce++; p++; + } else if (interpolate) { + if (*p == '{') { + if (p[1] != '{') { + break; + } + p++; + reduce++; + } else if (*p == '}') { + p++; + if (*p != '}') { + semsg(_(e_stray_closing_curly_str), *arg); + return FAIL; + } + reduce++; + } } } - if (*p != '\'') { + if (*p != '\'' && !(interpolate && *p == '{')) { semsg(_("E115: Missing quote: %s"), *arg); return FAIL; } // If only parsing return after setting "*arg" if (!evaluate) { - *arg = p + 1; + *arg = p + off; return OK; } - // Copy the string into allocated memory, handling '' to ' reduction. + // Copy the string into allocated memory, handling '' to ' reduction and + // any expressions. char *str = xmalloc((size_t)((p - *arg) - reduce)); rettv->v_type = VAR_STRING; rettv->vval.v_string = str; - for (p = *arg + 1; *p != NUL;) { + for (p = *arg + off; *p != NUL;) { if (*p == '\'') { if (p[1] != '\'') { break; } p++; + } else if (interpolate && (*p == '{' || *p == '}')) { + if (*p == '{' && p[1] != '{') { + break; + } + p++; } mb_copy_char((const char **)&p, &str); } *str = NUL; - *arg = p + 1; + *arg = p + off; return OK; } -int eval_interp_string(char **arg, typval_T *rettv, int evaluate) +/// Evaluate a single or double quoted string possibly containing expressions. +/// "arg" points to the '$'. The result is put in "rettv". +/// +/// @return OK or FAIL. +int eval_interp_string(char **arg, typval_T *rettv, bool evaluate) { - // *arg is on the '$' character. + int ret = OK; + + garray_T ga; + ga_init(&ga, 1, 80); + + // *arg is on the '$' character, move it to the first string character. + (*arg)++; + const int quote = (uint8_t)(**arg); (*arg)++; - rettv->v_type = VAR_STRING; + for (;;) { + typval_T tv; + // Get the string up to the matching quote or to a single '{'. + // "arg" is advanced to either the quote or the '{'. + if (quote == '"') { + ret = eval_string(arg, &tv, evaluate, true); + } else { + ret = eval_lit_string(arg, &tv, evaluate, true); + } + if (ret == FAIL) { + break; + } + if (evaluate) { + ga_concat(&ga, tv.vval.v_string); + tv_clear(&tv); + } - typval_T tv; - int ret; - if (**arg == '"') { - ret = eval_string(arg, &tv, evaluate); - } else { - ret = eval_lit_string(arg, &tv, evaluate); + if (**arg != '{') { + // found terminating quote + (*arg)++; + break; + } + char *p = eval_one_expr_in_str(*arg, &ga); + if (p == NULL) { + ret = FAIL; + break; + } + *arg = p; } - if (ret == FAIL || !evaluate) { - return ret; + rettv->v_type = VAR_STRING; + if (ret != FAIL && evaluate) { + ga_append(&ga, NUL); } - - rettv->vval.v_string = eval_all_expr_in_str(tv.vval.v_string); - - tv_clear(&tv); - - return rettv->vval.v_string != NULL ? OK : FAIL; + rettv->vval.v_string = ga.ga_data; + return OK; } /// @return the function name of the partial. diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index b86c49fd98..7ae7b5a57a 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -53,8 +53,42 @@ static const char *e_letunexp = N_("E18: Unexpected characters in :let"); static const char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); -/// Evaluate all the Vim expressions ({expr}) in string "str" and return the -/// resulting string. The caller must free the returned string. +/// Evaluate one Vim expression {expr} in string "p" and append the +/// resulting string to "gap". "p" points to the opening "{". +/// Return a pointer to the character after "}", NULL for an error. +char *eval_one_expr_in_str(char *p, garray_T *gap) +{ + char *block_start = skipwhite(p + 1); // skip the opening { + char *block_end = block_start; + + if (*block_start == NUL) { + semsg(_(e_missing_close_curly_str), p); + return NULL; + } + if (skip_expr(&block_end, NULL) == FAIL) { + return NULL; + } + block_end = skipwhite(block_end); + if (*block_end != '}') { + semsg(_(e_missing_close_curly_str), p); + return NULL; + } + *block_end = NUL; + char *expr_val = eval_to_string(block_start, true); + *block_end = '}'; + if (expr_val == NULL) { + return NULL; + } + ga_concat(gap, expr_val); + xfree(expr_val); + + return block_end + 1; +} + +/// Evaluate all the Vim expressions {expr} in "str" and return the resulting +/// string in allocated memory. "{{" is reduced to "{" and "}}" to "}". +/// Used for a heredoc assignment. +/// Returns NULL for an error. char *eval_all_expr_in_str(char *str) { garray_T ga; @@ -67,13 +101,13 @@ char *eval_all_expr_in_str(char *str) // Look for a block start. char *lit_start = p; while (*p != '{' && *p != '}' && *p != NUL) { - ++p; + p++; } if (*p != NUL && *p == p[1]) { // Escaped brace, unescape and continue. // Include the brace in the literal string. - ++p; + p++; escaped_brace = true; } else if (*p == '}') { semsg(_(e_stray_closing_curly_str), str); @@ -90,36 +124,16 @@ char *eval_all_expr_in_str(char *str) if (escaped_brace) { // Skip the second brace. - ++p; + p++; continue; } - // Skip the opening {. - char *block_start = ++p; - char *block_end = block_start; - if (*block_start != NUL && skip_expr(&block_end, NULL) == FAIL) { - ga_clear(&ga); - return NULL; - } - block_end = skipwhite(block_end); - // The block must be closed by a }. - if (*block_end != '}') { - semsg(_(e_missing_close_curly_str), str); + // Evaluate the expression and append the result. + p = eval_one_expr_in_str(p, &ga); + if (p == NULL) { ga_clear(&ga); return NULL; } - char save_c = *block_end; - *block_end = NUL; - char *expr_val = eval_to_string(block_start, true); - *block_end = save_c; - if (expr_val == NULL) { - ga_clear(&ga); - return NULL; - } - ga_concat(&ga, expr_val); - xfree(expr_val); - - p = block_end + 1; } ga_append(&ga, NUL); |