diff options
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r-- | src/nvim/eval.c | 65 |
1 files changed, 45 insertions, 20 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 245ad8d9d8..1200ba20ba 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -65,6 +65,7 @@ static char *e_nowhitespace = N_("E274: No white space allowed before parenthesis"); static char *e_write2 = N_("E80: Error while writing: %s"); static char *e_string_list_or_blob_required = N_("E1098: String, List or Blob required"); +static char e_expression_too_recursive_str[] = N_("E1169: Expression too recursive: %s"); static char * const namespace_char = "abglstvw"; @@ -132,8 +133,7 @@ static struct vimvar { char *vv_name; ///< Name of the variable, without v:. TV_DICTITEM_STRUCT(VIMVAR_KEY_LEN + 1) vv_di; ///< Value and name for key (max 16 chars). char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. -} vimvars[] = -{ +} vimvars[] = { // VV_ tails differing from upcased string literals: // VV_CC_FROM "charconvert_from" // VV_CC_TO "charconvert_to" @@ -489,7 +489,7 @@ void eval_clear(void) /// Set an internal variable to a string value. Creates the variable if it does /// not already exist. -void set_internal_string_var(const char *name, char *value) +void set_internal_string_var(const char *name, char *value) // NOLINT(readability-non-const-parameter) FUNC_ATTR_NONNULL_ARG(1) { typval_T tv = { @@ -545,7 +545,7 @@ int var_redir_start(char *name, int append) // check if we can write to the variable: set it to or append an empty // string - int save_emsg = did_emsg; + const int called_emsg_before = called_emsg; did_emsg = false; typval_T tv; tv.v_type = VAR_STRING; @@ -556,9 +556,7 @@ int var_redir_start(char *name, int append) set_var_lval(redir_lval, redir_endp, &tv, true, false, "="); } clear_lval(redir_lval); - int err = did_emsg; - did_emsg |= save_emsg; - if (err) { + if (called_emsg > called_emsg_before) { redir_endp = NULL; // don't store a value, only cleanup var_redir_stop(); return FAIL; @@ -1443,12 +1441,13 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const } wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE && tv_is_func(*rettv) - && !var_check_func_name((const char *)key, lp->ll_di == NULL)) + && var_wrong_func_name((const char *)key, lp->ll_di == NULL)) || !valid_varname((const char *)key)); if (len != -1) { key[len] = prevval; } if (wrong) { + tv_clear(&var1); return NULL; } } @@ -2184,7 +2183,7 @@ char *get_user_var_name(expand_T *xp, int idx) /// Does not use 'cpo' and always uses 'magic'. /// /// @return true if "pat" matches "text". -int pattern_match(char *pat, char *text, bool ic) +int pattern_match(const char *pat, const char *text, bool ic) { int matches = 0; regmatch_T regmatch; @@ -2192,7 +2191,7 @@ int pattern_match(char *pat, char *text, bool ic) // avoid 'l' flag in 'cpoptions' char *save_cpo = p_cpo; p_cpo = empty_option; - regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + regmatch.regprog = vim_regcomp((char *)pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { regmatch.rm_ic = ic; matches = vim_regexec_nl(®match, (char_u *)text, (colnr_T)0); @@ -2910,6 +2909,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) const char *start_leader, *end_leader; int ret = OK; char *alias; + static int recurse = 0; // Initialise variable so that tv_clear() can't mistake this for a // string and free a string that isn't there. @@ -2922,6 +2922,20 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) } end_leader = *arg; + // Limit recursion to 1000 levels. At least at 10000 we run out of stack + // and crash. With MSVC the stack is smaller. + if (recurse == +#ifdef _MSC_VER + 300 +#else + 1000 +#endif + ) { + semsg(_(e_expression_too_recursive_str), *arg); + return FAIL; + } + recurse++; + switch (**arg) { // Number constant. case '0': @@ -3035,7 +3049,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) case '#': if ((*arg)[1] == '{') { (*arg)++; - ret = dict_get_tv(arg, rettv, evaluate, true); + ret = eval_dict(arg, rettv, evaluate, true); } else { ret = NOTDONE; } @@ -3046,7 +3060,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) case '{': ret = get_lambda_tv(arg, rettv, evaluate); if (ret == NOTDONE) { - ret = dict_get_tv(arg, rettv, evaluate, false); + ret = eval_dict(arg, rettv, evaluate, false); } break; @@ -3126,6 +3140,8 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) if (ret == OK && evaluate && end_leader > start_leader) { ret = eval7_leader(rettv, (char *)start_leader, &end_leader); } + + recurse--; return ret; } @@ -4225,11 +4241,11 @@ bool garbage_collect(bool testing) // history items (ShaDa additional elements) if (p_hi) { - for (uint8_t i = 0; i < HIST_COUNT; i++) { + for (HistoryType i = 0; i < HIST_COUNT; i++) { const void *iter = NULL; do { histentry_T hist; - iter = hist_iter(iter, i, false, &hist); + iter = hist_iter(iter, (uint8_t)i, false, &hist); if (hist.hisstr != NULL) { ABORTING(set_ref_list)(hist.additional_elements, copyID); } @@ -4577,7 +4593,7 @@ static int get_literal_key(char **arg, typval_T *tv) /// "literal" is true for #{key: val} /// /// @return OK or FAIL. Returns NOTDONE for {expr}. -static int dict_get_tv(char **arg, typval_T *rettv, int evaluate, bool literal) +static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal) { typval_T tv; char *key = NULL; @@ -4785,20 +4801,20 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) int save_did_emsg; int idx = 0; + // Always return the first argument, also on failure. + tv_copy(&argvars[0], rettv); + if (argvars[0].v_type == VAR_BLOB) { - tv_copy(&argvars[0], rettv); if ((b = argvars[0].vval.v_blob) == NULL) { return; } } else if (argvars[0].v_type == VAR_LIST) { - tv_copy(&argvars[0], rettv); if ((l = argvars[0].vval.v_list) == NULL || (!map && var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE))) { return; } } else if (argvars[0].v_type == VAR_DICT) { - tv_copy(&argvars[0], rettv); if ((d = argvars[0].vval.v_dict) == NULL || (!map && var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { return; @@ -5184,6 +5200,7 @@ linenr_T tv_get_lnum_buf(const typval_T *const tv, const buf_T *const buf) if (tv->v_type == VAR_STRING && tv->vval.v_string != NULL && tv->vval.v_string[0] == '$' + && tv->vval.v_string[1] == NUL && buf != NULL) { return buf->b_ml.ml_line_count; } @@ -5563,6 +5580,13 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T const char *line = NULL; if (lines->v_type == VAR_LIST) { l = lines->vval.v_list; + if (l == NULL || tv_list_len(l) == 0) { + // set proper return code + if (lnum > curbuf->b_ml.ml_line_count) { + rettv->vval.v_number = 1; // FAIL + } + goto done; + } li = tv_list_first(l); } else { line = tv_get_string_chk(lines); @@ -5633,6 +5657,7 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T update_topline(curwin); } +done: if (!is_curbuf) { curbuf = curbuf_save; curwin = curwin_save; @@ -8311,7 +8336,7 @@ repeat: /// "flags" can be "g" to do a global substitute. /// /// @return an allocated string, NULL for error. -char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags) +char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char *flags) { int sublen; regmatch_T regmatch; @@ -8855,7 +8880,7 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic) case EXPR_MATCH: case EXPR_NOMATCH: - n1 = pattern_match((char *)s2, (char *)s1, ic); + n1 = pattern_match(s2, s1, ic); if (type == EXPR_NOMATCH) { n1 = !n1; } |