diff options
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r-- | src/nvim/eval.c | 1018 |
1 files changed, 390 insertions, 628 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ebc60ea5c7..4392ea306f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3,13 +3,19 @@ // eval.c: Expression evaluation. +#include <assert.h> +#include <ctype.h> +#include <inttypes.h> #include <math.h> +#include <stdio.h> #include <stdlib.h> +#include <string.h> +#include "auto/config.h" +#include "nvim/api/private/defs.h" #include "nvim/ascii.h" -#include "nvim/autocmd.h" #include "nvim/buffer.h" -#include "nvim/change.h" +#include "nvim/buffer_defs.h" #include "nvim/channel.h" #include "nvim/charset.h" #include "nvim/cmdhist.h" @@ -22,37 +28,58 @@ #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" +#include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" +#include "nvim/event/process.h" #include "nvim/ex_cmds.h" -#include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" #include "nvim/ex_session.h" +#include "nvim/garray.h" #include "nvim/getchar.h" +#include "nvim/gettext.h" +#include "nvim/globals.h" +#include "nvim/grid_defs.h" #include "nvim/highlight_group.h" +#include "nvim/insexpand.h" +#include "nvim/keycodes.h" +#include "nvim/lib/queue.h" #include "nvim/locale.h" #include "nvim/lua/executor.h" +#include "nvim/macros.h" +#include "nvim/main.h" +#include "nvim/map.h" #include "nvim/mark.h" +#include "nvim/mbyte.h" #include "nvim/memline.h" +#include "nvim/memory.h" +#include "nvim/message.h" #include "nvim/move.h" +#include "nvim/msgpack_rpc/channel_defs.h" #include "nvim/ops.h" #include "nvim/option.h" #include "nvim/optionstr.h" -#include "nvim/os/input.h" +#include "nvim/os/fileio.h" +#include "nvim/os/fs_defs.h" +#include "nvim/os/os.h" #include "nvim/os/shell.h" +#include "nvim/os/stdpaths_defs.h" #include "nvim/path.h" +#include "nvim/pos.h" #include "nvim/profile.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" #include "nvim/runtime.h" -#include "nvim/screen.h" #include "nvim/search.h" -#include "nvim/sign.h" -#include "nvim/syntax.h" +#include "nvim/strings.h" +#include "nvim/tag.h" +#include "nvim/types.h" #include "nvim/ui.h" #include "nvim/ui_compositor.h" -#include "nvim/undo.h" +#include "nvim/usercmd.h" #include "nvim/version.h" +#include "nvim/vim.h" #include "nvim/window.h" // TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead @@ -60,11 +87,15 @@ #define DICT_MAXNEST 100 // maximum nesting of lists and dicts static char *e_missbrac = N_("E111: Missing ']'"); +static char *e_list_end = N_("E697: Missing end of List ']': %s"); static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); 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 e_dot_can_only_be_used_on_dictionary_str[] + = N_("E1203: Dot can only be used on a dictionary: %s"); static char * const namespace_char = "abglstvw"; @@ -132,8 +163,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" @@ -237,6 +267,8 @@ static struct vimvar { VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO), VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO), + VV(VV_RELNUM, "relnum", VAR_NUMBER, VV_RO), + VV(VV_VIRTNUM, "virtnum", VAR_NUMBER, VV_RO), }; #undef VV @@ -330,6 +362,10 @@ varnumber_T num_divide(varnumber_T n1, varnumber_T n2) } else { result = VARNUMBER_MAX; } + } else if (n1 == VARNUMBER_MIN && n2 == -1) { + // specific case: trying to do VARNUMBAR_MIN / -1 results in a positive + // number that doesn't fit in varnumber_T and causes an FPE + result = VARNUMBER_MAX; } else { result = n1 / n2; } @@ -370,11 +406,11 @@ void eval_init(void) // add to v: scope dict, unless the value is not always available if (p->vv_type != VAR_UNKNOWN) { - hash_add(&vimvarht, p->vv_di.di_key); + hash_add(&vimvarht, (char *)p->vv_di.di_key); } if (p->vv_flags & VV_COMPAT) { // add to compat scope dict - hash_add(&compat_hashtab, p->vv_di.di_key); + hash_add(&compat_hashtab, (char *)p->vv_di.di_key); } } vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; @@ -485,7 +521,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 = { @@ -541,7 +577,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; @@ -552,9 +588,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; @@ -640,25 +674,6 @@ int eval_charconvert(const char *const enc_from, const char *const enc_to, return OK; } -int eval_printexpr(const char *const fname, const char *const args) -{ - bool err = false; - - set_vim_var_string(VV_FNAME_IN, fname, -1); - set_vim_var_string(VV_CMDARG, args, -1); - if (eval_to_bool(p_pexpr, &err, NULL, false)) { - err = true; - } - set_vim_var_string(VV_FNAME_IN, NULL, -1); - set_vim_var_string(VV_CMDARG, NULL, -1); - - if (err) { - os_remove(fname); - return FAIL; - } - return OK; -} - void eval_diff(const char *const origfile, const char *const newfile, const char *const outfile) { bool err = false; @@ -679,7 +694,7 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch set_vim_var_string(VV_FNAME_IN, origfile, -1); set_vim_var_string(VV_FNAME_DIFF, difffile, -1); set_vim_var_string(VV_FNAME_OUT, outfile, -1); - (void)eval_to_bool((char *)p_pex, &err, NULL, false); + (void)eval_to_bool(p_pex, &err, NULL, false); set_vim_var_string(VV_FNAME_IN, NULL, -1); set_vim_var_string(VV_FNAME_DIFF, NULL, -1); set_vim_var_string(VV_FNAME_OUT, NULL, -1); @@ -977,7 +992,7 @@ void prepare_vimvar(int idx, typval_T *save_tv) { *save_tv = vimvars[idx].vv_tv; if (vimvars[idx].vv_type == VAR_UNKNOWN) { - hash_add(&vimvarht, vimvars[idx].vv_di.di_key); + hash_add(&vimvarht, (char *)vimvars[idx].vv_di.di_key); } } @@ -986,24 +1001,15 @@ void prepare_vimvar(int idx, typval_T *save_tv) void restore_vimvar(int idx, typval_T *save_tv) { vimvars[idx].vv_tv = *save_tv; - if (vimvars[idx].vv_type == VAR_UNKNOWN) { - hashitem_T *hi = hash_find(&vimvarht, (char *)vimvars[idx].vv_di.di_key); - if (HASHITEM_EMPTY(hi)) { - internal_error("restore_vimvar()"); - } else { - hash_remove(&vimvarht, hi); - } + if (vimvars[idx].vv_type != VAR_UNKNOWN) { + return; } -} -/// If there is a window for "curbuf", make it the current window. -void find_win_for_curbuf(void) -{ - for (wininfo_T *wip = curbuf->b_wininfo; wip != NULL; wip = wip->wi_next) { - if (wip->wi_win != NULL) { - curwin = wip->wi_win; - break; - } + hashitem_T *hi = hash_find(&vimvarht, (char *)vimvars[idx].vv_di.di_key); + if (HASHITEM_EMPTY(hi)) { + internal_error("restore_vimvar()"); + } else { + hash_remove(&vimvarht, hi); } } @@ -1069,11 +1075,11 @@ int get_spellword(list_T *const list, const char **ret_word) return (int)tv_list_find_nr(list, -1, NULL); } -// Call some vim script function and return the result in "*rettv". -// Uses argv[0] to argv[argc-1] for the function arguments. argv[argc] -// should have type VAR_UNKNOWN. -// -// @return OK or FAIL. +/// Call some Vim script function and return the result in "*rettv". +/// Uses argv[0] to argv[argc - 1] for the function arguments. argv[argc] +/// should have type VAR_UNKNOWN. +/// +/// @return OK or FAIL. int call_vim_function(const char *func, int argc, typval_T *argv, typval_T *rettv) FUNC_ATTR_NONNULL_ALL { @@ -1106,26 +1112,10 @@ fail: return ret; } -/// Call Vim script function and return the result as a number -/// -/// @param[in] func Function name. -/// @param[in] argc Number of arguments. -/// @param[in] argv Array with typval_T arguments. -/// -/// @return -1 when calling function fails, result of function otherwise. -varnumber_T call_func_retnr(const char *func, int argc, typval_T *argv) - FUNC_ATTR_NONNULL_ALL -{ - typval_T rettv; - if (call_vim_function((char *)func, argc, argv, &rettv) == FAIL) { - return -1; - } - varnumber_T retval = tv_get_number_chk(&rettv, NULL); - tv_clear(&rettv); - return retval; -} -/// Call Vim script function and return the result as a string +/// Call Vim script function and return the result as a string. +/// Uses "argv[0]" to "argv[argc - 1]" for the function arguments. "argv[argc]" +/// should have type VAR_UNKNOWN. /// /// @param[in] func Function name. /// @param[in] argc Number of arguments. @@ -1147,7 +1137,9 @@ char *call_func_retstr(const char *const func, int argc, typval_T *argv) tv_clear(&rettv); return retval; } -/// Call Vim script function and return the result as a List + +/// Call Vim script function and return the result as a List. +/// Uses "argv" and "argc" as call_func_retstr(). /// /// @param[in] func Function name. /// @param[in] argc Number of arguments. @@ -1215,8 +1207,6 @@ int eval_foldexpr(char *arg, int *cp) return (int)retval; } -// TODO(ZyX-I): move to eval/executor - /// Get an lvalue /// /// Lvalue may be @@ -1311,13 +1301,26 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const return NULL; } - // Loop until no more [idx] or .key is following. lp->ll_tv = &v->di_tv; + + if (tv_is_luafunc(lp->ll_tv)) { + // For v:lua just return a pointer to the "." after the "v:lua". + // If the caller is trans_function_name() it will check for a Lua function name. + return p; + } + + // Loop until no more [idx] or .key is following. typval_T var1; var1.v_type = VAR_UNKNOWN; typval_T var2; var2.v_type = VAR_UNKNOWN; - while (*p == '[' || (*p == '.' && lp->ll_tv->v_type == VAR_DICT)) { + while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.')) { + if (*p == '.' && lp->ll_tv->v_type != VAR_DICT) { + if (!quiet) { + semsg(_(e_dot_can_only_be_used_on_dictionary_str), name); + } + return NULL; + } if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL) && !(lp->ll_tv->v_type == VAR_DICT && lp->ll_tv->vval.v_dict != NULL) && !(lp->ll_tv->v_type == VAR_BLOB && lp->ll_tv->vval.v_blob != NULL)) { @@ -1439,12 +1442,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; } } @@ -1481,9 +1485,9 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const tv_clear(&var1); break; // existing variable, need to check if it can be changed - } else if (!(flags & GLV_READ_ONLY) && var_check_ro(lp->ll_di->di_flags, - (const char *)name, - (size_t)(p - name))) { + } else if (!(flags & GLV_READ_ONLY) + && (var_check_ro(lp->ll_di->di_flags, name, (size_t)(p - name)) + || var_check_lock(lp->ll_di->di_flags, name, (size_t)(p - name)))) { tv_clear(&var1); return NULL; } @@ -1587,8 +1591,6 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const return p; } -// TODO(ZyX-I): move to eval/executor - /// Clear lval "lp" that was filled by get_lval(). void clear_lval(lval_T *lp) { @@ -1596,8 +1598,6 @@ void clear_lval(lval_T *lp) xfree(lp->ll_newkey); } -// TODO(ZyX-I): move to eval/executor - /// Set a variable that was parsed by get_lval() to "rettv". /// /// @param endp points to just after the parsed name. @@ -1618,7 +1618,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool semsg(_(e_letwrong), op); return; } - if (var_check_lock(lp->ll_blob->bv_lock, lp->ll_name, TV_CSTRING)) { + if (value_check_lock(lp->ll_blob->bv_lock, lp->ll_name, TV_CSTRING)) { return; } @@ -1648,7 +1648,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool // the end is an error otherwise. if (lp->ll_n1 < gap->ga_len || lp->ll_n1 == gap->ga_len) { ga_grow(&lp->ll_blob->bv_ga, 1); - tv_blob_set(lp->ll_blob, (int)lp->ll_n1, (char_u)val); + tv_blob_set(lp->ll_blob, (int)lp->ll_n1, (uint8_t)val); if (lp->ll_n1 == gap->ga_len) { gap->ga_len++; } @@ -1681,10 +1681,10 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool set_var_const(lp->ll_name, lp->ll_name_len, rettv, copy, is_const); } *endp = (char)cc; - } else if (var_check_lock(lp->ll_newkey == NULL - ? lp->ll_tv->v_lock - : lp->ll_tv->vval.v_dict->dv_lock, - lp->ll_name, TV_CSTRING)) { + } else if (value_check_lock(lp->ll_newkey == NULL + ? lp->ll_tv->v_lock + : lp->ll_tv->vval.v_dict->dv_lock, + lp->ll_name, TV_CSTRING)) { // Skip } else if (lp->ll_range) { listitem_T *ll_li = lp->ll_li; @@ -1698,8 +1698,8 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool // Check whether any of the list items is locked for (ri = tv_list_first(rettv->vval.v_list); ri != NULL && ll_li != NULL;) { - if (var_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, lp->ll_name, - TV_CSTRING)) { + if (value_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, lp->ll_name, + TV_CSTRING)) { return; } ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); @@ -1754,7 +1754,10 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool // Assign to a List or Dictionary item. if (lp->ll_newkey != NULL) { if (op != NULL && *op != '=') { - semsg(_(e_letwrong), op); + semsg(_(e_dictkey), lp->ll_newkey); + return; + } + if (tv_dict_wrong_func_name(lp->ll_tv->vval.v_dict, rettv, lp->ll_newkey)) { return; } @@ -1802,8 +1805,6 @@ notify: } } -// TODO(ZyX-I): move to eval/ex_cmds - /// Evaluate the expression used in a ":for var in expr" command. /// "arg" points to "var". /// @@ -1879,8 +1880,6 @@ void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip) return fi; } -// TODO(ZyX-I): move to eval/ex_cmds - /// Use the first item in a ":for" list. Advance to the next. /// Assign the values to the variable (list). "arg" points to the first one. /// @@ -1921,15 +1920,12 @@ bool next_for_item(void *fi_void, char *arg) listitem_T *item = fi->fi_lw.lw_item; if (item == NULL) { return false; - } else { - fi->fi_lw.lw_item = TV_LIST_ITEM_NEXT(fi->fi_list, item); - return (ex_let_vars(arg, TV_LIST_ITEM_TV(item), true, - fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK); } + fi->fi_lw.lw_item = TV_LIST_ITEM_NEXT(fi->fi_list, item); + return (ex_let_vars(arg, TV_LIST_ITEM_TV(item), true, + fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK); } -// TODO(ZyX-I): move to eval/ex_cmds - /// Free the structure used to store info used by ":for". void free_for_info(void *fi_void) { @@ -2059,7 +2055,7 @@ void del_menutrans_vars(void) { hash_lock(&globvarht); HASHTAB_ITER(&globvarht, hi, { - if (STRNCMP(hi->hi_key, "menutrans_", 10) == 0) { + if (strncmp(hi->hi_key, "menutrans_", 10) == 0) { delete_var(&globvarht, hi); } }); @@ -2117,10 +2113,10 @@ char *get_user_var_name(expand_T *xp, int idx) while (HASHITEM_EMPTY(hi)) { hi++; } - if (STRNCMP("g:", xp->xp_pattern, 2) == 0) { - return cat_prefix_varname('g', (char *)hi->hi_key); + if (strncmp("g:", xp->xp_pattern, 2) == 0) { + return cat_prefix_varname('g', hi->hi_key); } - return (char *)hi->hi_key; + return hi->hi_key; } // b: variables @@ -2134,7 +2130,7 @@ char *get_user_var_name(expand_T *xp, int idx) while (HASHITEM_EMPTY(hi)) { hi++; } - return cat_prefix_varname('b', (char *)hi->hi_key); + return cat_prefix_varname('b', hi->hi_key); } // w: variables @@ -2148,7 +2144,7 @@ char *get_user_var_name(expand_T *xp, int idx) while (HASHITEM_EMPTY(hi)) { hi++; } - return cat_prefix_varname('w', (char *)hi->hi_key); + return cat_prefix_varname('w', hi->hi_key); } // t: variables @@ -2162,7 +2158,7 @@ char *get_user_var_name(expand_T *xp, int idx) while (HASHITEM_EMPTY(hi)) { hi++; } - return cat_prefix_varname('t', (char *)hi->hi_key); + return cat_prefix_varname('t', hi->hi_key); } // v: variables @@ -2175,12 +2171,10 @@ char *get_user_var_name(expand_T *xp, int idx) return NULL; } -// TODO(ZyX-I): move to eval/expressions - /// 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; @@ -2188,10 +2182,10 @@ 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); + matches = vim_regexec_nl(®match, (char *)text, (colnr_T)0); vim_regfree(regmatch.regprog); } p_cpo = save_cpo; @@ -2219,7 +2213,7 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ // If "s" is the name of a variable of type VAR_FUNC // use its contents. partial_T *partial; - s = (char *)deref_func_name((const char *)s, &len, &partial, !evaluate); + s = deref_func_name((const char *)s, &len, &partial, !evaluate); // Need to make a copy, in case evaluating the arguments makes // the name invalid. @@ -2232,7 +2226,7 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ funcexe.fe_evaluate = evaluate; funcexe.fe_partial = partial; funcexe.fe_basetv = basetv; - int ret = get_func_tv((char_u *)s, len, rettv, arg, &funcexe); + int ret = get_func_tv(s, len, rettv, arg, &funcexe); xfree(s); @@ -2256,8 +2250,6 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ return ret; } -// TODO(ZyX-I): move to eval/expressions - /// The "evaluate" argument: When false, the argument is only parsed but not /// executed. The function may return OK, but the rettv will be of type /// VAR_UNKNOWN. The function still returns FAIL for a syntax error. @@ -2274,10 +2266,15 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate) char *p; const int did_emsg_before = did_emsg; const int called_emsg_before = called_emsg; + bool end_error = false; p = skipwhite(arg); ret = eval1(&p, rettv, evaluate); - if (ret == FAIL || !ends_excmd(*p)) { + + if (ret != FAIL) { + end_error = !ends_excmd(*p); + } + if (ret == FAIL || end_error) { if (ret != FAIL) { tv_clear(rettv); } @@ -2287,7 +2284,11 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate) // Also check called_emsg for when using assert_fails(). if (!aborting() && did_emsg == did_emsg_before && called_emsg == called_emsg_before) { - semsg(_(e_invexpr2), arg); + if (end_error) { + semsg(_(e_trailing_arg), p); + } else { + semsg(_(e_invexpr2), arg); + } } ret = FAIL; } @@ -2298,8 +2299,6 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate) return ret; } -// TODO(ZyX-I): move to eval/expressions - /// Handle top level expression: /// expr2 ? expr1 : expr1 /// @@ -2364,8 +2363,6 @@ int eval1(char **arg, typval_T *rettv, int evaluate) return OK; } -// TODO(ZyX-I): move to eval/expressions - /// Handle first level expression: /// expr2 || expr2 || expr2 logical OR /// @@ -2423,8 +2420,6 @@ static int eval2(char **arg, typval_T *rettv, int evaluate) return OK; } -// TODO(ZyX-I): move to eval/expressions - /// Handle second level expression: /// expr3 && expr3 && expr3 logical AND /// @@ -2482,8 +2477,6 @@ static int eval3(char **arg, typval_T *rettv, int evaluate) return OK; } -// TODO(ZyX-I): move to eval/expressions - /// Handle third level expression: /// var1 == var2 /// var1 =~ var2 @@ -2550,7 +2543,7 @@ static int eval4(char **arg, typval_T *rettv, int evaluate) if (p[2] == 'n' && p[3] == 'o' && p[4] == 't') { len = 5; } - if (!isalnum(p[len]) && p[len] != '_') { + if (!isalnum((uint8_t)p[len]) && p[len] != '_') { type = len == 2 ? EXPR_IS : EXPR_ISNOT; } } @@ -2587,8 +2580,6 @@ static int eval4(char **arg, typval_T *rettv, int evaluate) return OK; } -// TODO(ZyX-I): move to eval/expressions - /// Handle fourth level expression: /// + number addition /// - number subtraction @@ -2669,10 +2660,10 @@ static int eval5(char **arg, typval_T *rettv, int evaluate) blob_T *const b = tv_blob_alloc(); for (int i = 0; i < tv_blob_len(b1); i++) { - ga_append(&b->bv_ga, (char)tv_blob_get(b1, i)); + ga_append(&b->bv_ga, tv_blob_get(b1, i)); } for (int i = 0; i < tv_blob_len(b2); i++) { - ga_append(&b->bv_ga, (char)tv_blob_get(b2, i)); + ga_append(&b->bv_ga, tv_blob_get(b2, i)); } tv_clear(rettv); @@ -2750,8 +2741,6 @@ static int eval5(char **arg, typval_T *rettv, int evaluate) return OK; } -// TODO(ZyX-I): move to eval/expressions - /// Handle fifth level expression: /// - * number multiplication /// - / number division @@ -2867,8 +2856,6 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string) return OK; } -// TODO(ZyX-I): move to eval/expressions - /// Handle sixth level expression: /// number number constant /// 0zFFFFFFFF Blob constant @@ -2900,23 +2887,33 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string) /// @return OK or FAIL. static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) { - varnumber_T n; - int len; - char *s; - 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. rettv->v_type = VAR_UNKNOWN; // Skip '!', '-' and '+' characters. They are handled later. - start_leader = *arg; + const char *start_leader = *arg; while (**arg == '!' || **arg == '-' || **arg == '+') { *arg = skipwhite(*arg + 1); } - end_leader = *arg; + const char *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. @@ -2929,88 +2926,9 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) case '6': case '7': case '8': - case '9': { - char *p = skipdigits(*arg + 1); - int get_float = false; - - // We accept a float when the format matches - // "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?". This is very - // strict to avoid backwards compatibility problems. - // Don't look for a float after the "." operator, so that - // ":let vers = 1.2.3" doesn't fail. - if (!want_string && p[0] == '.' && ascii_isdigit(p[1])) { - get_float = true; - p = skipdigits(p + 2); - if (*p == 'e' || *p == 'E') { - p++; - if (*p == '-' || *p == '+') { - p++; - } - if (!ascii_isdigit(*p)) { - get_float = false; - } else { - p = skipdigits(p + 1); - } - } - if (ASCII_ISALPHA(*p) || *p == '.') { - get_float = false; - } - } - if (get_float) { - float_T f; - - *arg += string2float(*arg, &f); - if (evaluate) { - rettv->v_type = VAR_FLOAT; - rettv->vval.v_float = f; - } - } else if (**arg == '0' && ((*arg)[1] == 'z' || (*arg)[1] == 'Z')) { - blob_T *blob = NULL; - // Blob constant: 0z0123456789abcdef - if (evaluate) { - blob = tv_blob_alloc(); - } - char *bp; - for (bp = *arg + 2; ascii_isxdigit(bp[0]); bp += 2) { - if (!ascii_isxdigit(bp[1])) { - if (blob != NULL) { - emsg(_("E973: Blob literal should have an even number of hex " - "characters")); - ga_clear(&blob->bv_ga); - XFREE_CLEAR(blob); - } - ret = FAIL; - break; - } - if (blob != NULL) { - ga_append(&blob->bv_ga, (char)((hex2nr(*bp) << 4) + hex2nr(*(bp + 1)))); - } - if (bp[2] == '.' && ascii_isxdigit(bp[3])) { - bp++; - } - } - if (blob != NULL) { - tv_blob_set_ret(rettv, blob); - } - *arg = bp; - } else { - // decimal, hex or octal number - vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, true); - if (len == 0) { - if (evaluate) { - semsg(_(e_invexpr2), *arg); - } - ret = FAIL; - break; - } - *arg += len; - if (evaluate) { - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = n; - } - } + case '9': + ret = get_number_tv(arg, rettv, evaluate, want_string); break; - } // String constant: "string". case '"': @@ -3031,7 +2949,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; } @@ -3042,7 +2960,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; @@ -3088,8 +3006,9 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) if (ret == NOTDONE) { // Must be a variable or function name. // Can also be a curly-braces kind of name: {expr}. - s = *arg; - len = get_name_len((const char **)arg, &alias, evaluate, true); + char *s = *arg; + char *alias; + int len = get_name_len((const char **)arg, &alias, evaluate, true); if (alias != NULL) { s = alias; } @@ -3122,6 +3041,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; } @@ -3217,7 +3138,7 @@ static int call_func_rettv(char **const arg, typval_T *const rettv, const bool e funcexe.fe_partial = pt; funcexe.fe_selfdict = selfdict; funcexe.fe_basetv = basetv; - const int ret = get_func_tv((char_u *)funcname, is_lua ? (int)(*arg - funcname) : -1, rettv, + const int ret = get_func_tv(funcname, is_lua ? (int)(*arg - funcname) : -1, rettv, arg, &funcexe); // Clear the funcref afterwards, so that deleting it while @@ -3290,7 +3211,7 @@ static int eval_method(char **const arg, typval_T *const rettv, const bool evalu int len; char *name = *arg; char *lua_funcname = NULL; - if (STRNCMP(name, "v:lua.", 6) == 0) { + if (strncmp(name, "v:lua.", 6) == 0) { lua_funcname = name + 6; *arg = (char *)skip_luafunc_name((const char *)lua_funcname); *arg = skipwhite(*arg); // to detect trailing whitespace later @@ -3345,8 +3266,6 @@ static int eval_method(char **const arg, typval_T *const rettv, const bool evalu return ret; } -// TODO(ZyX-I): move to eval/expressions - /// Evaluate an "[expr]" or "[expr:expr]" index. Also "dict.key". /// "*arg" points to the '[' or '.'. /// @@ -3398,7 +3317,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) if (**arg == '.') { // dict.name key = *arg + 1; - for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) {} + for (len = 0; eval_isdictc(key[len]); len++) {} if (len == 0) { return FAIL; } @@ -3646,8 +3565,6 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) return OK; } -// TODO(ZyX-I): move to eval/executor - /// Get an option value /// /// @param[in,out] arg Points to the '&' or '+' before the option name. Is @@ -3659,10 +3576,11 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) int get_option_tv(const char **const arg, typval_T *const rettv, const bool evaluate) FUNC_ATTR_NONNULL_ARG(1) { - int opt_flags; + const bool working = (**arg == '+'); // has("+option") + int scope; // Isolate the option name and find its value. - char *option_end = (char *)find_option_end(arg, &opt_flags); + char *option_end = (char *)find_option_end(arg, &scope); if (option_end == NULL) { if (rettv != NULL) { semsg(_("E112: Option name missing: %s"), *arg); @@ -3682,7 +3600,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval char c = *option_end; *option_end = NUL; getoption_T opt_type = get_option_value(*arg, &numval, - rettv == NULL ? NULL : &stringval, opt_flags); + rettv == NULL ? NULL : &stringval, NULL, scope); if (opt_type == gov_unknown) { if (rettv != NULL) { @@ -3703,6 +3621,10 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval rettv->v_type = VAR_STRING; rettv->vval.v_string = stringval; } + } else if (working && (opt_type == gov_hidden_bool + || opt_type == gov_hidden_number + || opt_type == gov_hidden_string)) { + ret = FAIL; } *option_end = c; // put back for error messages @@ -3711,6 +3633,91 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval return ret; } +/// Allocate a variable for a number constant. Also deals with "0z" for blob. +/// +/// @return OK or FAIL. +static int get_number_tv(char **arg, typval_T *rettv, bool evaluate, bool want_string) +{ + char *p = skipdigits(*arg + 1); + bool get_float = false; + + // We accept a float when the format matches + // "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?". This is very + // strict to avoid backwards compatibility problems. + // Don't look for a float after the "." operator, so that + // ":let vers = 1.2.3" doesn't fail. + if (!want_string && p[0] == '.' && ascii_isdigit(p[1])) { + get_float = true; + p = skipdigits(p + 2); + if (*p == 'e' || *p == 'E') { + p++; + if (*p == '-' || *p == '+') { + p++; + } + if (!ascii_isdigit(*p)) { + get_float = false; + } else { + p = skipdigits(p + 1); + } + } + if (ASCII_ISALPHA(*p) || *p == '.') { + get_float = false; + } + } + if (get_float) { + float_T f; + *arg += string2float(*arg, &f); + if (evaluate) { + rettv->v_type = VAR_FLOAT; + rettv->vval.v_float = f; + } + } else if (**arg == '0' && ((*arg)[1] == 'z' || (*arg)[1] == 'Z')) { + // Blob constant: 0z0123456789abcdef + blob_T *blob = NULL; + if (evaluate) { + blob = tv_blob_alloc(); + } + char *bp; + for (bp = *arg + 2; ascii_isxdigit(bp[0]); bp += 2) { + if (!ascii_isxdigit(bp[1])) { + if (blob != NULL) { + emsg(_("E973: Blob literal should have an even number of hex characters")); + ga_clear(&blob->bv_ga); + XFREE_CLEAR(blob); + } + return FAIL; + } + if (blob != NULL) { + ga_append(&blob->bv_ga, (uint8_t)((hex2nr(*bp) << 4) + hex2nr(*(bp + 1)))); + } + if (bp[2] == '.' && ascii_isxdigit(bp[3])) { + bp++; + } + } + if (blob != NULL) { + tv_blob_set_ret(rettv, blob); + } + *arg = bp; + } else { + // decimal, hex or octal number + int len; + varnumber_T n; + vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, true); + if (len == 0) { + if (evaluate) { + semsg(_(e_invexpr2), *arg); + } + return FAIL; + } + *arg += len; + if (evaluate) { + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = n; + } + } + return OK; +} + /// Allocate a variable for a string constant. /// /// @return OK or FAIL. @@ -3772,7 +3779,7 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate) case 'U': if (ascii_isxdigit(p[1])) { int n, nr; - int c = toupper(*p); + int c = toupper((uint8_t)(*p)); if (c == 'X') { n = 2; @@ -3823,7 +3830,7 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate) if (p[1] != '*') { flags |= FSK_SIMPLIFY; } - extra = trans_special((const char_u **)&p, strlen(p), (char_u *)name, flags, false, NULL); + extra = trans_special((const char **)&p, strlen(p), name, flags, false, NULL); if (extra != 0) { name += extra; if (name >= rettv->vval.v_string + len) { @@ -3911,8 +3918,6 @@ char *partial_name(partial_T *pt) return (char *)pt->pt_func->uf_name; } -// TODO(ZyX-I): Move to eval/typval.h - static void partial_free(partial_T *pt) { for (int i = 0; i < pt->pt_argc; i++) { @@ -3921,7 +3926,7 @@ static void partial_free(partial_T *pt) xfree(pt->pt_argv); tv_dict_unref(pt->pt_dict); if (pt->pt_name != NULL) { - func_unref((char_u *)pt->pt_name); + func_unref(pt->pt_name); xfree(pt->pt_name); } else { func_ptr_unref(pt->pt_func); @@ -3929,8 +3934,6 @@ static void partial_free(partial_T *pt) xfree(pt); } -// TODO(ZyX-I): Move to eval/typval.h - /// Unreference a closure: decrement the reference count and free it when it /// becomes zero. void partial_unref(partial_T *pt) @@ -3973,7 +3976,7 @@ static int get_list_tv(char **arg, typval_T *rettv, int evaluate) } if (**arg != ']') { - semsg(_("E697: Missing end of List ']': %s"), *arg); + semsg(_(e_list_end), *arg); failret: if (evaluate) { tv_list_free(l); @@ -4138,10 +4141,23 @@ bool garbage_collect(bool testing) ABORTING(set_ref_dict)(buf->additional_data, copyID); // buffer callback functions - set_ref_in_callback(&buf->b_prompt_callback, copyID, NULL, NULL); - set_ref_in_callback(&buf->b_prompt_interrupt, copyID, NULL, NULL); + ABORTING(set_ref_in_callback)(&buf->b_prompt_callback, copyID, NULL, NULL); + ABORTING(set_ref_in_callback)(&buf->b_prompt_interrupt, copyID, NULL, NULL); + ABORTING(set_ref_in_callback)(&buf->b_cfu_cb, copyID, NULL, NULL); + ABORTING(set_ref_in_callback)(&buf->b_ofu_cb, copyID, NULL, NULL); + ABORTING(set_ref_in_callback)(&buf->b_tsrfu_cb, copyID, NULL, NULL); + ABORTING(set_ref_in_callback)(&buf->b_tfu_cb, copyID, NULL, NULL); } + // 'completefunc', 'omnifunc' and 'thesaurusfunc' callbacks + ABORTING(set_ref_in_insexpand_funcs)(copyID); + + // 'operatorfunc' callback + ABORTING(set_ref_in_opfunc)(copyID); + + // 'tagfunc' callback + ABORTING(set_ref_in_tagfunc)(copyID); + FOR_ALL_TAB_WINDOWS(tp, wp) { // window-local variables ABORTING(set_ref_in_item)(&wp->w_winvar.di_tv, copyID, NULL, NULL); @@ -4150,8 +4166,11 @@ bool garbage_collect(bool testing) ABORTING(set_ref_in_fmark)(wp->w_jumplist[i].fmark, copyID); } } - if (aucmd_win != NULL) { - ABORTING(set_ref_in_item)(&aucmd_win->w_winvar.di_tv, copyID, NULL, NULL); + // window-local variables in autocmd windows + for (int i = 0; i < AUCMD_WIN_COUNT; i++) { + if (aucmd_win[i].auc_win != NULL) { + ABORTING(set_ref_in_item)(&aucmd_win[i].auc_win->w_winvar.di_tv, copyID, NULL, NULL); + } } // registers (ShaDa additional data) @@ -4221,11 +4240,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); } @@ -4473,7 +4492,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack // A partial does not have a copyID, because it cannot contain itself. if (pt != NULL) { - abort = set_ref_in_func((char_u *)pt->pt_name, pt->pt_func, copyID); + abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID); if (pt->pt_dict != NULL) { typval_T dtv; @@ -4490,7 +4509,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack break; } case VAR_FUNC: - abort = set_ref_in_func((char_u *)tv->vval.v_string, NULL, copyID); + abort = set_ref_in_func(tv->vval.v_string, NULL, copyID); break; case VAR_UNKNOWN: case VAR_BOOL: @@ -4573,7 +4592,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; @@ -4754,19 +4773,6 @@ void assert_error(garray_T *gap) (const char *)gap->ga_data, (ptrdiff_t)gap->ga_len); } -/// Find a window: When using a Window ID in any tab page, when using a number -/// in the current tab page. -win_T *find_win_by_nr_or_id(typval_T *vp) -{ - int nr = (int)tv_get_number_chk(vp, NULL); - - if (nr >= LOWEST_WIN_ID) { - return win_id2wp((int)tv_get_number(vp)); - } - - return find_win_by_nr(vp, NULL); -} - /// Implementation of map() and filter(). void filter_map(typval_T *argvars, typval_T *rettv, int map) { @@ -4781,22 +4787,22 @@ 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))) { + && value_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))) { + || (!map && value_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { return; } } else { @@ -4835,7 +4841,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) dictitem_T *di = TV_DICT_HI2DI(hi); if (map - && (var_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE) + && (value_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE) || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) { break; } @@ -4875,7 +4881,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) } if (map) { if (tv.vval.v_number != val) { - tv_blob_set(b, i, (char_u)tv.vval.v_number); + tv_blob_set(b, i, (uint8_t)tv.vval.v_number); } } else if (rem) { char *const p = argvars[0].vval.v_blob->bv_ga.ga_data; @@ -4895,8 +4901,8 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) } for (listitem_T *li = tv_list_first(l); li != NULL;) { if (map - && var_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, - TV_TRANSLATE)) { + && value_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, + TV_TRANSLATE)) { break; } vimvars[VV_KEY].vv_nr = idx; @@ -4982,9 +4988,8 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) if ((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) || is_funcref) { name = s; - trans_name = (char *)trans_function_name(&name, false, - TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD - | TFN_NO_DEREF, NULL, NULL); + trans_name = save_function_name(&name, false, + TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL); if (*name != NUL) { s = NULL; } @@ -4997,26 +5002,19 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) // Don't check an autoload name for existence here. } else if (trans_name != NULL && (is_funcref - ? find_func((char_u *)trans_name) == NULL + ? find_func(trans_name) == NULL : !translated_function_exists((const char *)trans_name))) { semsg(_("E700: Unknown function: %s"), s); } else { int dict_idx = 0; int arg_idx = 0; list_T *list = NULL; - if (STRNCMP(s, "s:", 2) == 0 || STRNCMP(s, "<SID>", 5) == 0) { - char sid_buf[25]; - int off = *s == 's' ? 2 : 5; - + if (strncmp(s, "s:", 2) == 0 || strncmp(s, "<SID>", 5) == 0) { // Expand s: and <SID> into <SNR>nr_, so that the function can // also be called from another script. Using trans_function_name() // would also work, but some plugins depend on the name being // printable text. - snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRId64 "_", - (int64_t)current_sctx.sc_sid); - name = xmalloc(strlen(sid_buf) + strlen(s + off) + 1); - STRCPY(name, sid_buf); - STRCAT(name, s + off); + name = get_scriptlocal_funcname(s); } else { name = xstrdup(s); } @@ -5054,7 +5052,7 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) if (tv_list_len(list) == 0) { arg_idx = 0; } else if (tv_list_len(list) > MAX_FUNC_ARGS) { - emsg_funcname((char *)e_toomanyarg, (char_u *)s); + emsg_funcname((char *)e_toomanyarg, s); xfree(name); goto theend; } @@ -5103,12 +5101,12 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) func_ptr_ref(pt->pt_func); xfree(name); } else if (is_funcref) { - pt->pt_func = find_func((char_u *)trans_name); + pt->pt_func = find_func(trans_name); func_ptr_ref(pt->pt_func); xfree(name); } else { pt->pt_name = name; - func_ref((char_u *)name); + func_ref(name); } rettv->v_type = VAR_PARTIAL; @@ -5117,53 +5115,13 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) // result is a VAR_FUNC rettv->v_type = VAR_FUNC; rettv->vval.v_string = name; - func_ref((char_u *)name); + func_ref(name); } } theend: xfree(trans_name); } -/// @return buffer options, variables and other attributes in a dictionary. -dict_T *get_buffer_info(buf_T *buf) -{ - dict_T *const dict = tv_dict_alloc(); - - tv_dict_add_nr(dict, S_LEN("bufnr"), buf->b_fnum); - tv_dict_add_str(dict, S_LEN("name"), - buf->b_ffname != NULL ? (const char *)buf->b_ffname : ""); - tv_dict_add_nr(dict, S_LEN("lnum"), - buf == curbuf ? curwin->w_cursor.lnum : buflist_findlnum(buf)); - tv_dict_add_nr(dict, S_LEN("linecount"), buf->b_ml.ml_line_count); - tv_dict_add_nr(dict, S_LEN("loaded"), buf->b_ml.ml_mfp != NULL); - tv_dict_add_nr(dict, S_LEN("listed"), buf->b_p_bl); - tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf)); - tv_dict_add_nr(dict, S_LEN("changedtick"), buf_get_changedtick(buf)); - tv_dict_add_nr(dict, S_LEN("hidden"), - buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0); - - // Get a reference to buffer variables - tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars); - - // List of windows displaying this buffer - list_T *const windows = tv_list_alloc(kListLenMayKnow); - FOR_ALL_TAB_WINDOWS(tp, wp) { - if (wp->w_buffer == buf) { - tv_list_append_number(windows, (varnumber_T)wp->handle); - } - } - tv_dict_add_list(dict, S_LEN("windows"), windows); - - if (buf->b_signlist != NULL) { - // List of signs placed in this buffer - tv_dict_add_list(dict, S_LEN("signs"), get_buffer_signs(buf)); - } - - tv_dict_add_nr(dict, S_LEN("lastused"), buf->b_last_used); - - return dict; -} - /// Get the line number from VimL object /// /// @note Unlike tv_get_lnum(), this one supports only "$" special string. @@ -5180,121 +5138,13 @@ 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; } return (linenr_T)tv_get_number_chk(tv, NULL); } -/// @return information (variables, options, etc.) about a tab page -/// as a dictionary. -dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) -{ - dict_T *const dict = tv_dict_alloc(); - - tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx); - - list_T *const l = tv_list_alloc(kListLenMayKnow); - FOR_ALL_WINDOWS_IN_TAB(wp, tp) { - tv_list_append_number(l, (varnumber_T)wp->handle); - } - tv_dict_add_list(dict, S_LEN("windows"), l); - - // Make a reference to tabpage variables - tv_dict_add_dict(dict, S_LEN("variables"), tp->tp_vars); - - return dict; -} - -/// @return information about a window as a dictionary. -dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) -{ - dict_T *const dict = tv_dict_alloc(); - - // make sure w_botline is valid - validate_botline(wp); - - tv_dict_add_nr(dict, S_LEN("tabnr"), tpnr); - tv_dict_add_nr(dict, S_LEN("winnr"), winnr); - tv_dict_add_nr(dict, S_LEN("winid"), wp->handle); - tv_dict_add_nr(dict, S_LEN("height"), wp->w_height_inner); - tv_dict_add_nr(dict, S_LEN("winrow"), wp->w_winrow + 1); - tv_dict_add_nr(dict, S_LEN("topline"), wp->w_topline); - tv_dict_add_nr(dict, S_LEN("botline"), wp->w_botline - 1); - tv_dict_add_nr(dict, S_LEN("winbar"), wp->w_winbar_height); - tv_dict_add_nr(dict, S_LEN("width"), wp->w_width_inner); - tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum); - tv_dict_add_nr(dict, S_LEN("wincol"), wp->w_wincol + 1); - tv_dict_add_nr(dict, S_LEN("textoff"), win_col_off(wp)); - tv_dict_add_nr(dict, S_LEN("terminal"), bt_terminal(wp->w_buffer)); - tv_dict_add_nr(dict, S_LEN("quickfix"), bt_quickfix(wp->w_buffer)); - tv_dict_add_nr(dict, S_LEN("loclist"), - (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL)); - - // Add a reference to window variables - tv_dict_add_dict(dict, S_LEN("variables"), wp->w_vars); - - return dict; -} - -/// Find window specified by "vp" in tabpage "tp". -/// -/// @param tp NULL for current tab page -win_T *find_win_by_nr(typval_T *vp, tabpage_T *tp) -{ - int nr = (int)tv_get_number_chk(vp, NULL); - - if (nr < 0) { - return NULL; - } - - if (nr == 0) { - return curwin; - } - - // This method accepts NULL as an alias for curtab. - if (tp == NULL) { - tp = curtab; - } - - FOR_ALL_WINDOWS_IN_TAB(wp, tp) { - if (nr >= LOWEST_WIN_ID) { - if (wp->handle == nr) { - return wp; - } - } else if (--nr <= 0) { - return wp; - } - } - return NULL; -} - -/// Find window specified by "wvp" in tabpage "tvp". -win_T *find_tabwin(typval_T *wvp, typval_T *tvp) -{ - win_T *wp = NULL; - tabpage_T *tp = NULL; - - if (wvp->v_type != VAR_UNKNOWN) { - if (tvp->v_type != VAR_UNKNOWN) { - long n = tv_get_number(tvp); - if (n >= 0) { - tp = find_tabpage((int)n); - } - } else { - tp = curtab; - } - - if (tp != NULL) { - wp = find_win_by_nr(wvp, tp); - } - } else { - wp = curwin; - } - - return wp; -} - /// This function is used by f_input() and f_inputdialog() functions. The third /// argument to f_input() specifies the type of completion to use at the /// prompt. The third argument to f_inputdialog() specifies the value to return @@ -5516,126 +5366,6 @@ void screenchar_adjust(ScreenGrid **grid, int *row, int *col) *col -= (*grid)->comp_col; } -/// Set line or list of lines in buffer "buf". -void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T *lines, - typval_T *rettv) - FUNC_ATTR_NONNULL_ARG(4, 5) -{ - linenr_T lnum = lnum_arg + (append ? 1 : 0); - long added = 0; - buf_T *curbuf_save = NULL; - win_T *curwin_save = NULL; - const bool is_curbuf = buf == curbuf; - const bool save_VIsual_active = VIsual_active; - - // When using the current buffer ml_mfp will be set if needed. Useful when - // setline() is used on startup. For other buffers the buffer must be - // loaded. - if (buf == NULL || (!is_curbuf && buf->b_ml.ml_mfp == NULL) || lnum < 1) { - rettv->vval.v_number = 1; // FAIL - return; - } - - if (!is_curbuf) { - VIsual_active = false; - curbuf_save = curbuf; - curwin_save = curwin; - curbuf = buf; - find_win_for_curbuf(); - } - - linenr_T append_lnum; - if (append) { - // appendbufline() uses the line number below which we insert - append_lnum = lnum - 1; - } else { - // setbufline() uses the line number above which we insert, we only - // append if it's below the last line - append_lnum = curbuf->b_ml.ml_line_count; - } - - list_T *l = NULL; - listitem_T *li = NULL; - const char *line = NULL; - if (lines->v_type == VAR_LIST) { - l = lines->vval.v_list; - li = tv_list_first(l); - } else { - line = tv_get_string_chk(lines); - } - - // Default result is zero == OK. - for (;;) { - if (lines->v_type == VAR_LIST) { - // List argument, get next string. - if (li == NULL) { - break; - } - line = tv_get_string_chk(TV_LIST_ITEM_TV(li)); - li = TV_LIST_ITEM_NEXT(l, li); - } - - rettv->vval.v_number = 1; // FAIL - if (line == NULL || lnum > curbuf->b_ml.ml_line_count + 1) { - break; - } - - // When coming here from Insert mode, sync undo, so that this can be - // undone separately from what was previously inserted. - if (u_sync_once == 2) { - u_sync_once = 1; // notify that u_sync() was called - u_sync(true); - } - - if (!append && lnum <= curbuf->b_ml.ml_line_count) { - // Existing line, replace it. - int old_len = (int)strlen(ml_get(lnum)); - if (u_savesub(lnum) == OK - && ml_replace(lnum, (char *)line, true) == OK) { - inserted_bytes(lnum, 0, old_len, (int)strlen(line)); - if (is_curbuf && lnum == curwin->w_cursor.lnum) { - check_cursor_col(); - } - rettv->vval.v_number = 0; // OK - } - } else if (added > 0 || u_save(lnum - 1, lnum) == OK) { - // append the line. - added++; - if (ml_append(lnum - 1, (char *)line, 0, false) == OK) { - rettv->vval.v_number = 0; // OK - } - } - - if (l == NULL) { // only one string argument - break; - } - lnum++; - } - - if (added > 0) { - appended_lines_mark(append_lnum, added); - - // Only adjust the cursor for buffers other than the current, unless it - // is the current window. For curbuf and other windows it has been done - // in mark_adjust_internal(). - FOR_ALL_TAB_WINDOWS(tp, wp) { - if (wp->w_buffer == buf - && (wp->w_buffer != curbuf || wp == curwin) - && wp->w_cursor.lnum > append_lnum) { - wp->w_cursor.lnum += (linenr_T)added; - } - } - check_cursor_col(); - update_topline(curwin); - } - - if (!is_curbuf) { - curbuf = curbuf_save; - curwin = curwin_save; - VIsual_active = save_VIsual_active; - } -} - /// "stdpath()" helper for list results void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv) FUNC_ATTR_NONNULL_ALL @@ -5688,7 +5418,7 @@ void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist // get input to the shell command (if any), and its length ptrdiff_t input_len; - char *input = save_tv_as_string(&argvars[1], &input_len, false); + char *input = save_tv_as_string(&argvars[1], &input_len, false, false); if (input_len < 0) { assert(input == NULL); return; @@ -5772,7 +5502,8 @@ void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist } } -bool callback_from_typval(Callback *const callback, typval_T *const arg) +/// Get a callback from "arg". It can be a Funcref or a function name. +bool callback_from_typval(Callback *const callback, const typval_T *const arg) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { int r = OK; @@ -5793,13 +5524,19 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg) callback->type = kCallbackNone; callback->data.funcref = NULL; } else { - func_ref((char_u *)name); - callback->data.funcref = xstrdup(name); + callback->data.funcref = NULL; + if (arg->v_type == VAR_STRING) { + callback->data.funcref = get_scriptlocal_funcname(name); + } + if (callback->data.funcref == NULL) { + callback->data.funcref = xstrdup(name); + } + func_ref(callback->data.funcref); callback->type = kCallbackFuncref; } } else if (nlua_is_table_from_lua(arg)) { // TODO(tjdvries): UnifiedCallback - char *name = (char *)nlua_register_table_as_callable(arg); + char *name = nlua_register_table_as_callable(arg); if (name != NULL) { callback->data.funcref = xstrdup(name); @@ -5822,6 +5559,7 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg) return true; } +/// @return whether the callback could be called. bool callback_call(Callback *const callback, const int argcount_in, typval_T *const argvars_in, typval_T *const rettv) FUNC_ATTR_NONNULL_ALL @@ -5871,8 +5609,8 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co return call_func(name, -1, rettv, argcount_in, argvars_in, &funcexe); } -static bool set_ref_in_callback(Callback *callback, int copyID, ht_stack_T **ht_stack, - list_stack_T **list_stack) +bool set_ref_in_callback(Callback *callback, int copyID, ht_stack_T **ht_stack, + list_stack_T **list_stack) { typval_T tv; switch (callback->type) { @@ -6175,9 +5913,10 @@ bool read_blob(FILE *const fd, blob_T *const blob) /// @param[in] tv Value to store as a string. /// @param[out] len Length of the resulting string or -1 on error. /// @param[in] endnl If true, the output will end in a newline (if a list). +/// @param[in] crlf If true, list items will be joined with CRLF (if a list). /// @returns an allocated string if `tv` represents a VimL string, list, or /// number; NULL otherwise. -char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) +char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl, bool crlf) FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { *len = 0; @@ -6234,20 +5973,23 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) // Pre-calculate the resulting length. list_T *list = tv->vval.v_list; TV_LIST_ITER_CONST(list, li, { - *len += (ptrdiff_t)strlen(tv_get_string(TV_LIST_ITEM_TV(li))) + 1; + *len += (ptrdiff_t)strlen(tv_get_string(TV_LIST_ITEM_TV(li))) + (crlf ? 2 : 1); }); if (*len == 0) { return NULL; } - char *ret = xmalloc((size_t)(*len) + endnl); + char *ret = xmalloc((size_t)(*len) + (endnl ? (crlf ? 2 : 1) : 0)); char *end = ret; TV_LIST_ITER_CONST(list, li, { for (const char *s = tv_get_string(TV_LIST_ITEM_TV(li)); *s != NUL; s++) { *end++ = (*s == '\n') ? NUL : *s; } if (endnl || TV_LIST_ITEM_NEXT(list, li) != NULL) { + if (crlf) { + *end++ = '\r'; + } *end++ = '\n'; } }); @@ -6358,7 +6100,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret } int len; if (charcol) { - len = mb_charlen((char_u *)ml_get(pos.lnum)); + len = mb_charlen(ml_get(pos.lnum)); } else { len = (int)strlen(ml_get(pos.lnum)); } @@ -6444,7 +6186,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret } else { pos.lnum = curwin->w_cursor.lnum; if (charcol) { - pos.col = (colnr_T)mb_charlen((char_u *)get_cursor_line_ptr()); + pos.col = (colnr_T)mb_charlen(get_cursor_line_ptr()); } else { pos.col = (colnr_T)strlen(get_cursor_line_ptr()); } @@ -6535,7 +6277,7 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool c int get_env_len(const char **arg) { const char *p; - for (p = *arg; vim_isIDc(*p); p++) {} + for (p = *arg; vim_isIDc((uint8_t)(*p)); p++) {} if (p == *arg) { // No name found. return 0; } @@ -6562,7 +6304,7 @@ int get_id_len(const char **const arg) // slice "[n:]". Also "xx:" is not a namespace. len = (int)(p - *arg); if (len > 1 - || (len == 1 && vim_strchr(namespace_char, **arg) == NULL)) { + || (len == 1 && vim_strchr(namespace_char, (uint8_t)(**arg)) == NULL)) { break; } } @@ -6665,7 +6407,8 @@ const char *find_name_end(const char *arg, const char **expr_start, const char * for (p = arg; *p != NUL && (eval_isnamec(*p) || *p == '{' - || ((flags & FNE_INCL_BR) && (*p == '[' || *p == '.')) + || ((flags & FNE_INCL_BR) && (*p == '[' + || (*p == '.' && eval_isdictc(p[1])))) || mb_nest != 0 || br_nest != 0); MB_PTR_ADV(p)) { if (*p == '\'') { @@ -6689,7 +6432,7 @@ const char *find_name_end(const char *arg, const char **expr_start, const char * // slice "[n:]". Also "xx:" is not a namespace. But {ns}: is. len = (int)(p - arg); if ((len > 1 && p[-1] != '}') - || (len == 1 && vim_strchr(namespace_char, *arg) == NULL)) { + || (len == 1 && vim_strchr(namespace_char, (uint8_t)(*arg)) == NULL)) { break; } } @@ -6778,18 +6521,25 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex /// @return true if character "c" can be used in a variable or function name. /// Does not include '{' or '}' for magic braces. -int eval_isnamec(int c) +bool eval_isnamec(int c) { return ASCII_ISALNUM(c) || c == '_' || c == ':' || c == AUTOLOAD_CHAR; } /// @return true if character "c" can be used as the first character in a /// variable or function name (excluding '{' and '}'). -int eval_isnamec1(int c) +bool eval_isnamec1(int c) { return ASCII_ISALPHA(c) || c == '_'; } +/// @return true if character "c" can be used as the first character of a +/// dictionary key. +bool eval_isdictc(int c) +{ + return ASCII_ISALNUM(c) || c == '_'; +} + /// Get typval_T v: variable value. typval_T *get_vim_var_tv(int idx) { @@ -6922,12 +6672,13 @@ void set_vim_var_dict(const VimVarIndex idx, dict_T *const val) tv_clear(&vimvars[idx].vv_di.di_tv); vimvars[idx].vv_type = VAR_DICT; vimvars[idx].vv_dict = val; - - if (val != NULL) { - val->dv_refcount++; - // Set readonly - tv_dict_set_keys_readonly(val); + if (val == NULL) { + return; } + + val->dv_refcount++; + // Set readonly + tv_dict_set_keys_readonly(val); } /// Set the v:argv list. @@ -7021,6 +6772,9 @@ char *set_cmdarg(exarg_T *eap, char *oldarg) if (eap->bad_char != 0) { len += 7 + 4; // " ++bad=" + "keep" or "drop" } + if (eap->mkdir_p != 0) { + len += 4; + } const size_t newval_len = len + 1; char *newval = xmalloc(newval_len); @@ -7054,6 +6808,11 @@ char *set_cmdarg(exarg_T *eap, char *oldarg) snprintf(newval + strlen(newval), newval_len, " ++bad=%c", eap->bad_char); } + + if (eap->mkdir_p) { + snprintf(newval, newval_len, " ++p"); + } + vimvars[VV_CMDARG].vv_str = newval; return oldval; } @@ -7108,9 +6867,8 @@ int check_luafunc_name(const char *const str, const bool paren) const char *const p = skip_luafunc_name(str); if (*p != (paren ? '(' : NUL)) { return 0; - } else { - return (int)(p - str); } + return (int)(p - str); } /// Handle: @@ -7652,7 +7410,7 @@ void ex_echo(exarg_T *eap) /// ":echohl {name}". void ex_echohl(exarg_T *eap) { - echo_attr = syn_name2attr((char_u *)eap->arg); + echo_attr = syn_name2attr(eap->arg); } /// ":execute expr1 ..." execute the result of an expression. @@ -7740,19 +7498,19 @@ void ex_execute(exarg_T *eap) /// /// @return NULL when no option name found. Otherwise pointer to the char /// after the option name. -const char *find_option_end(const char **const arg, int *const opt_flags) +const char *find_option_end(const char **const arg, int *const scope) { const char *p = *arg; p++; if (*p == 'g' && p[1] == ':') { - *opt_flags = OPT_GLOBAL; + *scope = OPT_GLOBAL; p += 2; } else if (*p == 'l' && p[1] == ':') { - *opt_flags = OPT_LOCAL; + *scope = OPT_LOCAL; p += 2; } else { - *opt_flags = 0; + *scope = 0; } if (!ASCII_ISALPHA(*p)) { @@ -7856,9 +7614,8 @@ static var_flavour_T var_flavour(char *varname) } } return VAR_FLAVOUR_SHADA; - } else { - return VAR_FLAVOUR_DEFAULT; } + return VAR_FLAVOUR_DEFAULT; } /// Iterate over global variables @@ -7884,7 +7641,7 @@ const void *var_shada_iter(const void *const iter, const char **const name, typv hi = globvarht.ht_array; while ((size_t)(hi - hifirst) < hinum && (HASHITEM_EMPTY(hi) - || !(var_flavour((char *)hi->hi_key) & flavour))) { + || !(var_flavour(hi->hi_key) & flavour))) { hi++; } if ((size_t)(hi - hifirst) == hinum) { @@ -7896,7 +7653,7 @@ const void *var_shada_iter(const void *const iter, const char **const name, typv *name = (char *)TV_DICT_HI2DI(hi)->di_key; tv_copy(&TV_DICT_HI2DI(hi)->di_tv, rettv); while ((size_t)(++hi - hifirst) < hinum) { - if (!HASHITEM_EMPTY(hi) && (var_flavour((char *)hi->hi_key) & flavour)) { + if (!HASHITEM_EMPTY(hi) && (var_flavour(hi->hi_key) & flavour)) { return hi; } } @@ -7920,8 +7677,7 @@ int store_session_globals(FILE *fd) && var_flavour((char *)this_var->di_key) == VAR_FLAVOUR_SESSION) { // Escape special characters with a backslash. Turn a LF and // CR into \n and \r. - char *const p = (char *)vim_strsave_escaped((const char_u *)tv_get_string(&this_var->di_tv), - (const char_u *)"\\\"\n\r"); + char *const p = (char *)vim_strsave_escaped(tv_get_string(&this_var->di_tv), "\\\"\n\r"); for (char *t = p; *t != NUL; t++) { if (*t == '\n') { *t = 'n'; @@ -8062,8 +7818,8 @@ repeat: } // FullName_save() is slow, don't use it when not needed. - if (*p != NUL || !vim_isAbsName((char_u *)(*fnamep))) { - *fnamep = FullName_save((*fnamep), *p != NUL); + if (*p != NUL || !vim_isAbsName(*fnamep)) { + *fnamep = FullName_save(*fnamep, *p != NUL); xfree(*bufp); // free any allocated file name *bufp = *fnamep; if (*fnamep == NULL) { @@ -8108,7 +7864,7 @@ repeat: if (p != NULL) { if (c == '.') { - os_dirname((char_u *)dirname, MAXPATHL); + os_dirname(dirname, MAXPATHL); if (has_homerelative) { s = xstrdup(dirname); home_replace(NULL, s, dirname, MAXPATHL, true); @@ -8252,7 +8008,7 @@ repeat: s++; } - int sep = (char_u)(*s++); + int sep = (uint8_t)(*s++); if (sep) { // find end of pattern p = vim_strchr(s, sep); @@ -8285,11 +8041,11 @@ repeat: if (src[*usedlen] == ':' && src[*usedlen + 1] == 'S') { // vim_strsave_shellescape() needs a NUL terminated string. - c = (char_u)(*fnamep)[*fnamelen]; + c = (uint8_t)(*fnamep)[*fnamelen]; if (c != NUL) { (*fnamep)[*fnamelen] = NUL; } - p = (char *)vim_strsave_shellescape((char_u *)(*fnamep), false, false); + p = vim_strsave_shellescape(*fnamep, false, false); if (c != NUL) { (*fnamep)[*fnamelen] = (char)c; } @@ -8307,7 +8063,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; @@ -8327,7 +8083,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags if (regmatch.regprog != NULL) { char *tail = str; char *end = str + strlen(str); - while (vim_regexec_nl(®match, (char_u *)str, (colnr_T)(tail - str))) { + while (vim_regexec_nl(®match, str, (colnr_T)(tail - str))) { // Skip empty match except for first match. if (regmatch.startp[0] == regmatch.endp[0]) { if (zero_width == regmatch.startp[0]) { @@ -8346,7 +8102,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags // - The text up to where the match is. // - The substituted text. // - The text after the match. - sublen = vim_regsub(®match, (char_u *)sub, expr, (char_u *)tail, 0, REGSUB_MAGIC); + sublen = vim_regsub(®match, sub, expr, tail, 0, REGSUB_MAGIC); ga_grow(&ga, (int)((end - tail) + sublen - (regmatch.endp[0] - regmatch.startp[0]))); @@ -8354,8 +8110,8 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags int i = (int)(regmatch.startp[0] - tail); memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i); // add the substituted text - (void)vim_regsub(®match, (char_u *)sub, expr, - (char_u *)ga.ga_data + ga.ga_len + i, sublen, + (void)vim_regsub(®match, sub, expr, + (char *)ga.ga_data + ga.ga_len + i, sublen, REGSUB_COPY | REGSUB_MAGIC); ga.ga_len += i + sublen - 1; tail = regmatch.endp[0]; @@ -8543,7 +8299,7 @@ bool eval_has_provider(const char *feat) if (get_var_tv(buf, len, &tv, NULL, false, true) == FAIL) { // Show a hint if Call() is defined but g:loaded_xx_provider is missing. snprintf(buf, sizeof(buf), "provider#%s#Call", name); - if (!!find_func((char_u *)buf) && p_lpl) { + if (!!find_func(buf) && p_lpl) { semsg("provider: %s: missing required variable g:loaded_%s_provider", name, name); } @@ -8558,7 +8314,7 @@ bool eval_has_provider(const char *feat) if (ok) { // Call() must be defined if provider claims to be working. snprintf(buf, sizeof(buf), "provider#%s#Call", name); - if (!find_func((char_u *)buf)) { + if (!find_func(buf)) { semsg("provider: %s: g:loaded_%s_provider=2 but %s is not defined", name, name, buf); ok = false; @@ -8581,10 +8337,10 @@ void eval_fmt_source_name_line(char *buf, size_t bufsize) /// ":checkhealth [plugins]" void ex_checkhealth(exarg_T *eap) { - bool found = !!find_func((char_u *)"health#check"); + bool found = !!find_func("health#check"); if (!found && script_autoload("health#check", sizeof("health#check") - 1, false)) { - found = !!find_func((char_u *)"health#check"); + found = !!find_func("health#check"); } if (!found) { const char *vimruntime_env = os_getenv("VIMRUNTIME"); @@ -8626,7 +8382,7 @@ void invoke_prompt_callback(void) return; } char *text = ml_get(lnum); - char *prompt = (char *)prompt_text(); + char *prompt = prompt_text(); if (strlen(text) >= strlen(prompt)) { text += strlen(prompt); } @@ -8651,9 +8407,9 @@ bool invoke_prompt_interrupt(void) argv[0].v_type = VAR_UNKNOWN; got_int = false; // don't skip executing commands - callback_call(&curbuf->b_prompt_interrupt, 0, argv, &rettv); + int ret = callback_call(&curbuf->b_prompt_interrupt, 0, argv, &rettv); tv_clear(&rettv); - return true; + return ret != FAIL; } /// Compare "typ1" and "typ2". Put the result in "typ1". @@ -8851,7 +8607,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; } @@ -8866,10 +8622,16 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic) return OK; } -char *typval_tostring(typval_T *arg) +/// Convert any type to a string, never give an error. +/// When "quotes" is true add quotes to a string. +/// Returns an allocated string. +char *typval_tostring(typval_T *arg, bool quotes) { if (arg == NULL) { return xstrdup("(does not exist)"); } + if (!quotes && arg->v_type == VAR_STRING) { + return xstrdup(arg->vval.v_string == NULL ? "" : arg->vval.v_string); + } return encode_tv2string(arg, NULL); } |