Mercurial > vim
diff src/typval.c @ 28813:3626ca6a20ea v8.2.4930
patch 8.2.4930: interpolated string expression requires escaping
Commit: https://github.com/vim/vim/commit/0abc2871c105882ed1c1effb9a7757fad8a395bd
Author: Bram Moolenaar <Bram@vim.org>
Date: Tue May 10 13:24:30 2022 +0100
patch 8.2.4930: interpolated string expression requires escaping
Problem: Interpolated string expression requires escaping.
Solution: Do not require escaping in the expression.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Tue, 10 May 2022 14:30:04 +0200 |
parents | d0241e74bfdb |
children | 006d525419fa |
line wrap: on
line diff
--- a/src/typval.c +++ b/src/typval.c @@ -2065,19 +2065,23 @@ eval_number( } /* - * 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. */ int -eval_string(char_u **arg, typval_T *rettv, int evaluate) +eval_string(char_u **arg, typval_T *rettv, int evaluate, int interpolate) { char_u *p; char_u *end; - int extra = 0; + int extra = interpolate ? 1 : 0; + int off = interpolate ? 0 : 1; int len; // 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) { @@ -2088,9 +2092,21 @@ eval_string(char_u **arg, typval_T *rett 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(_(e_missing_double_quote_str), *arg); return FAIL; @@ -2099,7 +2115,7 @@ eval_string(char_u **arg, typval_T *rett // If only parsing, set *arg and return here if (!evaluate) { - *arg = p + 1; + *arg = p + off; return OK; } @@ -2112,7 +2128,7 @@ eval_string(char_u **arg, typval_T *rett return FAIL; end = rettv->vval.v_string; - for (p = *arg + 1; *p != NUL && *p != '"'; ) + for (p = *arg + off; *p != NUL && *p != '"'; ) { if (*p == '\\') { @@ -2192,15 +2208,23 @@ eval_string(char_u **arg, typval_T *rett } // FALLTHROUGH - default: MB_COPY_CHAR(p, end); + default: MB_COPY_CHAR(p, end); break; } } else + { + if (interpolate && (*p == '{' || *p == '}')) + { + if (*p == '{' && p[1] != '{') // start of expression + break; + ++p; // reduce "{{" to "{" and "}}" to "}" + } MB_COPY_CHAR(p, end); + } } *end = NUL; - if (*p != NUL) // just in case + if (*p == '"' && !interpolate) ++p; *arg = p; @@ -2209,17 +2233,20 @@ eval_string(char_u **arg, typval_T *rett /* * Allocate a variable for a 'str''ing' constant. - * Return OK or FAIL. + * When "interpolate" is TRUE reduce "{{" to "{" and stop at a single "{". + * Return OK when a "rettv" was set to the string. + * Return FAIL on error, "rettv" is not set. */ int -eval_lit_string(char_u **arg, typval_T *rettv, int evaluate) +eval_lit_string(char_u **arg, typval_T *rettv, int evaluate, int interpolate) { char_u *p; char_u *str; - int reduce = 0; + int reduce = interpolate ? -1 : 0; + 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 == '\'') { @@ -2228,9 +2255,29 @@ eval_lit_string(char_u **arg, typval_T * ++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(_(e_missing_single_quote_str), *arg); return FAIL; @@ -2239,18 +2286,19 @@ eval_lit_string(char_u **arg, typval_T * // 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. str = alloc((p - *arg) - reduce); if (str == NULL) return FAIL; rettv->v_type = VAR_STRING; rettv->vval.v_string = str; - for (p = *arg + 1; *p != NUL; ) + for (p = *arg + off; *p != NUL; ) { if (*p == '\'') { @@ -2258,38 +2306,82 @@ eval_lit_string(char_u **arg, typval_T * break; ++p; } + else if (interpolate && (*p == '{' || *p == '}')) + { + if (*p == '{' && p[1] != '{') + break; + ++p; + } MB_COPY_CHAR(p, str); } *str = NUL; - *arg = p + 1; + *arg = p + off; return OK; } +/* + * Evaluate a single or double quoted string possibly containing expressions. + * "arg" points to the '$'. The result is put in "rettv". + * Returns OK or FAIL. + */ int eval_interp_string(char_u **arg, typval_T *rettv, int evaluate) { typval_T tv; - int ret; + int ret = OK; + int quote; + garray_T ga; + char_u *p; + + ga_init2(&ga, 1, 80); + + // *arg is on the '$' character, move it to the first string character. + ++*arg; + quote = **arg; + ++*arg; - // *arg is on the '$' character. - (*arg)++; + for (;;) + { + // 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); + clear_tv(&tv); + } + + if (**arg != '{') + { + // found terminating quote + ++*arg; + break; + } + p = eval_one_expr_in_str(*arg, &ga); + if (p == NULL) + { + ret = FAIL; + break; + } + *arg = p; + } rettv->v_type = VAR_STRING; - - if (**arg == '"') - ret = eval_string(arg, &tv, evaluate); - else - ret = eval_lit_string(arg, &tv, evaluate); + if (ret == FAIL || !evaluate || ga_append(&ga, NUL) == FAIL) + { + ga_clear(&ga); + rettv->vval.v_string = NULL; + return ret; + } - if (ret == FAIL || !evaluate) - return ret; - - rettv->vval.v_string = eval_all_expr_in_str(tv.vval.v_string); - - clear_tv(&tv); - - return rettv->vval.v_string != NULL ? OK : FAIL; + rettv->vval.v_string = ga.ga_data; + return OK; } /*