diff options
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r-- | src/nvim/eval.c | 943 |
1 files changed, 349 insertions, 594 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2dbaa2f8ac..f9825496a5 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_WRAP, "wrap", VAR_BOOL, 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; } @@ -358,7 +394,7 @@ void eval_init(void) for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) { struct vimvar *p = &vimvars[i]; - assert(STRLEN(p->vv_name) <= VIMVAR_KEY_LEN); + assert(strlen(p->vv_name) <= VIMVAR_KEY_LEN); STRCPY(p->vv_di.di_key, p->vv_name); if (p->vv_flags & VV_RO) { p->vv_di.di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; @@ -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; @@ -578,7 +612,7 @@ void var_redir_str(char *value, int value_len) int len; if (value_len == -1) { - len = (int)STRLEN(value); // Append the entire string + len = (int)strlen(value); // Append the entire string } else { len = value_len; // Append only "value_len" characters } @@ -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); @@ -757,7 +772,7 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *r if (s == NULL || *s == NUL) { return FAIL; } - funcexe.evaluate = true; + funcexe.fe_evaluate = true; if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { return FAIL; } @@ -767,8 +782,8 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *r if (s == NULL || *s == NUL) { return FAIL; } - funcexe.evaluate = true; - funcexe.partial = partial; + funcexe.fe_evaluate = true; + funcexe.fe_partial = partial; if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { return FAIL; } @@ -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,16 +1075,16 @@ 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 { int ret; - int len = (int)STRLEN(func); + int len = (int)strlen(func); partial_T *pt = NULL; if (len >= 6 && !memcmp(func, "v:lua.", 6)) { @@ -1093,10 +1099,10 @@ int call_vim_function(const char *func, int argc, typval_T *argv, typval_T *rett rettv->v_type = VAR_UNKNOWN; // tv_clear() uses this. funcexe_T funcexe = FUNCEXE_INIT; - funcexe.firstline = curwin->w_cursor.lnum; - funcexe.lastline = curwin->w_cursor.lnum; - funcexe.evaluate = true; - funcexe.partial = pt; + funcexe.fe_firstline = curwin->w_cursor.lnum; + funcexe.fe_lastline = curwin->w_cursor.lnum; + funcexe.fe_evaluate = true; + funcexe.fe_partial = pt; ret = call_func(func, len, rettv, argc, argv, &funcexe); fail: @@ -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; } } @@ -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. @@ -1667,7 +1667,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool // handle +=, -=, *=, /=, %= and .= di = NULL; - if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name), + if (get_var_tv(lp->ll_name, (int)strlen(lp->ll_name), &tv, &di, true, false) == OK) { if ((di == NULL || (!var_check_ro(di->di_flags, lp->ll_name, TV_CSTRING) @@ -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) { @@ -1960,7 +1956,7 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx) xp->xp_context = EXPAND_USER_VARS; if (strpbrk(arg, "\"'+-*/%.=!?~|&$([<>,#") == NULL) { // ":let var1 var2 ...": find last space. - for (p = arg + STRLEN(arg); p >= arg;) { + for (p = arg + strlen(arg); p >= arg;) { xp->xp_pattern = p; MB_PTR_BACK(arg, p); if (ascii_iswhite(*p)) { @@ -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); } }); @@ -2077,7 +2073,7 @@ static size_t varnamebuflen = 0; char *cat_prefix_varname(int prefix, const char *name) FUNC_ATTR_NONNULL_ALL { - size_t len = STRLEN(name) + 3; + size_t len = strlen(name) + 3; if (len > varnamebuflen) { xfree(varnamebuf); @@ -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,7 +2182,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); @@ -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. @@ -2227,12 +2221,12 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ // Invoke the function. funcexe_T funcexe = FUNCEXE_INIT; - funcexe.firstline = curwin->w_cursor.lnum; - funcexe.lastline = curwin->w_cursor.lnum; - funcexe.evaluate = evaluate; - funcexe.partial = partial; - funcexe.basetv = basetv; - int ret = get_func_tv((char_u *)s, len, rettv, arg, &funcexe); + funcexe.fe_firstline = curwin->w_cursor.lnum; + funcexe.fe_lastline = curwin->w_cursor.lnum; + funcexe.fe_evaluate = evaluate; + funcexe.fe_partial = partial; + funcexe.fe_basetv = basetv; + 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,19 +2284,21 @@ 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; } if (nextcmd != NULL) { - *nextcmd = (char *)check_nextcmd((char_u *)p); + *nextcmd = check_nextcmd(p); } 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 @@ -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 @@ -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 @@ -2906,6 +2893,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. @@ -2918,6 +2906,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': @@ -2995,9 +2997,11 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) *arg = bp; } else { // decimal, hex or octal number - vim_str2nr((char_u *)(*arg), NULL, &len, STR2NR_ALL, &n, NULL, 0, true); + vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, true); if (len == 0) { - semsg(_(e_invexpr2), *arg); + if (evaluate) { + semsg(_(e_invexpr2), *arg); + } ret = FAIL; break; } @@ -3029,7 +3033,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; } @@ -3040,7 +3044,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; @@ -3120,6 +3124,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; } @@ -3209,13 +3215,13 @@ static int call_func_rettv(char **const arg, typval_T *const rettv, const bool e } funcexe_T funcexe = FUNCEXE_INIT; - funcexe.firstline = curwin->w_cursor.lnum; - funcexe.lastline = curwin->w_cursor.lnum; - funcexe.evaluate = evaluate; - funcexe.partial = pt; - funcexe.selfdict = selfdict; - funcexe.basetv = basetv; - const int ret = get_func_tv((char_u *)funcname, is_lua ? (int)(*arg - funcname) : -1, rettv, + funcexe.fe_firstline = curwin->w_cursor.lnum; + funcexe.fe_lastline = curwin->w_cursor.lnum; + funcexe.fe_evaluate = evaluate; + funcexe.fe_partial = pt; + funcexe.fe_selfdict = selfdict; + funcexe.fe_basetv = basetv; + 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 @@ -3288,7 +3294,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 @@ -3343,8 +3349,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 '.'. /// @@ -3396,7 +3400,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; } @@ -3644,8 +3648,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 @@ -3657,11 +3659,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) { - bool working = (**arg == '+'); // has("+option") - 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); @@ -3681,7 +3683,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) { @@ -3826,7 +3828,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), (char_u *)name, flags, false, NULL); if (extra != 0) { name += extra; if (name >= rettv->vval.v_string + len) { @@ -3909,13 +3911,11 @@ char *partial_name(partial_T *pt) FUNC_ATTR_PURE { if (pt->pt_name != NULL) { - return (char *)pt->pt_name; + return pt->pt_name; } 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++) { @@ -3924,7 +3924,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(pt->pt_name); + func_unref((char_u *)pt->pt_name); xfree(pt->pt_name); } else { func_ptr_unref(pt->pt_func); @@ -3932,8 +3932,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) @@ -3976,7 +3974,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); @@ -3996,13 +3994,11 @@ failret: bool func_equal(typval_T *tv1, typval_T *tv2, bool ic) { // empty and NULL function name considered the same - char_u *s1 = - (char_u *)(tv1->v_type == VAR_FUNC ? tv1->vval.v_string : partial_name(tv1->vval.v_partial)); + char *s1 = tv1->v_type == VAR_FUNC ? tv1->vval.v_string : partial_name(tv1->vval.v_partial); if (s1 != NULL && *s1 == NUL) { s1 = NULL; } - char_u *s2 = - (char_u *)(tv2->v_type == VAR_FUNC ? tv2->vval.v_string : partial_name(tv2->vval.v_partial)); + char *s2 = tv2->v_type == VAR_FUNC ? tv2->vval.v_string : partial_name(tv2->vval.v_partial); if (s2 != NULL && *s2 == NUL) { s2 = NULL; } @@ -4010,7 +4006,7 @@ bool func_equal(typval_T *tv1, typval_T *tv2, bool ic) if (s1 != s2) { return false; } - } else if (STRCMP(s1, s2) != 0) { + } else if (strcmp(s1, s2) != 0) { return false; } @@ -4143,10 +4139,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); @@ -4155,8 +4164,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) @@ -4226,11 +4238,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); } @@ -4478,7 +4490,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(pt->pt_name, pt->pt_func, copyID); + abort = set_ref_in_func((char_u *)pt->pt_name, pt->pt_func, copyID); if (pt->pt_dict != NULL) { typval_T dtv; @@ -4578,25 +4590,24 @@ 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; - char *start = skipwhite(*arg + 1); + char *curly_expr = skipwhite(*arg + 1); char buf[NUMBUFLEN]; - // First check if it's not a curly-braces thing: {expr}. + // First check if it's not a curly-braces expression: {expr}. // Must do this without evaluating, otherwise a function may be called // twice. Unfortunately this means we need to call eval1() twice for the // first item. - // But {} is an empty Dictionary. - if (*start != '}') { - if (eval1(&start, &tv, false) == FAIL) { // recursive! - return FAIL; - } - if (*skipwhite(start) == '}') { - return NOTDONE; - } + // "{}" is an empty Dictionary. + // "#{abc}" is never a curly-braces expression. + if (*curly_expr != '}' + && !literal + && eval1(&curly_expr, &tv, false) == OK + && *skipwhite(curly_expr) == '}') { + return NOTDONE; } dict_T *d = NULL; @@ -4760,19 +4771,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) { @@ -4787,20 +4785,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; @@ -4846,7 +4844,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) break; } - vimvars[VV_KEY].vv_str = (char *)vim_strsave(di->di_key); + vimvars[VV_KEY].vv_str = xstrdup((char *)di->di_key); int r = filter_map_one(&di->di_tv, expr, map, &rem); tv_clear(&vimvars[VV_KEY].vv_tv); if (r == FAIL || did_emsg) { @@ -4988,9 +4986,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; } @@ -5010,19 +5007,12 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) 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); } @@ -5060,7 +5050,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; } @@ -5113,7 +5103,7 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) func_ptr_ref(pt->pt_func); xfree(name); } else { - pt->pt_name = (char_u *)name; + pt->pt_name = name; func_ref((char_u *)name); } @@ -5130,46 +5120,6 @@ 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. @@ -5186,121 +5136,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 @@ -5522,126 +5364,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 @@ -5694,7 +5416,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; @@ -5778,7 +5500,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; @@ -5799,8 +5522,14 @@ 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((char_u *)callback->data.funcref); callback->type = kCallbackFuncref; } } else if (nlua_is_table_from_lua(arg)) { @@ -5828,6 +5557,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 @@ -5839,7 +5569,7 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co switch (callback->type) { case kCallbackFuncref: name = callback->data.funcref; - int len = (int)STRLEN(name); + int len = (int)strlen(name); if (len >= 6 && !memcmp(name, "v:lua.", 6)) { name += 6; len = check_luafunc_name(name, false); @@ -5858,13 +5588,8 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co break; case kCallbackLua: - rv = nlua_call_ref(callback->data.luaref, NULL, args, true, NULL); - switch (rv.type) { - case kObjectTypeBoolean: - return rv.data.boolean; - default: - return false; - } + rv = nlua_call_ref(callback->data.luaref, NULL, args, false, NULL); + return (rv.type == kObjectTypeBoolean && rv.data.boolean == true); case kCallbackNone: return false; @@ -5875,15 +5600,15 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co } funcexe_T funcexe = FUNCEXE_INIT; - funcexe.firstline = curwin->w_cursor.lnum; - funcexe.lastline = curwin->w_cursor.lnum; - funcexe.evaluate = true; - funcexe.partial = partial; + funcexe.fe_firstline = curwin->w_cursor.lnum; + funcexe.fe_lastline = curwin->w_cursor.lnum; + funcexe.fe_evaluate = true; + funcexe.fe_partial = partial; 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) { @@ -6186,9 +5911,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; @@ -6213,7 +5939,7 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) buf_T *buf = buflist_findnr((int)tv->vval.v_number); if (buf) { for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { - for (char *p = (char *)ml_get_buf(buf, lnum, false); *p != NUL; p++) { + for (char *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) { *len += 1; } *len += 1; @@ -6231,7 +5957,7 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) char *ret = xmalloc((size_t)(*len) + 1); char *end = ret; for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { - for (char *p = (char *)ml_get_buf(buf, lnum, false); *p != NUL; p++) { + for (char *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) { *end++ = (*p == '\n') ? NUL : *p; } *end++ = '\n'; @@ -6245,20 +5971,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'; } }); @@ -6280,7 +6009,7 @@ int buf_byteidx_to_charidx(buf_T *buf, linenr_T lnum, int byteidx) lnum = buf->b_ml.ml_line_count; } - char *str = (char *)ml_get_buf(buf, lnum, false); + char *str = ml_get_buf(buf, lnum, false); if (*str == NUL) { return 0; @@ -6318,7 +6047,7 @@ int buf_charidx_to_byteidx(buf_T *buf, linenr_T lnum, int charidx) lnum = buf->b_ml.ml_line_count; } - char *str = (char *)ml_get_buf(buf, lnum, false); + char *str = ml_get_buf(buf, lnum, false); // Convert the character offset to a byte offset char *t = str; @@ -6371,14 +6100,14 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret if (charcol) { len = mb_charlen(ml_get(pos.lnum)); } else { - len = (int)STRLEN(ml_get(pos.lnum)); + len = (int)strlen(ml_get(pos.lnum)); } // We accept "$" for the column number: last column. listitem_T *li = tv_list_find(l, 1L); if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING && TV_LIST_ITEM_TV(li)->vval.v_string != NULL - && STRCMP(TV_LIST_ITEM_TV(li)->vval.v_string, "$") == 0) { + && strcmp(TV_LIST_ITEM_TV(li)->vval.v_string, "$") == 0) { pos.col = len + 1; } @@ -6457,7 +6186,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret if (charcol) { pos.col = (colnr_T)mb_charlen(get_cursor_line_ptr()); } else { - pos.col = (colnr_T)STRLEN(get_cursor_line_ptr()); + pos.col = (colnr_T)strlen(get_cursor_line_ptr()); } } return &pos; @@ -6465,11 +6194,14 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret return NULL; } -/// Convert list in "arg" into a position and optional file number. -/// When "fnump" is NULL there is no file number, only 3 items. +/// Convert list in "arg" into position "posp" and optional file number "fnump". +/// When "fnump" is NULL there is no file number, only 3 items: [lnum, col, off] /// Note that the column is passed on as-is, the caller may want to decrement /// it to use 1 for the first column. /// +/// @param charcol if true, use the column as the character index instead of the +/// byte index. +/// /// @return FAIL when conversion is not possible, doesn't check the position for /// validity. int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool charcol) @@ -6509,13 +6241,16 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool c return FAIL; } // If character position is specified, then convert to byte position + // If the line number is zero use the cursor line. if (charcol) { // Get the text for the specified line in a loaded buffer buf_T *buf = buflist_findnr(fnump == NULL ? curbuf->b_fnum : *fnump); if (buf == NULL || buf->b_ml.ml_mfp == NULL) { return FAIL; } - n = buf_charidx_to_byteidx(buf, posp->lnum, (int)n) + 1; + n = buf_charidx_to_byteidx(buf, + posp->lnum == 0 ? curwin->w_cursor.lnum : posp->lnum, + (int)n) + 1; } posp->col = (colnr_T)n; @@ -6540,7 +6275,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; } @@ -6627,7 +6362,7 @@ int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbo } *alias = temp_string; *arg = (const char *)skipwhite(p); - return (int)STRLEN(temp_string); + return (int)strlen(temp_string); } len += get_id_len(arg); @@ -6670,7 +6405,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 == '\'') { @@ -6753,7 +6489,7 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex char *temp_result = eval_to_string(expr_start + 1, &nextcmd, false); if (temp_result != NULL && nextcmd == NULL) { - retval = xmalloc(STRLEN(temp_result) + (size_t)(expr_start - in_start) + retval = xmalloc(strlen(temp_result) + (size_t)(expr_start - in_start) + (size_t)(in_end - expr_end) + 1); STRCPY(retval, in_start); STRCAT(retval, temp_result); @@ -6783,18 +6519,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) { @@ -6927,12 +6670,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,11 +6765,14 @@ char *set_cmdarg(exarg_T *eap, char *oldarg) len += 10; // " ++ff=unix" } if (eap->force_enc != 0) { - len += STRLEN(eap->cmd + eap->force_enc) + 7; + len += strlen(eap->cmd + eap->force_enc) + 7; } 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); @@ -7043,22 +6790,27 @@ char *set_cmdarg(exarg_T *eap, char *oldarg) } if (eap->force_ff != 0) { - snprintf(newval + STRLEN(newval), newval_len, " ++ff=%s", + snprintf(newval + strlen(newval), newval_len, " ++ff=%s", eap->force_ff == 'u' ? "unix" : eap->force_ff == 'd' ? "dos" : "mac"); } if (eap->force_enc != 0) { - snprintf(newval + STRLEN(newval), newval_len, " ++enc=%s", + snprintf(newval + strlen(newval), newval_len, " ++enc=%s", eap->cmd + eap->force_enc); } if (eap->bad_char == BAD_KEEP) { - STRCPY(newval + STRLEN(newval), " ++bad=keep"); + STRCPY(newval + strlen(newval), " ++bad=keep"); } else if (eap->bad_char == BAD_DROP) { - STRCPY(newval + STRLEN(newval), " ++bad=drop"); + STRCPY(newval + strlen(newval), " ++bad=drop"); } else if (eap->bad_char != 0) { - snprintf(newval + STRLEN(newval), newval_len, " ++bad=%c", + 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; } @@ -7113,9 +6865,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: @@ -7639,7 +7390,7 @@ void ex_echo(exarg_T *eap) tv_clear(&rettv); arg = skipwhite(arg); } - eap->nextcmd = (char *)check_nextcmd((char_u *)arg); + eap->nextcmd = check_nextcmd(arg); if (eap->skip) { emsg_skip--; @@ -7736,7 +7487,7 @@ void ex_execute(exarg_T *eap) emsg_skip--; } - eap->nextcmd = (char *)check_nextcmd((char_u *)arg); + eap->nextcmd = check_nextcmd(arg); } /// Skip over the name of an option: "&option", "&g:option" or "&l:option". @@ -7745,19 +7496,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)) { @@ -7826,7 +7577,7 @@ bool script_autoload(const char *const name, const size_t name_len, const bool r // "autoload/", it's always the same. int i = 0; for (; i < ga_loaded.ga_len; i++) { - if (STRCMP(((char **)ga_loaded.ga_data)[i] + 9, scriptname + 9) == 0) { + if (strcmp(((char **)ga_loaded.ga_data)[i] + 9, scriptname + 9) == 0) { break; } } @@ -7861,9 +7612,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 @@ -7889,7 +7639,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) { @@ -7901,7 +7651,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; } } @@ -7925,8 +7675,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'; @@ -8067,8 +7816,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) { @@ -8079,7 +7828,7 @@ repeat: // Append a path separator to a directory. if (os_isdir(*fnamep)) { // Make room for one or two extra characters. - *fnamep = xstrnsave(*fnamep, STRLEN(*fnamep) + 2); + *fnamep = xstrnsave(*fnamep, strlen(*fnamep) + 2); xfree(*bufp); // free any allocated file name *bufp = *fnamep; add_pathsep(*fnamep); @@ -8113,17 +7862,17 @@ 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); xfree(s); } - size_t namelen = STRLEN(dirname); + size_t namelen = strlen(dirname); // Do not call shorten_fname() here since it removes the prefix // even though the path does not have a prefix. - if (FNAMENCMP(p, dirname, namelen) == 0) { + if (path_fnamencmp(p, dirname, namelen) == 0) { p += namelen; if (vim_ispathsep(*p)) { while (*p && vim_ispathsep(*p)) { @@ -8155,7 +7904,7 @@ repeat: } char *tail = path_tail(*fnamep); - *fnamelen = STRLEN(*fnamep); + *fnamelen = strlen(*fnamep); // ":h" - head, remove "/file_name", can be repeated // Don't remove the first "/" or "c:\" @@ -8257,7 +8006,7 @@ repeat: s++; } - int sep = (char_u)(*s++); + int sep = (uint8_t)(*s++); if (sep) { // find end of pattern p = vim_strchr(s, sep); @@ -8272,7 +8021,7 @@ repeat: *usedlen = (size_t)(p + 1 - src); s = do_string_sub(str, pat, sub, NULL, flags); *fnamep = s; - *fnamelen = STRLEN(s); + *fnamelen = strlen(s); xfree(*bufp); *bufp = s; didit = true; @@ -8290,17 +8039,17 @@ 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; } xfree(*bufp); *bufp = *fnamep = p; - *fnamelen = STRLEN(p); + *fnamelen = strlen(p); *usedlen += 2; } @@ -8312,7 +8061,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; @@ -8331,11 +8080,11 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { char *tail = str; - char *end = str + STRLEN(str); + char *end = str + strlen(str); while (vim_regexec_nl(®match, (char_u *)str, (colnr_T)(tail - str))) { // Skip empty match except for first match. if (regmatch.startp[0] == regmatch.endp[0]) { - if ((char_u *)zero_width == regmatch.startp[0]) { + if (zero_width == regmatch.startp[0]) { // avoid getting stuck on a match with an empty string int i = utfc_ptr2len(tail); memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i); @@ -8343,7 +8092,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags tail += i; continue; } - zero_width = (char *)regmatch.startp[0]; + zero_width = regmatch.startp[0]; } // Get some space for a temporary buffer to do the substitution @@ -8356,14 +8105,14 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags (regmatch.endp[0] - regmatch.startp[0]))); // copy the text up to where the match is - int i = (int)(regmatch.startp[0] - (char_u *)tail); + 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, REGSUB_COPY | REGSUB_MAGIC); ga.ga_len += i + sublen - 1; - tail = (char *)regmatch.endp[0]; + tail = regmatch.endp[0]; if (*tail == NUL) { break; } @@ -8497,9 +8246,9 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, boo tv_list_ref(arguments); funcexe_T funcexe = FUNCEXE_INIT; - funcexe.firstline = curwin->w_cursor.lnum; - funcexe.lastline = curwin->w_cursor.lnum; - funcexe.evaluate = true; + funcexe.fe_firstline = curwin->w_cursor.lnum; + funcexe.fe_lastline = curwin->w_cursor.lnum; + funcexe.fe_evaluate = true; (void)call_func(func, name_len, &rettv, 2, argvars, &funcexe); tv_list_unref(arguments); @@ -8606,7 +8355,7 @@ void ex_checkhealth(exarg_T *eap) return; } - size_t bufsize = STRLEN(eap->arg) + sizeof("call health#check('')"); + size_t bufsize = strlen(eap->arg) + sizeof("call health#check('')"); char *buf = xmalloc(bufsize); snprintf(buf, bufsize, "call health#check('%s')", eap->arg); @@ -8630,10 +8379,10 @@ void invoke_prompt_callback(void) if (curbuf->b_prompt_callback.type == kCallbackNone) { return; } - char *text = (char *)ml_get(lnum); - char *prompt = (char *)prompt_text(); - if (STRLEN(text) >= STRLEN(prompt)) { - text += STRLEN(prompt); + char *text = ml_get(lnum); + char *prompt = prompt_text(); + if (strlen(text) >= strlen(prompt)) { + text += strlen(prompt); } argv[0].v_type = VAR_STRING; argv[0].vval.v_string = xstrdup(text); @@ -8656,9 +8405,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". @@ -8856,7 +8605,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; } @@ -8871,10 +8620,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); } |