aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-11-29 22:39:54 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-11-29 22:39:54 +0000
commit21cb7d04c387e4198ca8098a884c78b56ffcf4c2 (patch)
tree84fe5690df1551f0bb2bdfe1a13aacd29ebc1de7 /src/nvim/eval.c
parentd9c904f85a23a496df4eb6be42aa43f007b22d50 (diff)
parent4a8bf24ac690004aedf5540fa440e788459e5e34 (diff)
downloadrneovim-colorcolchar.tar.gz
rneovim-colorcolchar.tar.bz2
rneovim-colorcolchar.zip
Merge remote-tracking branch 'upstream/master' into colorcolcharcolorcolchar
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r--src/nvim/eval.c2885
1 files changed, 1727 insertions, 1158 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 4392ea306f..f4479d06a6 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1,23 +1,24 @@
-// This is an open source non-commercial project. Dear PVS-Studio, please check
-// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-
// 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 <sys/stat.h>
+#include <sys/types.h>
#include "auto/config.h"
+#include "nvim/api/private/converter.h"
#include "nvim/api/private/defs.h"
-#include "nvim/ascii.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/ascii_defs.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/channel.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/cmdhist.h"
#include "nvim/cursor.h"
#include "nvim/edit.h"
@@ -28,7 +29,6 @@
#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"
@@ -36,20 +36,21 @@
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/ex_session.h"
+#include "nvim/func_attr.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/grid_defs.h"
+#include "nvim/hashtab.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/macros_defs.h"
#include "nvim/main.h"
-#include "nvim/map.h"
+#include "nvim/map_defs.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
@@ -59,14 +60,16 @@
#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/option_vars.h"
#include "nvim/optionstr.h"
#include "nvim/os/fileio.h"
-#include "nvim/os/fs_defs.h"
+#include "nvim/os/fs.h"
+#include "nvim/os/lang.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/pos_defs.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
@@ -74,28 +77,43 @@
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
-#include "nvim/types.h"
+#include "nvim/types_defs.h"
#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
#include "nvim/usercmd.h"
#include "nvim/version.h"
-#include "nvim/vim.h"
+#include "nvim/vim_defs.h"
#include "nvim/window.h"
// TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead
#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
+#define MAX_CALLBACK_DEPTH 20
+
+static const char *e_missbrac = N_("E111: Missing ']'");
+static const char *e_list_end = N_("E697: Missing end of List ']': %s");
+static const char e_cannot_slice_dictionary[]
+ = N_("E719: Cannot slice a Dictionary");
+static const char e_cannot_index_special_variable[]
+ = N_("E909: Cannot index a special variable");
+static const 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[]
+static const char *e_write2 = N_("E80: Error while writing: %s");
+static const char e_cannot_index_a_funcref[]
+ = N_("E695: Cannot index a Funcref");
+static const char e_variable_nested_too_deep_for_making_copy[]
+ = N_("E698: Variable nested too deep for making a copy");
+static const char e_string_list_or_blob_required[]
+ = N_("E1098: String, List or Blob required");
+static const char e_expression_too_recursive_str[]
+ = N_("E1169: Expression too recursive: %s");
+static const char e_dot_can_only_be_used_on_dictionary_str[]
= N_("E1203: Dot can only be used on a dictionary: %s");
+static const char e_empty_function_name[]
+ = N_("E1192: Empty function name");
+static const char e_argument_of_str_must_be_list_string_dictionary_or_blob[]
+ = N_("E1250: Argument of %s must be a List, String, Dictionary or Blob");
static char * const namespace_char = "abglstvw";
@@ -109,22 +127,11 @@ static hashtab_T compat_hashtab;
/// Used for checking if local variables or arguments used in a lambda.
bool *eval_lavars_used = NULL;
-/// Array to hold the hashtab with variables local to each sourced script.
-/// Each item holds a variable (nameless) that points to the dict_T.
-typedef struct {
- ScopeDictDictItem sv_var;
- dict_T sv_dict;
-} scriptvar_T;
-
-static garray_T ga_scripts = { 0, 0, sizeof(scriptvar_T *), 4, NULL };
-#define SCRIPT_SV(id) (((scriptvar_T **)ga_scripts.ga_data)[(id) - 1])
+#define SCRIPT_SV(id) (SCRIPT_ITEM(id)->sn_vars)
#define SCRIPT_VARS(id) (SCRIPT_SV(id)->sv_dict.dv_hashtab)
static int echo_attr = 0; // attributes used for ":echo"
-// The names of packages that once were loaded are remembered.
-static garray_T ga_loaded = { 0, 0, sizeof(char *), 4, NULL };
-
/// Info used by a ":for" loop.
typedef struct {
int fi_semicolon; // true if ending in '; var]'
@@ -259,6 +266,7 @@ static struct vimvar {
VV(VV_ARGV, "argv", VAR_LIST, VV_RO),
VV(VV_COLLATE, "collate", VAR_STRING, VV_RO),
VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO),
+ VV(VV_MAXCOL, "maxcol", VAR_NUMBER, VV_RO),
// Neovim
VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO),
VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO),
@@ -293,6 +301,13 @@ static partial_T *vvlua_partial;
/// v: hashtab
#define vimvarht vimvardict.dv_hashtab
+/// Enum used by filter(), map() and mapnew()
+typedef enum {
+ FILTERMAP_FILTER,
+ FILTERMAP_MAP,
+ FILTERMAP_MAPNEW,
+} filtermap_T;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval.c.generated.h"
#endif
@@ -406,11 +421,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, (char *)p->vv_di.di_key);
+ hash_add(&vimvarht, p->vv_di.di_key);
}
if (p->vv_flags & VV_COMPAT) {
// add to compat scope dict
- hash_add(&compat_hashtab, (char *)p->vv_di.di_key);
+ hash_add(&compat_hashtab, p->vv_di.di_key);
}
}
vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
@@ -440,8 +455,8 @@ void eval_init(void)
set_vim_var_dict(VV_EVENT, tv_dict_alloc_lock(VAR_FIXED));
set_vim_var_list(VV_ERRORS, tv_list_alloc(kListLenUnknown));
set_vim_var_nr(VV_STDERR, CHAN_STDERR);
- set_vim_var_nr(VV_SEARCHFORWARD, 1L);
- set_vim_var_nr(VV_HLSEARCH, 1L);
+ set_vim_var_nr(VV_SEARCHFORWARD, 1);
+ set_vim_var_nr(VV_HLSEARCH, 1);
set_vim_var_nr(VV_COUNT1, 1);
set_vim_var_nr(VV_TYPE_NUMBER, VAR_TYPE_NUMBER);
set_vim_var_nr(VV_TYPE_STRING, VAR_TYPE_STRING);
@@ -459,6 +474,7 @@ void eval_init(void)
set_vim_var_nr(VV_NUMBERMIN, VARNUMBER_MIN);
set_vim_var_nr(VV_NUMBERSIZE, sizeof(varnumber_T) * 8);
set_vim_var_special(VV_EXITING, kSpecialVarNull);
+ set_vim_var_nr(VV_MAXCOL, MAXCOL);
set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
@@ -473,7 +489,7 @@ void eval_init(void)
}
#if defined(EXITFREE)
-void eval_clear(void)
+static void evalvars_clear(void)
{
for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) {
struct vimvar *p = &vimvars[i];
@@ -488,27 +504,28 @@ void eval_clear(void)
hash_init(&vimvarht); // garbage_collect() will access it
hash_clear(&compat_hashtab);
- free_scriptnames();
-# ifdef HAVE_WORKING_LIBINTL
- free_locales();
-# endif
-
// global variables
vars_clear(&globvarht);
- // autoloaded script names
- ga_clear_strings(&ga_loaded);
-
- // Script-local variables. First clear all the variables and in a second
- // loop free the scriptvar_T, because a variable in one script might hold
- // a reference to the whole scope of another script.
- for (int i = 1; i <= ga_scripts.ga_len; i++) {
+ // Script-local variables. Clear all the variables here.
+ // The scriptvar_T is cleared later in free_scriptnames(), because a
+ // variable in one script might hold a reference to the whole scope of
+ // another script.
+ for (int i = 1; i <= script_items.ga_len; i++) {
vars_clear(&SCRIPT_VARS(i));
}
- for (int i = 1; i <= ga_scripts.ga_len; i++) {
- xfree(SCRIPT_SV(i));
- }
- ga_clear(&ga_scripts);
+}
+
+void eval_clear(void)
+{
+ evalvars_clear();
+ free_scriptnames(); // must come after evalvars_clear().
+# ifdef HAVE_WORKING_LIBINTL
+ free_locales();
+# endif
+
+ // autoloaded script names
+ free_autoload_scriptnames();
// unreferenced lists and dicts
(void)garbage_collect(false);
@@ -604,7 +621,7 @@ int var_redir_start(char *name, int append)
/// :redir => foo
/// :let foo
/// :redir END
-void var_redir_str(char *value, int value_len)
+void var_redir_str(const char *value, int value_len)
{
if (redir_lval == NULL) {
return;
@@ -654,19 +671,27 @@ void var_redir_stop(void)
int eval_charconvert(const char *const enc_from, const char *const enc_to,
const char *const fname_from, const char *const fname_to)
{
- bool err = false;
+ const sctx_T saved_sctx = current_sctx;
set_vim_var_string(VV_CC_FROM, enc_from, -1);
set_vim_var_string(VV_CC_TO, enc_to, -1);
set_vim_var_string(VV_FNAME_IN, fname_from, -1);
set_vim_var_string(VV_FNAME_OUT, fname_to, -1);
+ sctx_T *ctx = get_option_sctx("charconvert");
+ if (ctx != NULL) {
+ current_sctx = *ctx;
+ }
+
+ bool err = false;
if (eval_to_bool(p_ccv, &err, NULL, false)) {
err = true;
}
+
set_vim_var_string(VV_CC_FROM, NULL, -1);
set_vim_var_string(VV_CC_TO, NULL, -1);
set_vim_var_string(VV_FNAME_IN, NULL, -1);
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
+ current_sctx = saved_sctx;
if (err) {
return FAIL;
@@ -676,28 +701,57 @@ int eval_charconvert(const char *const enc_from, const char *const enc_to,
void eval_diff(const char *const origfile, const char *const newfile, const char *const outfile)
{
- bool err = false;
-
+ const sctx_T saved_sctx = current_sctx;
set_vim_var_string(VV_FNAME_IN, origfile, -1);
set_vim_var_string(VV_FNAME_NEW, newfile, -1);
set_vim_var_string(VV_FNAME_OUT, outfile, -1);
- (void)eval_to_bool(p_dex, &err, NULL, false);
+
+ sctx_T *ctx = get_option_sctx("diffexpr");
+ if (ctx != NULL) {
+ current_sctx = *ctx;
+ }
+
+ // errors are ignored
+ typval_T *tv = eval_expr(p_dex, NULL);
+ tv_free(tv);
+
set_vim_var_string(VV_FNAME_IN, NULL, -1);
set_vim_var_string(VV_FNAME_NEW, NULL, -1);
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
+ current_sctx = saved_sctx;
}
void eval_patch(const char *const origfile, const char *const difffile, const char *const outfile)
{
- bool err = false;
-
+ const sctx_T saved_sctx = current_sctx;
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(p_pex, &err, NULL, false);
+
+ sctx_T *ctx = get_option_sctx("patchexpr");
+ if (ctx != NULL) {
+ current_sctx = *ctx;
+ }
+
+ // errors are ignored
+ typval_T *tv = eval_expr(p_pex, NULL);
+ tv_free(tv);
+
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);
+ current_sctx = saved_sctx;
+}
+
+void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip)
+{
+ *evalarg = (evalarg_T){ .eval_flags = skip ? 0 : EVAL_EVALUATE };
+ if (eap != NULL) {
+ if (getline_equal(eap->getline, eap->cookie, getsourceline)) {
+ evalarg->eval_getline = eap->getline;
+ evalarg->eval_cookie = eap->cookie;
+ }
+ }
}
/// Top level evaluation function, returning a boolean.
@@ -706,15 +760,18 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch
/// @param skip only parse, don't execute
///
/// @return true or false.
-int eval_to_bool(char *arg, bool *error, char **nextcmd, int skip)
+int eval_to_bool(char *arg, bool *error, exarg_T *eap, int skip)
{
typval_T tv;
bool retval = false;
+ evalarg_T evalarg;
+
+ fill_evalarg_from_eap(&evalarg, eap, skip);
if (skip) {
emsg_skip++;
}
- if (eval0(arg, &tv, nextcmd, !skip) == FAIL) {
+ if (eval0(arg, &tv, eap, &evalarg) == FAIL) {
*error = true;
} else {
*error = false;
@@ -726,19 +783,23 @@ int eval_to_bool(char *arg, bool *error, char **nextcmd, int skip)
if (skip) {
emsg_skip--;
}
+ clear_evalarg(&evalarg, eap);
return retval;
}
/// Call eval1() and give an error message if not done at a lower level.
-static int eval1_emsg(char **arg, typval_T *rettv, bool evaluate)
+static int eval1_emsg(char **arg, typval_T *rettv, exarg_T *eap)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
const char *const start = *arg;
const int did_emsg_before = did_emsg;
const int called_emsg_before = called_emsg;
+ evalarg_T evalarg;
- const int ret = eval1(arg, rettv, evaluate);
+ fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip);
+
+ const int ret = eval1(arg, rettv, &evalarg);
if (ret == FAIL) {
// Report the invalid expression unless the expression evaluation has
// been cancelled due to an aborting error, an interrupt, or an
@@ -750,6 +811,7 @@ static int eval1_emsg(char **arg, typval_T *rettv, bool evaluate)
semsg(_(e_invexpr2), start);
}
}
+ clear_evalarg(&evalarg, eap);
return ret;
}
@@ -762,39 +824,50 @@ bool eval_expr_valid_arg(const typval_T *const tv)
&& (tv->v_type != VAR_STRING || (tv->vval.v_string != NULL && *tv->vval.v_string != NUL));
}
-int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv)
- FUNC_ATTR_NONNULL_ARG(1, 2, 4)
+/// Evaluate an expression, which can be a function, partial or string.
+/// Pass arguments "argv[argc]".
+/// Return the result in "rettv" and OK or FAIL.
+///
+/// @param want_func if true, treat a string as a function name, not an expression
+int eval_expr_typval(const typval_T *expr, bool want_func, typval_T *argv, int argc,
+ typval_T *rettv)
+ FUNC_ATTR_NONNULL_ALL
{
+ char buf[NUMBUFLEN];
funcexe_T funcexe = FUNCEXE_INIT;
- if (expr->v_type == VAR_FUNC) {
- const char *const s = expr->vval.v_string;
+ if (expr->v_type == VAR_PARTIAL) {
+ partial_T *const partial = expr->vval.v_partial;
+ if (partial == NULL) {
+ return FAIL;
+ }
+ const char *const s = partial_name(partial);
if (s == NULL || *s == NUL) {
return FAIL;
}
funcexe.fe_evaluate = true;
+ funcexe.fe_partial = partial;
if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) {
return FAIL;
}
- } else if (expr->v_type == VAR_PARTIAL) {
- partial_T *const partial = expr->vval.v_partial;
- const char *const s = partial_name(partial);
+ } else if (expr->v_type == VAR_FUNC || want_func) {
+ const char *const s = (expr->v_type == VAR_FUNC
+ ? expr->vval.v_string
+ : tv_get_string_buf_chk(expr, buf));
if (s == NULL || *s == NUL) {
return FAIL;
}
funcexe.fe_evaluate = true;
- funcexe.fe_partial = partial;
if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) {
return FAIL;
}
} else {
- char buf[NUMBUFLEN];
char *s = (char *)tv_get_string_buf_chk(expr, buf);
if (s == NULL) {
return FAIL;
}
s = skipwhite(s);
- if (eval1_emsg(&s, rettv, true) == FAIL) {
+ if (eval1_emsg(&s, rettv, NULL) == FAIL) {
return FAIL;
}
if (*skipwhite(s) != NUL) { // check for trailing chars after expr
@@ -813,7 +886,7 @@ bool eval_expr_to_bool(const typval_T *expr, bool *error)
{
typval_T argv, rettv;
- if (eval_expr_typval(expr, &argv, 0, &rettv) == FAIL) {
+ if (eval_expr_typval(expr, false, &argv, 0, &rettv) == FAIL) {
*error = true;
return false;
}
@@ -825,22 +898,23 @@ bool eval_expr_to_bool(const typval_T *expr, bool *error)
/// Top level evaluation function, returning a string
///
/// @param[in] arg String to evaluate.
-/// @param nextcmd Pointer to the start of the next Ex command.
/// @param[in] skip If true, only do parsing to nextcmd without reporting
/// errors or actually evaluating anything.
///
/// @return [allocated] string result of evaluation or NULL in case of error or
/// when skipping.
-char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip)
+char *eval_to_string_skip(char *arg, exarg_T *eap, const bool skip)
FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT
{
typval_T tv;
char *retval;
+ evalarg_T evalarg;
+ fill_evalarg_from_eap(&evalarg, eap, skip);
if (skip) {
emsg_skip++;
}
- if (eval0((char *)arg, &tv, (char **)nextcmd, !skip) == FAIL || skip) {
+ if (eval0(arg, &tv, eap, &evalarg) == FAIL || skip) {
retval = NULL;
} else {
retval = xstrdup(tv_get_string(&tv));
@@ -849,6 +923,7 @@ char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip
if (skip) {
emsg_skip--;
}
+ clear_evalarg(&evalarg, eap);
return retval;
}
@@ -856,48 +931,65 @@ char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip
/// Skip over an expression at "*pp".
///
/// @return FAIL for an error, OK otherwise.
-int skip_expr(char **pp)
+int skip_expr(char **pp, evalarg_T *const evalarg)
{
- typval_T rettv;
+ const int save_flags = evalarg == NULL ? 0 : evalarg->eval_flags;
+
+ // Don't evaluate the expression.
+ if (evalarg != NULL) {
+ evalarg->eval_flags &= ~EVAL_EVALUATE;
+ }
*pp = skipwhite(*pp);
- return eval1(pp, &rettv, false);
+ typval_T rettv;
+ int res = eval1(pp, &rettv, NULL);
+
+ if (evalarg != NULL) {
+ evalarg->eval_flags = save_flags;
+ }
+
+ return res;
+}
+
+/// Convert "tv" to a string.
+///
+/// @param convert when true convert a List into a sequence of lines.
+///
+/// @return an allocated string.
+static char *typval2string(typval_T *tv, bool convert)
+{
+ if (convert && tv->v_type == VAR_LIST) {
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char), 80);
+ if (tv->vval.v_list != NULL) {
+ tv_list_join(&ga, tv->vval.v_list, "\n");
+ if (tv_list_len(tv->vval.v_list) > 0) {
+ ga_append(&ga, NL);
+ }
+ }
+ ga_append(&ga, NUL);
+ return (char *)ga.ga_data;
+ }
+ return xstrdup(tv_get_string(tv));
}
/// Top level evaluation function, returning a string.
///
-/// @param convert when true convert a List into a sequence of lines and convert
-/// a Float to a String.
+/// @param convert when true convert a List into a sequence of lines.
///
-/// @return pointer to allocated memory, or NULL for failure.
-char *eval_to_string(char *arg, char **nextcmd, bool convert)
+/// @return pointer to allocated memory, or NULL for failure.
+char *eval_to_string(char *arg, bool convert)
{
typval_T tv;
char *retval;
- garray_T ga;
- if (eval0(arg, &tv, nextcmd, true) == FAIL) {
+ if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
retval = NULL;
} else {
- if (convert && tv.v_type == VAR_LIST) {
- ga_init(&ga, (int)sizeof(char), 80);
- if (tv.vval.v_list != NULL) {
- tv_list_join(&ga, tv.vval.v_list, "\n");
- if (tv_list_len(tv.vval.v_list) > 0) {
- ga_append(&ga, NL);
- }
- }
- ga_append(&ga, NUL);
- retval = (char *)ga.ga_data;
- } else if (convert && tv.v_type == VAR_FLOAT) {
- char numbuf[NUMBUFLEN];
- vim_snprintf(numbuf, NUMBUFLEN, "%g", tv.vval.v_float);
- retval = xstrdup(numbuf);
- } else {
- retval = xstrdup(tv_get_string(&tv));
- }
+ retval = typval2string(&tv, convert);
tv_clear(&tv);
}
+ clear_evalarg(&EVALARG_EVALUATE, NULL);
return retval;
}
@@ -906,7 +998,7 @@ char *eval_to_string(char *arg, char **nextcmd, bool convert)
/// textlock.
///
/// @param use_sandbox when true, use the sandbox.
-char *eval_to_string_safe(char *arg, char **nextcmd, int use_sandbox)
+char *eval_to_string_safe(char *arg, const bool use_sandbox)
{
char *retval;
funccal_entry_T funccal_entry;
@@ -916,7 +1008,7 @@ char *eval_to_string_safe(char *arg, char **nextcmd, int use_sandbox)
sandbox++;
}
textlock++;
- retval = eval_to_string(arg, nextcmd, false);
+ retval = eval_to_string(arg, false);
if (use_sandbox) {
sandbox--;
}
@@ -937,7 +1029,7 @@ varnumber_T eval_to_number(char *expr)
emsg_off++;
- if (eval1(&p, &rettv, true) == FAIL) {
+ if (eval1(&p, &rettv, &EVALARG_EVALUATE) == FAIL) {
retval = -1;
} else {
retval = tv_get_number_chk(&rettv, NULL);
@@ -952,12 +1044,18 @@ varnumber_T eval_to_number(char *expr)
///
/// @return an allocated typval_T with the result or
/// NULL when there is an error.
-typval_T *eval_expr(char *arg)
+typval_T *eval_expr(char *arg, exarg_T *eap)
{
typval_T *tv = xmalloc(sizeof(*tv));
- if (eval0(arg, tv, NULL, true) == FAIL) {
+ evalarg_T evalarg;
+
+ fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip);
+
+ if (eval0(arg, tv, eap, &evalarg) == FAIL) {
XFREE_CLEAR(tv);
}
+
+ clear_evalarg(&evalarg, eap);
return tv;
}
@@ -970,7 +1068,7 @@ void list_vim_vars(int *first)
/// List script-local variables, if there is a script.
void list_script_vars(int *first)
{
- if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len) {
+ if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) {
list_hashtable_vars(&SCRIPT_VARS(current_sctx.sc_sid), "s:", false, first);
}
}
@@ -992,7 +1090,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, (char *)vimvars[idx].vv_di.di_key);
+ hash_add(&vimvarht, vimvars[idx].vv_di.di_key);
}
}
@@ -1005,7 +1103,7 @@ void restore_vimvar(int idx, typval_T *save_tv)
return;
}
- hashitem_T *hi = hash_find(&vimvarht, (char *)vimvars[idx].vv_di.di_key);
+ hashitem_T *hi = hash_find(&vimvarht, vimvars[idx].vv_di.di_key);
if (HASHITEM_EMPTY(hi)) {
internal_error("restore_vimvar()");
} else {
@@ -1023,6 +1121,7 @@ list_T *eval_spell_expr(char *badword, char *expr)
typval_T rettv;
list_T *list = NULL;
char *p = skipwhite(expr);
+ const sctx_T saved_sctx = current_sctx;
// Set "v:val" to the bad word.
prepare_vimvar(VV_VAL, &save_val);
@@ -1031,8 +1130,12 @@ list_T *eval_spell_expr(char *badword, char *expr)
if (p_verbose == 0) {
emsg_off++;
}
+ sctx_T *ctx = get_option_sctx("spellsuggest");
+ if (ctx != NULL) {
+ current_sctx = *ctx;
+ }
- if (eval1(&p, &rettv, true) == OK) {
+ if (eval1(&p, &rettv, &EVALARG_EVALUATE) == OK) {
if (rettv.v_type != VAR_LIST) {
tv_clear(&rettv);
} else {
@@ -1044,6 +1147,7 @@ list_T *eval_spell_expr(char *badword, char *expr)
emsg_off--;
}
restore_vimvar(VV_VAL, &save_val);
+ current_sctx = saved_sctx;
return list;
}
@@ -1123,7 +1227,7 @@ fail:
///
/// @return [allocated] NULL when calling function fails, allocated string
/// otherwise.
-char *call_func_retstr(const char *const func, int argc, typval_T *argv)
+void *call_func_retstr(const char *const func, int argc, typval_T *argv)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
{
typval_T rettv;
@@ -1153,7 +1257,7 @@ void *call_func_retlist(const char *func, int argc, typval_T *argv)
typval_T rettv;
// All arguments are passed as strings, no conversion to number.
- if (call_vim_function((char *)func, argc, argv, &rettv) == FAIL) {
+ if (call_vim_function(func, argc, argv, &rettv) == FAIL) {
return NULL;
}
@@ -1167,11 +1271,13 @@ void *call_func_retlist(const char *func, int argc, typval_T *argv)
/// Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding
/// it in "*cp". Doesn't give error messages.
-int eval_foldexpr(char *arg, int *cp)
+int eval_foldexpr(win_T *wp, int *cp)
{
- typval_T tv;
- varnumber_T retval;
- int use_sandbox = was_set_insecurely(curwin, "foldexpr", OPT_LOCAL);
+ const sctx_T saved_sctx = current_sctx;
+ const bool use_sandbox = was_set_insecurely(wp, "foldexpr", OPT_LOCAL);
+
+ char *arg = wp->w_p_fde;
+ current_sctx = wp->w_p_script_ctx[WV_FDE].script_ctx;
emsg_off++;
if (use_sandbox) {
@@ -1179,7 +1285,10 @@ int eval_foldexpr(char *arg, int *cp)
}
textlock++;
*cp = NUL;
- if (eval0(arg, &tv, NULL, true) == FAIL) {
+
+ typval_T tv;
+ varnumber_T retval;
+ if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
retval = 0;
} else {
// If the result is a number, just return the number.
@@ -1191,22 +1300,61 @@ int eval_foldexpr(char *arg, int *cp)
// If the result is a string, check if there is a non-digit before
// the number.
char *s = tv.vval.v_string;
- if (!ascii_isdigit(*s) && *s != '-') {
- *cp = (char_u)(*s++);
+ if (*s != NUL && !ascii_isdigit(*s) && *s != '-') {
+ *cp = (uint8_t)(*s++);
}
retval = atol(s);
}
tv_clear(&tv);
}
+
emsg_off--;
if (use_sandbox) {
sandbox--;
}
textlock--;
+ clear_evalarg(&EVALARG_EVALUATE, NULL);
+ current_sctx = saved_sctx;
return (int)retval;
}
+/// Evaluate 'foldtext', returning an Array or a String (NULL_STRING on failure).
+Object eval_foldtext(win_T *wp)
+{
+ const bool use_sandbox = was_set_insecurely(wp, "foldtext", OPT_LOCAL);
+ char *arg = wp->w_p_fdt;
+ funccal_entry_T funccal_entry;
+
+ save_funccal(&funccal_entry);
+ if (use_sandbox) {
+ sandbox++;
+ }
+ textlock++;
+
+ typval_T tv;
+ Object retval;
+ if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
+ retval = STRING_OBJ(NULL_STRING);
+ } else {
+ if (tv.v_type == VAR_LIST) {
+ retval = vim_to_object(&tv);
+ } else {
+ retval = STRING_OBJ(cstr_to_string(tv_get_string(&tv)));
+ }
+ tv_clear(&tv);
+ }
+ clear_evalarg(&EVALARG_EVALUATE, NULL);
+
+ if (use_sandbox) {
+ sandbox--;
+ }
+ textlock--;
+ restore_funccal();
+
+ return retval;
+}
+
/// Get an lvalue
///
/// Lvalue may be
@@ -1242,9 +1390,8 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
if (skip) {
// When skipping just find the end of the name.
- lp->ll_name = (const char *)name;
- return (char *)find_name_end(name, NULL, NULL,
- FNE_INCL_BR | fne_flags);
+ lp->ll_name = name;
+ return (char *)find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags);
}
// Find the end of the name.
@@ -1277,8 +1424,8 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
lp->ll_name_len = strlen(lp->ll_name);
}
} else {
- lp->ll_name = (const char *)name;
- lp->ll_name_len = (size_t)((const char *)p - lp->ll_name);
+ lp->ll_name = name;
+ lp->ll_name_len = (size_t)(p - lp->ll_name);
}
// Without [idx] or .key we are done.
@@ -1321,14 +1468,22 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
}
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)) {
+ if (lp->ll_tv->v_type != VAR_LIST
+ && lp->ll_tv->v_type != VAR_DICT
+ && lp->ll_tv->v_type != VAR_BLOB) {
if (!quiet) {
emsg(_("E689: Can only index a List, Dictionary or Blob"));
}
return NULL;
}
+
+ // a NULL list/blob works like an empty list/blob, allocate one now.
+ if (lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list == NULL) {
+ tv_list_alloc_ret(lp->ll_tv, kListLenUnknown);
+ } else if (lp->ll_tv->v_type == VAR_BLOB && lp->ll_tv->vval.v_blob == NULL) {
+ tv_blob_alloc_ret(lp->ll_tv);
+ }
+
if (lp->ll_range) {
if (!quiet) {
emsg(_("E708: [:] must come last"));
@@ -1355,7 +1510,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
empty1 = true;
} else {
empty1 = false;
- if (eval1(&p, &var1, true) == FAIL) { // Recursive!
+ if (eval1(&p, &var1, &EVALARG_EVALUATE) == FAIL) { // Recursive!
return NULL;
}
if (!tv_check_str(&var1)) {
@@ -1370,7 +1525,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
if (*p == ':') {
if (lp->ll_tv->v_type == VAR_DICT) {
if (!quiet) {
- emsg(_(e_dictrange));
+ emsg(_(e_cannot_slice_dictionary));
}
tv_clear(&var1);
return NULL;
@@ -1389,7 +1544,8 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
lp->ll_empty2 = true;
} else {
lp->ll_empty2 = false;
- if (eval1(&p, &var2, true) == FAIL) { // Recursive!
+ // Recursive!
+ if (eval1(&p, &var2, &EVALARG_EVALUATE) == FAIL) {
tv_clear(&var1);
return NULL;
}
@@ -1425,25 +1581,23 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
}
lp->ll_list = NULL;
lp->ll_dict = lp->ll_tv->vval.v_dict;
- lp->ll_di = tv_dict_find(lp->ll_dict, (const char *)key, len);
+ lp->ll_di = tv_dict_find(lp->ll_dict, key, len);
// When assigning to a scope dictionary check that a function and
// variable name is valid (only variable name unless it is l: or
// g: dictionary). Disallow overwriting a builtin function.
if (rettv != NULL && lp->ll_dict->dv_scope != 0) {
char prevval;
- int wrong;
-
if (len != -1) {
prevval = key[len];
key[len] = NUL;
} else {
prevval = 0; // Avoid compiler warning.
}
- wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE
- && tv_is_func(*rettv)
- && var_wrong_func_name((const char *)key, lp->ll_di == NULL))
- || !valid_varname((const char *)key));
+ bool wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE
+ && tv_is_func(*rettv)
+ && var_wrong_func_name(key, lp->ll_di == NULL))
+ || !valid_varname(key));
if (len != -1) {
key[len] = prevval;
}
@@ -1480,7 +1634,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
if (len == -1) {
lp->ll_newkey = xstrdup(key);
} else {
- lp->ll_newkey = xstrnsave(key, (size_t)len);
+ lp->ll_newkey = xmemdupz(key, (size_t)len);
}
tv_clear(&var1);
break;
@@ -1500,26 +1654,19 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
lp->ll_n1 = 0;
} else {
// Is number or string.
- lp->ll_n1 = (long)tv_get_number(&var1);
+ lp->ll_n1 = (int)tv_get_number(&var1);
}
tv_clear(&var1);
const int bloblen = tv_blob_len(lp->ll_tv->vval.v_blob);
- if (lp->ll_n1 < 0 || lp->ll_n1 > bloblen
- || (lp->ll_range && lp->ll_n1 == bloblen)) {
- if (!quiet) {
- semsg(_(e_blobidx), (int64_t)lp->ll_n1);
- }
+ if (tv_blob_check_index(bloblen, lp->ll_n1, quiet) == FAIL) {
tv_clear(&var2);
return NULL;
}
if (lp->ll_range && !lp->ll_empty2) {
- lp->ll_n2 = (long)tv_get_number(&var2);
+ lp->ll_n2 = (int)tv_get_number(&var2);
tv_clear(&var2);
- if (lp->ll_n2 < 0 || lp->ll_n2 >= bloblen || lp->ll_n2 < lp->ll_n1) {
- if (!quiet) {
- semsg(_(e_blobidx), (int64_t)lp->ll_n2);
- }
+ if (tv_blob_check_range(bloblen, lp->ll_n1, lp->ll_n2, quiet) == FAIL) {
return NULL;
}
}
@@ -1532,24 +1679,15 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
lp->ll_n1 = 0;
} else {
// Is number or string.
- lp->ll_n1 = (long)tv_get_number(&var1);
+ lp->ll_n1 = (int)tv_get_number(&var1);
}
tv_clear(&var1);
lp->ll_dict = NULL;
lp->ll_list = lp->ll_tv->vval.v_list;
- lp->ll_li = tv_list_find(lp->ll_list, (int)lp->ll_n1);
- if (lp->ll_li == NULL) {
- if (lp->ll_n1 < 0) {
- lp->ll_n1 = 0;
- lp->ll_li = tv_list_find(lp->ll_list, (int)lp->ll_n1);
- }
- }
+ lp->ll_li = tv_list_check_range_index_one(lp->ll_list, &lp->ll_n1, quiet);
if (lp->ll_li == NULL) {
tv_clear(&var2);
- if (!quiet) {
- semsg(_(e_listidx), (int64_t)lp->ll_n1);
- }
return NULL;
}
@@ -1558,27 +1696,11 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
// When no index given: "lp->ll_empty2" is true.
// Otherwise "lp->ll_n2" is set to the second index.
if (lp->ll_range && !lp->ll_empty2) {
- lp->ll_n2 = (long)tv_get_number(&var2); // Is number or string.
+ lp->ll_n2 = (int)tv_get_number(&var2); // Is number or string.
tv_clear(&var2);
- if (lp->ll_n2 < 0) {
- listitem_T *ni = tv_list_find(lp->ll_list, (int)lp->ll_n2);
- if (ni == NULL) {
- if (!quiet) {
- semsg(_(e_listidx), (int64_t)lp->ll_n2);
- }
- return NULL;
- }
- lp->ll_n2 = tv_list_idx_of_item(lp->ll_list, ni);
- }
-
- // Check that lp->ll_n2 isn't before lp->ll_n1.
- if (lp->ll_n1 < 0) {
- lp->ll_n1 = tv_list_idx_of_item(lp->ll_list, lp->ll_li);
- }
- if (lp->ll_n2 < lp->ll_n1) {
- if (!quiet) {
- semsg(_(e_listidx), (int64_t)lp->ll_n2);
- }
+ if (tv_list_check_range_index_two(lp->ll_list,
+ &lp->ll_n1, lp->ll_li,
+ &lp->ll_n2, quiet) == FAIL) {
return NULL;
}
}
@@ -1607,11 +1729,10 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool
const char *op)
{
int cc;
- listitem_T *ri;
dictitem_T *di;
if (lp->ll_tv == NULL) {
- cc = (char_u)(*endp);
+ cc = (uint8_t)(*endp);
*endp = NUL;
if (lp->ll_blob != NULL) {
if (op != NULL && *op != '=') {
@@ -1627,33 +1748,14 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool
lp->ll_n2 = tv_blob_len(lp->ll_blob) - 1;
}
- if (lp->ll_n2 - lp->ll_n1 + 1 != tv_blob_len(rettv->vval.v_blob)) {
- emsg(_("E972: Blob value does not have the right number of bytes"));
+ if (tv_blob_set_range(lp->ll_blob, lp->ll_n1, lp->ll_n2, rettv) == FAIL) {
return;
}
- if (lp->ll_empty2) {
- lp->ll_n2 = tv_blob_len(lp->ll_blob);
- }
-
- for (int il = (int)lp->ll_n1, ir = 0; il <= (int)lp->ll_n2; il++) {
- tv_blob_set(lp->ll_blob, il, tv_blob_get(rettv->vval.v_blob, ir++));
- }
} else {
bool error = false;
const char val = (char)tv_get_number_chk(rettv, &error);
if (!error) {
- garray_T *const gap = &lp->ll_blob->bv_ga;
-
- // Allow for appending a byte. Setting a byte beyond
- // 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, (uint8_t)val);
- if (lp->ll_n1 == gap->ga_len) {
- gap->ga_len++;
- }
- }
- // error for invalid range was already given in get_lval()
+ tv_blob_set_append(lp->ll_blob, lp->ll_n1, (uint8_t)val);
}
}
} else if (op != NULL && *op != '=') {
@@ -1667,8 +1769,8 @@ 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),
- &tv, &di, true, false) == OK) {
+ if (eval_variable(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)
&& !tv_check_lock(&di->di_tv, lp->ll_name, TV_CSTRING)))
@@ -1687,60 +1789,13 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool
lp->ll_name, TV_CSTRING)) {
// Skip
} else if (lp->ll_range) {
- listitem_T *ll_li = lp->ll_li;
- int ll_n1 = (int)lp->ll_n1;
-
if (is_const) {
emsg(_("E996: Cannot lock a range"));
return;
}
- // Check whether any of the list items is locked
- for (ri = tv_list_first(rettv->vval.v_list);
- ri != NULL && ll_li != NULL;) {
- 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);
- if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1)) {
- break;
- }
- ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, ll_li);
- ll_n1++;
- }
-
- // Assign the List values to the list items.
- for (ri = tv_list_first(rettv->vval.v_list); ri != NULL;) {
- if (op != NULL && *op != '=') {
- eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri), op);
- } else {
- tv_clear(TV_LIST_ITEM_TV(lp->ll_li));
- tv_copy(TV_LIST_ITEM_TV(ri), TV_LIST_ITEM_TV(lp->ll_li));
- }
- ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri);
- if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) {
- break;
- }
- assert(lp->ll_li != NULL);
- if (TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) == NULL) {
- // Need to add an empty item.
- tv_list_append_number(lp->ll_list, 0);
- // ll_li may have become invalid after append, don’t use it.
- lp->ll_li = tv_list_last(lp->ll_list); // Valid again.
- } else {
- lp->ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li);
- }
- lp->ll_n1++;
- }
- if (ri != NULL) {
- emsg(_("E710: List value has more items than target"));
- } else if (lp->ll_empty2
- ? (lp->ll_li != NULL
- && TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) != NULL)
- : lp->ll_n1 != lp->ll_n2) {
- emsg(_("E711: List value has not enough items"));
- }
+ (void)tv_list_assign_range(lp->ll_list, rettv->vval.v_list,
+ lp->ll_n1, lp->ll_n2, lp->ll_empty2, op, lp->ll_name);
} else {
typval_T oldtv = TV_INITIAL_VALUE;
dict_T *dict = lp->ll_dict;
@@ -1762,7 +1817,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool
}
// Need to add an item to the Dictionary.
- di = tv_dict_item_alloc((const char *)lp->ll_newkey);
+ di = tv_dict_item_alloc(lp->ll_newkey);
if (tv_dict_add(lp->ll_tv->vval.v_dict, di) == FAIL) {
xfree(di);
return;
@@ -1798,7 +1853,7 @@ notify:
} else {
dictitem_T *di_ = lp->ll_di;
assert(di_->di_key != NULL);
- tv_dict_watcher_notify(dict, (char *)di_->di_key, lp->ll_tv, &oldtv);
+ tv_dict_watcher_notify(dict, di_->di_key, lp->ll_tv, &oldtv);
tv_clear(&oldtv);
}
}
@@ -1811,22 +1866,23 @@ notify:
/// @param[out] *errp set to true for an error, false otherwise;
///
/// @return a pointer that holds the info. Null when there is an error.
-void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip)
+void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, evalarg_T *const evalarg)
{
forinfo_T *fi = xcalloc(1, sizeof(forinfo_T));
- const char *expr;
typval_T tv;
list_T *l;
+ const bool skip = !(evalarg->eval_flags & EVAL_EVALUATE);
*errp = true; // Default: there is an error.
- expr = skip_var_list((char *)arg, &fi->fi_varcount, &fi->fi_semicolon);
+ const char *expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon);
if (expr == NULL) {
return fi;
}
expr = skipwhite(expr);
- if (expr[0] != 'i' || expr[1] != 'n' || !ascii_iswhite(expr[2])) {
+ if (expr[0] != 'i' || expr[1] != 'n'
+ || !(expr[2] == NUL || ascii_iswhite(expr[2]))) {
emsg(_("E690: Missing \"in\" after :for"));
return fi;
}
@@ -1834,7 +1890,8 @@ void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip)
if (skip) {
emsg_skip++;
}
- if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK) {
+ expr = skipwhite(expr + 2);
+ if (eval0((char *)expr, &tv, eap, evalarg) == OK) {
*errp = false;
if (!skip) {
if (tv.v_type == VAR_LIST) {
@@ -1856,7 +1913,7 @@ void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip)
// Make a copy, so that the iteration still works when the
// blob is changed.
- tv_blob_copy(&tv, &btv);
+ tv_blob_copy(tv.vval.v_blob, &btv);
fi->fi_blob = btv.vval.v_blob;
}
tv_clear(&tv);
@@ -1909,7 +1966,7 @@ bool next_for_item(void *fi_void, char *arg)
typval_T tv;
tv.v_type = VAR_STRING;
tv.v_lock = VAR_FIXED;
- tv.vval.v_string = xstrnsave(fi->fi_string + fi->fi_byte_idx, (size_t)len);
+ tv.vval.v_string = xmemdupz(fi->fi_string + fi->fi_byte_idx, (size_t)len);
fi->fi_byte_idx += len;
const int result
= ex_let_vars(arg, &tv, true, fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK;
@@ -1949,14 +2006,12 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
FUNC_ATTR_NONNULL_ALL
{
bool got_eq = false;
- int c;
- char *p;
if (cmdidx == CMD_let || cmdidx == CMD_const) {
xp->xp_context = EXPAND_USER_VARS;
if (strpbrk(arg, "\"'+-*/%.=!?~|&$([<>,#") == NULL) {
// ":let var1 var2 ...": find last space.
- for (p = arg + strlen(arg); p >= arg;) {
+ for (char *p = arg + strlen(arg); p >= arg;) {
xp->xp_pattern = p;
MB_PTR_BACK(arg, p);
if (ascii_iswhite(*p)) {
@@ -1970,7 +2025,7 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
: EXPAND_EXPRESSION;
}
while ((xp->xp_pattern = strpbrk(arg, "\"'+-*/%.=!?~|&$([<>,#")) != NULL) {
- c = (uint8_t)(*xp->xp_pattern);
+ int c = (uint8_t)(*xp->xp_pattern);
if (c == '&') {
c = (uint8_t)xp->xp_pattern[1];
if (c == '&') {
@@ -2027,7 +2082,7 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
}
arg = xp->xp_pattern;
if (*arg != NUL) {
- while ((c = (char_u)(*++arg)) != NUL && (c == ' ' || c == '\t')) {}
+ while ((c = (uint8_t)(*++arg)) != NUL && (c == ' ' || c == '\t')) {}
}
}
@@ -2037,7 +2092,7 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
|| cmdidx == CMD_echon
|| cmdidx == CMD_echomsg)
&& xp->xp_context == EXPAND_EXPRESSION) {
- for (;;) {
+ while (true) {
char *const n = skiptowhite(arg);
if (n == arg || ascii_iswhite_or_nul(*skipwhite(n))) {
@@ -2181,11 +2236,11 @@ int pattern_match(const char *pat, const char *text, bool ic)
// avoid 'l' flag in 'cpoptions'
char *save_cpo = p_cpo;
- p_cpo = empty_option;
- regmatch.regprog = vim_regcomp((char *)pat, RE_MAGIC + RE_STRING);
+ p_cpo = empty_string_option;
+ regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL) {
regmatch.rm_ic = ic;
- matches = vim_regexec_nl(&regmatch, (char *)text, (colnr_T)0);
+ matches = vim_regexec_nl(&regmatch, text, 0);
vim_regfree(regmatch.regprog);
}
p_cpo = save_cpo;
@@ -2199,21 +2254,24 @@ int pattern_match(const char *pat, const char *text, bool ic)
/// @param basetv "expr" for "expr->name(arg)"
///
/// @return OK or FAIL.
-static int eval_func(char **const arg, char *const name, const int name_len, typval_T *const rettv,
- const bool evaluate, typval_T *const basetv)
- FUNC_ATTR_NONNULL_ARG(1, 2, 4)
+static int eval_func(char **const arg, evalarg_T *const evalarg, char *const name,
+ const int name_len, typval_T *const rettv, const int flags,
+ typval_T *const basetv)
+ FUNC_ATTR_NONNULL_ARG(1, 3, 5)
{
+ const bool evaluate = flags & EVAL_EVALUATE;
char *s = name;
int len = name_len;
+ bool found_var = false;
if (!evaluate) {
- check_vars((const char *)s, (size_t)len);
+ check_vars(s, (size_t)len);
}
// If "s" is the name of a variable of type VAR_FUNC
// use its contents.
partial_T *partial;
- s = deref_func_name((const char *)s, &len, &partial, !evaluate);
+ s = deref_func_name(s, &len, &partial, !evaluate, &found_var);
// Need to make a copy, in case evaluating the arguments makes
// the name invalid.
@@ -2226,7 +2284,8 @@ 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(s, len, rettv, arg, &funcexe);
+ funcexe.fe_found_var = found_var;
+ int ret = get_func_tv(s, len, rettv, arg, evalarg, &funcexe);
xfree(s);
@@ -2250,6 +2309,26 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ
return ret;
}
+/// After using "evalarg" filled from "eap": free the memory.
+void clear_evalarg(evalarg_T *evalarg, exarg_T *eap)
+{
+ if (evalarg != NULL) {
+ if (evalarg->eval_tofree != NULL) {
+ if (eap != NULL) {
+ // We may need to keep the original command line, e.g. for
+ // ":let" it has the variable names. But we may also need the
+ // new one, "nextcmd" points into it. Keep both.
+ xfree(eap->cmdline_tofree);
+ eap->cmdline_tofree = *eap->cmdlinep;
+ *eap->cmdlinep = evalarg->eval_tofree;
+ } else {
+ xfree(evalarg->eval_tofree);
+ }
+ evalarg->eval_tofree = NULL;
+ }
+ }
+}
+
/// 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.
@@ -2259,17 +2338,17 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ
/// Put the result in "rettv" when returning OK and "evaluate" is true.
/// Note: "rettv.v_lock" is not set.
///
+/// @param evalarg can be NULL, &EVALARG_EVALUATE or a pointer.
+///
/// @return OK or FAIL.
-int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
+int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
{
- int ret;
- 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);
+ char *p = skipwhite(arg);
+ int ret = eval1(&p, rettv, evalarg);
if (ret != FAIL) {
end_error = !ends_excmd(*p);
@@ -2282,7 +2361,8 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
// been cancelled due to an aborting error, an interrupt, or an
// exception, or we already gave a more specific error.
// Also check called_emsg for when using assert_fails().
- if (!aborting() && did_emsg == did_emsg_before
+ if (!aborting()
+ && did_emsg == did_emsg_before
&& called_emsg == called_emsg_before) {
if (end_error) {
semsg(_(e_trailing_arg), p);
@@ -2290,10 +2370,21 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
semsg(_(e_invexpr2), arg);
}
}
- ret = FAIL;
+
+ if (eap != NULL && p != NULL) {
+ // Some of the expression may not have been consumed.
+ // Only execute a next command if it cannot be a "||" operator.
+ // The next command may be "catch".
+ char *nextcmd = check_nextcmd(p);
+ if (nextcmd != NULL && *nextcmd != '|') {
+ eap->nextcmd = nextcmd;
+ }
+ }
+ return FAIL;
}
- if (nextcmd != NULL) {
- *nextcmd = check_nextcmd(p);
+
+ if (eap != NULL) {
+ eap->nextcmd = check_nextcmd(p);
}
return ret;
@@ -2301,6 +2392,7 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
/// Handle top level expression:
/// expr2 ? expr1 : expr1
+/// expr2 ?? expr1
///
/// "arg" must point to the first non-white of the expression.
/// "arg" is advanced to the next non-white after the recognized expression.
@@ -2308,55 +2400,89 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
/// Note: "rettv.v_lock" is not set.
///
/// @return OK or FAIL.
-int eval1(char **arg, typval_T *rettv, int evaluate)
+int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{
- bool result;
- typval_T var2;
-
// Get the first variable.
- if (eval2(arg, rettv, evaluate) == FAIL) {
+ if (eval2(arg, rettv, evalarg) == FAIL) {
return FAIL;
}
- if ((*arg)[0] == '?') {
- result = false;
+ char *p = *arg;
+ if (*p == '?') {
+ const bool op_falsy = p[1] == '?';
+ evalarg_T *evalarg_used = evalarg;
+ evalarg_T local_evalarg;
+ if (evalarg == NULL) {
+ local_evalarg = (evalarg_T){ .eval_flags = 0 };
+ evalarg_used = &local_evalarg;
+ }
+ const int orig_flags = evalarg_used->eval_flags;
+ const bool evaluate = evalarg_used->eval_flags & EVAL_EVALUATE;
+
+ bool result = false;
if (evaluate) {
bool error = false;
- if (tv_get_number_chk(rettv, &error) != 0) {
+ if (op_falsy) {
+ result = tv2bool(rettv);
+ } else if (tv_get_number_chk(rettv, &error) != 0) {
result = true;
}
- tv_clear(rettv);
+ if (error || !op_falsy || !result) {
+ tv_clear(rettv);
+ }
if (error) {
return FAIL;
}
}
- // Get the second variable.
+ // Get the second variable. Recursive!
+ if (op_falsy) {
+ (*arg)++;
+ }
*arg = skipwhite(*arg + 1);
- if (eval1(arg, rettv, evaluate && result) == FAIL) { // recursive!
+ evalarg_used->eval_flags = (op_falsy ? !result : result)
+ ? orig_flags : (orig_flags & ~EVAL_EVALUATE);
+ typval_T var2;
+ if (eval1(arg, &var2, evalarg_used) == FAIL) {
+ evalarg_used->eval_flags = orig_flags;
return FAIL;
}
+ if (!op_falsy || !result) {
+ *rettv = var2;
+ }
- // Check for the ":".
- if ((*arg)[0] != ':') {
- emsg(_("E109: Missing ':' after '?'"));
- if (evaluate && result) {
- tv_clear(rettv);
+ if (!op_falsy) {
+ // Check for the ":".
+ p = *arg;
+ if (*p != ':') {
+ emsg(_("E109: Missing ':' after '?'"));
+ if (evaluate && result) {
+ tv_clear(rettv);
+ }
+ evalarg_used->eval_flags = orig_flags;
+ return FAIL;
}
- return FAIL;
- }
- // Get the third variable.
- *arg = skipwhite(*arg + 1);
- if (eval1(arg, &var2, evaluate && !result) == FAIL) { // Recursive!
- if (evaluate && result) {
- tv_clear(rettv);
+ // Get the third variable. Recursive!
+ *arg = skipwhite(*arg + 1);
+ evalarg_used->eval_flags = !result ? orig_flags : (orig_flags & ~EVAL_EVALUATE);
+ if (eval1(arg, &var2, evalarg_used) == FAIL) {
+ if (evaluate && result) {
+ tv_clear(rettv);
+ }
+ evalarg_used->eval_flags = orig_flags;
+ return FAIL;
+ }
+ if (evaluate && !result) {
+ *rettv = var2;
}
- return FAIL;
}
- if (evaluate && !result) {
- *rettv = var2;
+
+ if (evalarg == NULL) {
+ clear_evalarg(&local_evalarg, NULL);
+ } else {
+ evalarg->eval_flags = orig_flags;
}
}
@@ -2370,21 +2496,29 @@ int eval1(char **arg, typval_T *rettv, int evaluate)
/// "arg" is advanced to the next non-white after the recognized expression.
///
/// @return OK or FAIL.
-static int eval2(char **arg, typval_T *rettv, int evaluate)
+static int eval2(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{
- typval_T var2;
- bool error = false;
-
// Get the first variable.
- if (eval3(arg, rettv, evaluate) == FAIL) {
+ if (eval3(arg, rettv, evalarg) == FAIL) {
return FAIL;
}
- // Repeat until there is no following "||".
- bool first = true;
- bool result = false;
- while ((*arg)[0] == '|' && (*arg)[1] == '|') {
- if (evaluate && first) {
+ // Handle the "||" operator.
+ char *p = *arg;
+ if (p[0] == '|' && p[1] == '|') {
+ evalarg_T *evalarg_used = evalarg;
+ evalarg_T local_evalarg;
+ if (evalarg == NULL) {
+ local_evalarg = (evalarg_T){ .eval_flags = 0 };
+ evalarg_used = &local_evalarg;
+ }
+ const int orig_flags = evalarg_used->eval_flags;
+ const bool evaluate = evalarg_used->eval_flags & EVAL_EVALUATE;
+
+ bool result = false;
+
+ if (evaluate) {
+ bool error = false;
if (tv_get_number_chk(rettv, &error) != 0) {
result = true;
}
@@ -2392,28 +2526,41 @@ static int eval2(char **arg, typval_T *rettv, int evaluate)
if (error) {
return FAIL;
}
- first = false;
}
- // Get the second variable.
- *arg = skipwhite(*arg + 2);
- if (eval3(arg, &var2, evaluate && !result) == FAIL) {
- return FAIL;
- }
+ // Repeat until there is no following "||".
+ while (p[0] == '|' && p[1] == '|') {
+ // Get the second variable.
+ *arg = skipwhite(*arg + 2);
+ evalarg_used->eval_flags = !result ? orig_flags : (orig_flags & ~EVAL_EVALUATE);
+ typval_T var2;
+ if (eval3(arg, &var2, evalarg_used) == FAIL) {
+ return FAIL;
+ }
- // Compute the result.
- if (evaluate && !result) {
- if (tv_get_number_chk(&var2, &error) != 0) {
- result = true;
+ // Compute the result.
+ if (evaluate && !result) {
+ bool error = false;
+ if (tv_get_number_chk(&var2, &error) != 0) {
+ result = true;
+ }
+ tv_clear(&var2);
+ if (error) {
+ return FAIL;
+ }
}
- tv_clear(&var2);
- if (error) {
- return FAIL;
+ if (evaluate) {
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = result;
}
+
+ p = *arg;
}
- if (evaluate) {
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = result;
+
+ if (evalarg == NULL) {
+ clear_evalarg(&local_evalarg, NULL);
+ } else {
+ evalarg->eval_flags = orig_flags;
}
}
@@ -2427,21 +2574,29 @@ static int eval2(char **arg, typval_T *rettv, int evaluate)
/// `arg` is advanced to the next non-white after the recognized expression.
///
/// @return OK or FAIL.
-static int eval3(char **arg, typval_T *rettv, int evaluate)
+static int eval3(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{
- typval_T var2;
- bool error = false;
-
// Get the first variable.
- if (eval4(arg, rettv, evaluate) == FAIL) {
+ if (eval4(arg, rettv, evalarg) == FAIL) {
return FAIL;
}
- // Repeat until there is no following "&&".
- bool first = true;
- bool result = true;
- while ((*arg)[0] == '&' && (*arg)[1] == '&') {
- if (evaluate && first) {
+ char *p = *arg;
+ // Handle the "&&" operator.
+ if (p[0] == '&' && p[1] == '&') {
+ evalarg_T *evalarg_used = evalarg;
+ evalarg_T local_evalarg;
+ if (evalarg == NULL) {
+ local_evalarg = (evalarg_T){ .eval_flags = 0 };
+ evalarg_used = &local_evalarg;
+ }
+ const int orig_flags = evalarg_used->eval_flags;
+ const bool evaluate = evalarg_used->eval_flags & EVAL_EVALUATE;
+
+ bool result = true;
+
+ if (evaluate) {
+ bool error = false;
if (tv_get_number_chk(rettv, &error) == 0) {
result = false;
}
@@ -2449,28 +2604,41 @@ static int eval3(char **arg, typval_T *rettv, int evaluate)
if (error) {
return FAIL;
}
- first = false;
}
- // Get the second variable.
- *arg = skipwhite(*arg + 2);
- if (eval4(arg, &var2, evaluate && result) == FAIL) {
- return FAIL;
- }
+ // Repeat until there is no following "&&".
+ while (p[0] == '&' && p[1] == '&') {
+ // Get the second variable.
+ *arg = skipwhite(*arg + 2);
+ evalarg_used->eval_flags = result ? orig_flags : (orig_flags & ~EVAL_EVALUATE);
+ typval_T var2;
+ if (eval4(arg, &var2, evalarg_used) == FAIL) {
+ return FAIL;
+ }
- // Compute the result.
- if (evaluate && result) {
- if (tv_get_number_chk(&var2, &error) == 0) {
- result = false;
+ // Compute the result.
+ if (evaluate && result) {
+ bool error = false;
+ if (tv_get_number_chk(&var2, &error) == 0) {
+ result = false;
+ }
+ tv_clear(&var2);
+ if (error) {
+ return FAIL;
+ }
}
- tv_clear(&var2);
- if (error) {
- return FAIL;
+ if (evaluate) {
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = result;
}
+
+ p = *arg;
}
- if (evaluate) {
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = result;
+
+ if (evalarg == NULL) {
+ clear_evalarg(&local_evalarg, NULL);
+ } else {
+ evalarg->eval_flags = orig_flags;
}
}
@@ -2493,20 +2661,18 @@ static int eval3(char **arg, typval_T *rettv, int evaluate)
/// "arg" is advanced to the next non-white after the recognized expression.
///
/// @return OK or FAIL.
-static int eval4(char **arg, typval_T *rettv, int evaluate)
+static int eval4(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{
typval_T var2;
- char *p;
exprtype_T type = EXPR_UNKNOWN;
int len = 2;
- bool ic;
// Get the first variable.
- if (eval5(arg, rettv, evaluate) == FAIL) {
+ if (eval5(arg, rettv, evalarg) == FAIL) {
return FAIL;
}
- p = *arg;
+ char *p = *arg;
switch (p[0]) {
case '=':
if (p[1] == '=') {
@@ -2552,6 +2718,7 @@ static int eval4(char **arg, typval_T *rettv, int evaluate)
// If there is a comparative operator, use it.
if (type != EXPR_UNKNOWN) {
+ bool ic;
// extra question mark appended: ignore case
if (p[len] == '?') {
ic = true;
@@ -2565,11 +2732,11 @@ static int eval4(char **arg, typval_T *rettv, int evaluate)
// Get the second variable.
*arg = skipwhite(p + len);
- if (eval5(arg, &var2, evaluate) == FAIL) {
+ if (eval5(arg, &var2, evalarg) == FAIL) {
tv_clear(rettv);
return FAIL;
}
- if (evaluate) {
+ if (evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE)) {
const int ret = typval_compare(rettv, &var2, type, ic);
tv_clear(&var2);
@@ -2580,8 +2747,41 @@ static int eval4(char **arg, typval_T *rettv, int evaluate)
return OK;
}
+/// Make a copy of blob "tv1" and append blob "tv2".
+static void eval_addblob(typval_T *tv1, typval_T *tv2)
+{
+ const blob_T *const b1 = tv1->vval.v_blob;
+ const blob_T *const b2 = tv2->vval.v_blob;
+ blob_T *const b = tv_blob_alloc();
+
+ for (int i = 0; i < tv_blob_len(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, tv_blob_get(b2, i));
+ }
+
+ tv_clear(tv1);
+ tv_blob_set_ret(tv1, b);
+}
+
+/// Make a copy of list "tv1" and append list "tv2".
+static int eval_addlist(typval_T *tv1, typval_T *tv2)
+{
+ typval_T var3;
+ // Concatenate Lists.
+ if (tv_list_concat(tv1->vval.v_list, tv2->vval.v_list, &var3) == FAIL) {
+ tv_clear(tv1);
+ tv_clear(tv2);
+ return FAIL;
+ }
+ tv_clear(tv1);
+ *tv1 = var3;
+ return OK;
+}
+
/// Handle fourth level expression:
-/// + number addition
+/// + number addition, concatenation of list or blob
/// - number subtraction
/// . string concatenation
/// .. string concatenation
@@ -2590,29 +2790,24 @@ static int eval4(char **arg, typval_T *rettv, int evaluate)
/// `arg` is advanced to the next non-white after the recognized expression.
///
/// @return OK or FAIL.
-static int eval5(char **arg, typval_T *rettv, int evaluate)
+static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{
- typval_T var2;
- typval_T var3;
- int op;
- varnumber_T n1, n2;
- float_T f1 = 0, f2 = 0;
- char *p;
-
// Get the first variable.
- if (eval6(arg, rettv, evaluate, false) == FAIL) {
+ if (eval6(arg, rettv, evalarg, false) == FAIL) {
return FAIL;
}
// Repeat computing, until no '+', '-' or '.' is following.
- for (;;) {
- op = (char_u)(**arg);
- if (op != '+' && op != '-' && op != '.') {
+ while (true) {
+ int op = (uint8_t)(**arg);
+ bool concat = op == '.';
+ if (op != '+' && op != '-' && !concat) {
break;
}
+ const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE);
if ((op != '+' || (rettv->v_type != VAR_LIST && rettv->v_type != VAR_BLOB))
- && (op == '.' || rettv->v_type != VAR_FLOAT)) {
+ && (op == '.' || rettv->v_type != VAR_FLOAT) && evaluate) {
// For "list + ...", an illegal use of the first operand as
// a number cannot be determined before evaluating the 2nd
// operand: if this is also a list, all is ok.
@@ -2620,7 +2815,7 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
// we know that the first operand needs to be a string or number
// without evaluating the 2nd operand. So check before to avoid
// side effects after an error.
- if (evaluate && !tv_check_str(rettv)) {
+ if ((op == '.' && !tv_check_str(rettv)) || (op != '.' && !tv_check_num(rettv))) {
tv_clear(rettv);
return FAIL;
}
@@ -2631,7 +2826,8 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
(*arg)++;
}
*arg = skipwhite(*arg + 1);
- if (eval6(arg, &var2, evaluate, op == '.') == FAIL) {
+ typval_T var2;
+ if (eval6(arg, &var2, evalarg, op == '.') == FAIL) {
tv_clear(rettv);
return FAIL;
}
@@ -2649,38 +2845,20 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
tv_clear(&var2);
return FAIL;
}
- p = concat_str(s1, s2);
+ char *p = concat_str(s1, s2);
tv_clear(rettv);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = p;
- } else if (op == '+' && rettv->v_type == VAR_BLOB
- && var2.v_type == VAR_BLOB) {
- const blob_T *const b1 = rettv->vval.v_blob;
- const blob_T *const b2 = var2.vval.v_blob;
- blob_T *const b = tv_blob_alloc();
-
- for (int i = 0; i < tv_blob_len(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, tv_blob_get(b2, i));
- }
-
- tv_clear(rettv);
- tv_blob_set_ret(rettv, b);
- } else if (op == '+' && rettv->v_type == VAR_LIST
- && var2.v_type == VAR_LIST) {
- // Concatenate Lists.
- if (tv_list_concat(rettv->vval.v_list, var2.vval.v_list, &var3)
- == FAIL) {
- tv_clear(rettv);
- tv_clear(&var2);
+ } else if (op == '+' && rettv->v_type == VAR_BLOB && var2.v_type == VAR_BLOB) {
+ eval_addblob(rettv, &var2);
+ } else if (op == '+' && rettv->v_type == VAR_LIST && var2.v_type == VAR_LIST) {
+ if (eval_addlist(rettv, &var2) == FAIL) {
return FAIL;
}
- tv_clear(rettv);
- *rettv = var3;
} else {
bool error = false;
+ varnumber_T n1, n2;
+ float_T f1 = 0, f2 = 0;
if (rettv->v_type == VAR_FLOAT) {
f1 = rettv->vval.v_float;
@@ -2750,32 +2928,30 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
/// expression. Is advanced to the next non-whitespace
/// character after the recognized expression.
/// @param[out] rettv Location where result is saved.
-/// @param[in] evaluate If not true, rettv is not populated.
/// @param[in] want_string True if "." is string_concatenation, otherwise
/// float
/// @return OK or FAIL.
-static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string)
+static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool want_string)
FUNC_ATTR_NO_SANITIZE_UNDEFINED
{
- typval_T var2;
- int op;
- varnumber_T n1, n2;
bool use_float = false;
- float_T f1 = 0, f2 = 0;
- bool error = false;
// Get the first variable.
- if (eval7(arg, rettv, evaluate, want_string) == FAIL) {
+ if (eval7(arg, rettv, evalarg, want_string) == FAIL) {
return FAIL;
}
// Repeat computing, until no '*', '/' or '%' is following.
- for (;;) {
- op = (char_u)(**arg);
+ while (true) {
+ int op = (uint8_t)(**arg);
if (op != '*' && op != '/' && op != '%') {
break;
}
+ varnumber_T n1, n2;
+ float_T f1 = 0, f2 = 0;
+ bool error = false;
+ const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE);
if (evaluate) {
if (rettv->v_type == VAR_FLOAT) {
f1 = rettv->vval.v_float;
@@ -2794,7 +2970,8 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string)
// Get the second variable.
*arg = skipwhite(*arg + 1);
- if (eval7(arg, &var2, evaluate, false) == FAIL) {
+ typval_T var2;
+ if (eval7(arg, &var2, evalarg, false) == FAIL) {
return FAIL;
}
@@ -2885,8 +3062,9 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string)
/// @param want_string after "." operator
///
/// @return OK or FAIL.
-static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
+static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool want_string)
{
+ const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
int ret = OK;
static int recurse = 0;
@@ -2927,29 +3105,35 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
case '7':
case '8':
case '9':
- ret = get_number_tv(arg, rettv, evaluate, want_string);
+ ret = eval_number(arg, rettv, evaluate, want_string);
+
+ // Apply prefixed "-" and "+" now. Matters especially when
+ // "->" follows.
+ if (ret == OK && evaluate && end_leader > start_leader) {
+ ret = eval7_leader(rettv, true, start_leader, &end_leader);
+ }
break;
// String constant: "string".
case '"':
- ret = get_string_tv(arg, rettv, evaluate);
+ ret = eval_string(arg, rettv, evaluate, false);
break;
// Literal string constant: 'str''ing'.
case '\'':
- ret = get_lit_string_tv(arg, rettv, evaluate);
+ ret = eval_lit_string(arg, rettv, evaluate, false);
break;
// List: [expr, expr]
case '[':
- ret = get_list_tv(arg, rettv, evaluate);
+ ret = eval_list(arg, rettv, evalarg);
break;
// Dictionary: #{key: val, key: val}
case '#':
if ((*arg)[1] == '{') {
(*arg)++;
- ret = eval_dict(arg, rettv, evaluate, true);
+ ret = eval_dict(arg, rettv, evalarg, true);
} else {
ret = NOTDONE;
}
@@ -2958,19 +3142,24 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
// Lambda: {arg, arg -> expr}
// Dictionary: {'key': val, 'key': val}
case '{':
- ret = get_lambda_tv(arg, rettv, evaluate);
+ ret = get_lambda_tv(arg, rettv, evalarg);
if (ret == NOTDONE) {
- ret = eval_dict(arg, rettv, evaluate, false);
+ ret = eval_dict(arg, rettv, evalarg, false);
}
break;
// Option value: &name
case '&':
- ret = get_option_tv((const char **)arg, rettv, evaluate);
+ ret = eval_option((const char **)arg, rettv, evaluate);
break;
// Environment variable: $VAR.
+ // Interpolated string: $"string" or $'string'.
case '$':
- ret = get_env_tv(arg, rettv, evaluate);
+ if ((*arg)[1] == '"' || (*arg)[1] == '\'') {
+ ret = eval_interp_string(arg, rettv, evaluate);
+ } else {
+ ret = eval_env_var(arg, rettv, evaluate);
+ }
break;
// Register contents: @r.
@@ -2988,7 +3177,8 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
// nested expression: (expression).
case '(':
*arg = skipwhite(*arg + 1);
- ret = eval1(arg, rettv, evaluate); // recursive!
+
+ ret = eval1(arg, rettv, evalarg); // recursive!
if (**arg == ')') {
(*arg)++;
} else if (ret == OK) {
@@ -3016,12 +3206,17 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
if (len <= 0) {
ret = FAIL;
} else {
- if (**arg == '(') { // recursive!
- ret = eval_func(arg, s, len, rettv, evaluate, NULL);
+ const int flags = evalarg == NULL ? 0 : evalarg->eval_flags;
+ if (*skipwhite(*arg) == '(') {
+ // "name(..." recursive!
+ *arg = skipwhite(*arg);
+ ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL);
} else if (evaluate) {
- ret = get_var_tv((const char *)s, len, rettv, NULL, true, false);
+ // get value of variable
+ ret = eval_variable(s, len, rettv, NULL, true, false);
} else {
- check_vars((const char *)s, (size_t)len);
+ // skip the name
+ check_vars(s, (size_t)len);
ret = OK;
}
}
@@ -3033,13 +3228,12 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
// Handle following '[', '(' and '.' for expr[expr], expr.name,
// expr(expr), expr->name(expr)
if (ret == OK) {
- ret = handle_subscript((const char **)arg, rettv, evaluate, true,
- (char *)start_leader, &end_leader);
+ ret = handle_subscript((const char **)arg, rettv, evalarg, true);
}
// Apply logical NOT and unary '-', from right to left, ignore '+'.
if (ret == OK && evaluate && end_leader > start_leader) {
- ret = eval7_leader(rettv, (char *)start_leader, &end_leader);
+ ret = eval7_leader(rettv, false, start_leader, &end_leader);
}
recurse--;
@@ -3049,12 +3243,14 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
/// Apply the leading "!" and "-" before an eval7 expression to "rettv".
/// Adjusts "end_leaderp" until it is at "start_leader".
///
+/// @param numeric_only if true only handle "+" and "-".
+///
/// @return OK on success, FAIL on failure.
-static int eval7_leader(typval_T *const rettv, const char *const start_leader,
- const char **const end_leaderp)
+static int eval7_leader(typval_T *const rettv, const bool numeric_only,
+ const char *const start_leader, const char **const end_leaderp)
FUNC_ATTR_NONNULL_ALL
{
- const char *end_leader = (char *)(*end_leaderp);
+ const char *end_leader = *end_leaderp;
int ret = OK;
bool error = false;
varnumber_T val = 0;
@@ -3072,6 +3268,10 @@ static int eval7_leader(typval_T *const rettv, const char *const start_leader,
while (end_leader > start_leader) {
end_leader--;
if (*end_leader == '!') {
+ if (numeric_only) {
+ end_leader++;
+ break;
+ }
if (rettv->v_type == VAR_FLOAT) {
f = !(bool)f;
} else {
@@ -3104,15 +3304,16 @@ static int eval7_leader(typval_T *const rettv, const char *const start_leader,
/// to the name of the Lua function to call (after the
/// "v:lua." prefix).
/// @return OK on success, FAIL on failure.
-static int call_func_rettv(char **const arg, typval_T *const rettv, const bool evaluate,
- dict_T *const selfdict, typval_T *const basetv,
+static int call_func_rettv(char **const arg, evalarg_T *const evalarg, typval_T *const rettv,
+ const bool evaluate, dict_T *const selfdict, typval_T *const basetv,
const char *const lua_funcname)
- FUNC_ATTR_NONNULL_ARG(1, 2)
+ FUNC_ATTR_NONNULL_ARG(1, 3)
{
partial_T *pt = NULL;
typval_T functv;
const char *funcname;
bool is_lua = false;
+ int ret;
// need to copy the funcref so that we can clear rettv
if (evaluate) {
@@ -3126,6 +3327,11 @@ static int call_func_rettv(char **const arg, typval_T *const rettv, const bool e
funcname = is_lua ? lua_funcname : partial_name(pt);
} else {
funcname = functv.vval.v_string;
+ if (funcname == NULL || *funcname == NUL) {
+ emsg(_(e_empty_function_name));
+ ret = FAIL;
+ goto theend;
+ }
}
} else {
funcname = "";
@@ -3138,9 +3344,10 @@ 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(funcname, is_lua ? (int)(*arg - funcname) : -1, rettv,
- arg, &funcexe);
+ ret = get_func_tv(funcname, is_lua ? (int)(*arg - funcname) : -1, rettv,
+ arg, evalarg, &funcexe);
+theend:
// Clear the funcref afterwards, so that deleting it while
// evaluating the arguments is possible (see test55).
if (evaluate) {
@@ -3158,16 +3365,17 @@ static int call_func_rettv(char **const arg, typval_T *const rettv, const bool e
/// @return FAIL or OK.
///
/// @note "*arg" is advanced to after the ')'.
-static int eval_lambda(char **const arg, typval_T *const rettv, const bool evaluate,
+static int eval_lambda(char **const arg, typval_T *const rettv, evalarg_T *const evalarg,
const bool verbose)
- FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_ARG(1, 2)
{
+ const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
// Skip over the ->.
*arg += 2;
typval_T base = *rettv;
rettv->v_type = VAR_UNKNOWN;
- int ret = get_lambda_tv(arg, rettv, evaluate);
+ int ret = get_lambda_tv(arg, rettv, evalarg);
if (ret != OK) {
return FAIL;
} else if (**arg != '(') {
@@ -3181,7 +3389,7 @@ static int eval_lambda(char **const arg, typval_T *const rettv, const bool evalu
tv_clear(rettv);
ret = FAIL;
} else {
- ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, NULL);
+ ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base, NULL);
}
// Clear the funcref afterwards, so that deleting it while
@@ -3198,10 +3406,12 @@ static int eval_lambda(char **const arg, typval_T *const rettv, const bool evalu
/// @param *arg points to the '-'.
///
/// @return FAIL or OK. "*arg" is advanced to after the ')'.
-static int eval_method(char **const arg, typval_T *const rettv, const bool evaluate,
+static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const evalarg,
const bool verbose)
- FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_ARG(1, 2)
{
+ const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
+
// Skip over the ->.
*arg += 2;
typval_T base = *rettv;
@@ -3213,7 +3423,7 @@ static int eval_method(char **const arg, typval_T *const rettv, const bool evalu
char *lua_funcname = NULL;
if (strncmp(name, "v:lua.", 6) == 0) {
lua_funcname = name + 6;
- *arg = (char *)skip_luafunc_name((const char *)lua_funcname);
+ *arg = (char *)skip_luafunc_name(lua_funcname);
*arg = skipwhite(*arg); // to detect trailing whitespace later
len = (int)(*arg - lua_funcname);
} else {
@@ -3251,9 +3461,9 @@ static int eval_method(char **const arg, typval_T *const rettv, const bool evalu
rettv->vval.v_partial = vvlua_partial;
rettv->vval.v_partial->pt_refcount++;
}
- ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, lua_funcname);
+ ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base, lua_funcname);
} else {
- ret = eval_func(arg, name, len, rettv, evaluate, &base);
+ ret = eval_func(arg, evalarg, name, len, rettv, evaluate ? EVAL_EVALUATE : 0, &base);
}
}
@@ -3272,44 +3482,17 @@ static int eval_method(char **const arg, typval_T *const rettv, const bool evalu
/// @param verbose give error messages
///
/// @returns FAIL or OK. "*arg" is advanced to after the ']'.
-static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
+static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool verbose)
{
+ const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
bool empty1 = false;
bool empty2 = false;
- long n1, n2 = 0;
- ptrdiff_t len = -1;
- int range = false;
- char *key = NULL;
+ bool range = false;
+ const char *key = NULL;
+ ptrdiff_t keylen = -1;
- switch (rettv->v_type) {
- case VAR_FUNC:
- case VAR_PARTIAL:
- if (verbose) {
- emsg(_("E695: Cannot index a Funcref"));
- }
- return FAIL;
- case VAR_FLOAT:
- if (verbose) {
- emsg(_(e_float_as_string));
- }
- return FAIL;
- case VAR_BOOL:
- case VAR_SPECIAL:
- if (verbose) {
- emsg(_("E909: Cannot index a special variable"));
- }
+ if (check_can_index(rettv, evaluate, verbose) == FAIL) {
return FAIL;
- case VAR_UNKNOWN:
- if (evaluate) {
- return FAIL;
- }
- FALLTHROUGH;
- case VAR_STRING:
- case VAR_NUMBER:
- case VAR_LIST:
- case VAR_DICT:
- case VAR_BLOB:
- break;
}
typval_T var1 = TV_INITIAL_VALUE;
@@ -3317,11 +3500,11 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
if (**arg == '.') {
// dict.name
key = *arg + 1;
- for (len = 0; eval_isdictc(key[len]); len++) {}
- if (len == 0) {
+ for (keylen = 0; eval_isdictc(key[keylen]); keylen++) {}
+ if (keylen == 0) {
return FAIL;
}
- *arg = skipwhite(key + len);
+ *arg = skipwhite(key + keylen);
} else {
// something[idx]
//
@@ -3329,7 +3512,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
*arg = skipwhite(*arg + 1);
if (**arg == ':') {
empty1 = true;
- } else if (eval1(arg, &var1, evaluate) == FAIL) { // Recursive!
+ } else if (eval1(arg, &var1, evalarg) == FAIL) { // Recursive!
return FAIL;
} else if (evaluate && !tv_check_str(&var1)) {
// Not a number or string.
@@ -3343,7 +3526,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
*arg = skipwhite(*arg + 1);
if (**arg == ']') {
empty2 = true;
- } else if (eval1(arg, &var2, evaluate) == FAIL) { // Recursive!
+ } else if (eval1(arg, &var2, evalarg) == FAIL) { // Recursive!
if (!empty1) {
tv_clear(&var1);
}
@@ -3373,195 +3556,194 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
}
if (evaluate) {
- n1 = 0;
- if (!empty1 && rettv->v_type != VAR_DICT && !tv_is_luafunc(rettv)) {
- n1 = tv_get_number(&var1);
+ int res = eval_index_inner(rettv, range,
+ empty1 ? NULL : &var1, empty2 ? NULL : &var2, false,
+ key, keylen, verbose);
+ if (!empty1) {
tv_clear(&var1);
}
if (range) {
- if (empty2) {
- n2 = -1;
- } else {
- n2 = tv_get_number(&var2);
- tv_clear(&var2);
- }
+ tv_clear(&var2);
}
+ return res;
+ }
+ return OK;
+}
- switch (rettv->v_type) {
- case VAR_NUMBER:
- case VAR_STRING: {
- const char *const s = tv_get_string(rettv);
- char *v;
- len = (ptrdiff_t)strlen(s);
- if (range) {
- // The resulting variable is a substring. If the indexes
- // are out of range the result is empty.
- if (n1 < 0) {
- n1 = len + n1;
- if (n1 < 0) {
- n1 = 0;
- }
- }
- if (n2 < 0) {
- n2 = len + n2;
- } else if (n2 >= len) {
- n2 = len;
- }
- if (n1 >= len || n2 < 0 || n1 > n2) {
- v = NULL;
- } else {
- v = xmemdupz(s + n1, (size_t)(n2 - n1 + 1));
- }
- } else {
- // The resulting variable is a string of a single
- // character. If the index is too big or negative the
- // result is empty.
- if (n1 >= len || n1 < 0) {
- v = NULL;
- } else {
- v = xmemdupz(s + n1, 1);
- }
+/// Check if "rettv" can have an [index] or [sli:ce]
+static int check_can_index(typval_T *rettv, bool evaluate, bool verbose)
+{
+ switch (rettv->v_type) {
+ case VAR_FUNC:
+ case VAR_PARTIAL:
+ if (verbose) {
+ emsg(_(e_cannot_index_a_funcref));
+ }
+ return FAIL;
+ case VAR_FLOAT:
+ if (verbose) {
+ emsg(_(e_using_float_as_string));
+ }
+ return FAIL;
+ case VAR_BOOL:
+ case VAR_SPECIAL:
+ if (verbose) {
+ emsg(_(e_cannot_index_special_variable));
+ }
+ return FAIL;
+ case VAR_UNKNOWN:
+ if (evaluate) {
+ emsg(_(e_cannot_index_special_variable));
+ return FAIL;
+ }
+ FALLTHROUGH;
+ case VAR_STRING:
+ case VAR_NUMBER:
+ case VAR_LIST:
+ case VAR_DICT:
+ case VAR_BLOB:
+ break;
+ }
+ return OK;
+}
+
+/// slice() function
+void f_slice(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ if (check_can_index(argvars, true, false) == OK) {
+ tv_copy(argvars, rettv);
+ eval_index_inner(rettv, true, argvars + 1,
+ argvars[2].v_type == VAR_UNKNOWN ? NULL : argvars + 2,
+ true, NULL, 0, false);
+ }
+}
+
+/// Apply index or range to "rettv".
+///
+/// @param var1 the first index, NULL for [:expr].
+/// @param var2 the second index, NULL for [expr] and [expr: ]
+/// @param exclusive true for slice(): second index is exclusive, use character
+/// index for string.
+/// Alternatively, "key" is not NULL, then key[keylen] is the dict index.
+static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typval_T *var2,
+ bool exclusive, const char *key, ptrdiff_t keylen, bool verbose)
+{
+ varnumber_T n1 = 0;
+ varnumber_T n2 = 0;
+ if (var1 != NULL && rettv->v_type != VAR_DICT) {
+ n1 = tv_get_number(var1);
+ }
+
+ if (is_range) {
+ if (rettv->v_type == VAR_DICT) {
+ if (verbose) {
+ emsg(_(e_cannot_slice_dictionary));
}
- tv_clear(rettv);
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = v;
- break;
+ return FAIL;
}
- case VAR_BLOB:
- len = tv_blob_len(rettv->vval.v_blob);
- if (range) {
- // The resulting variable is a sub-blob. If the indexes
- // are out of range the result is empty.
- if (n1 < 0) {
- n1 = len + n1;
- if (n1 < 0) {
- n1 = 0;
- }
- }
- if (n2 < 0) {
- n2 = len + n2;
- } else if (n2 >= len) {
- n2 = len - 1;
- }
- if (n1 >= len || n2 < 0 || n1 > n2) {
- tv_clear(rettv);
- rettv->v_type = VAR_BLOB;
- rettv->vval.v_blob = NULL;
- } else {
- blob_T *const blob = tv_blob_alloc();
- ga_grow(&blob->bv_ga, (int)(n2 - n1 + 1));
- blob->bv_ga.ga_len = (int)(n2 - n1 + 1);
- for (long i = n1; i <= n2; i++) {
- tv_blob_set(blob, (int)(i - n1), tv_blob_get(rettv->vval.v_blob, (int)i));
- }
- tv_clear(rettv);
- tv_blob_set_ret(rettv, blob);
- }
+ if (var2 != NULL) {
+ n2 = tv_get_number(var2);
+ } else {
+ n2 = VARNUMBER_MAX;
+ }
+ }
+
+ switch (rettv->v_type) {
+ case VAR_BOOL:
+ case VAR_SPECIAL:
+ case VAR_FUNC:
+ case VAR_FLOAT:
+ case VAR_PARTIAL:
+ case VAR_UNKNOWN:
+ break; // Not evaluating, skipping over subscript
+
+ case VAR_NUMBER:
+ case VAR_STRING: {
+ const char *const s = tv_get_string(rettv);
+ char *v;
+ int len = (int)strlen(s);
+ if (exclusive) {
+ if (is_range) {
+ v = string_slice(s, n1, n2, exclusive);
} else {
- // The resulting variable is a byte value.
- // If the index is too big or negative that is an error.
- if (n1 < 0) {
- n1 = len + n1;
- }
- if (n1 < len && n1 >= 0) {
- const int v = (int)tv_blob_get(rettv->vval.v_blob, (int)n1);
- tv_clear(rettv);
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = v;
- } else {
- semsg(_(e_blobidx), (int64_t)n1);
- }
+ v = char_from_string(s, n1);
}
- break;
- case VAR_LIST:
- len = tv_list_len(rettv->vval.v_list);
+ } else if (is_range) {
+ // The resulting variable is a substring. If the indexes
+ // are out of range the result is empty.
if (n1 < 0) {
n1 = len + n1;
- }
- if (!empty1 && (n1 < 0 || n1 >= len)) {
- // For a range we allow invalid values and return an empty
- // list. A list index out of range is an error.
- if (!range) {
- if (verbose) {
- semsg(_(e_listidx), (int64_t)n1);
- }
- return FAIL;
+ if (n1 < 0) {
+ n1 = 0;
}
- n1 = len;
}
- if (range) {
- list_T *l;
- listitem_T *item;
-
- if (n2 < 0) {
- n2 = len + n2;
- } else if (n2 >= len) {
- n2 = len - 1;
- }
- if (!empty2 && (n2 < 0 || n2 + 1 < n1)) {
- n2 = -1;
- }
- l = tv_list_alloc(n2 - n1 + 1);
- item = tv_list_find(rettv->vval.v_list, (int)n1);
- while (n1++ <= n2) {
- tv_list_append_tv(l, TV_LIST_ITEM_TV(item));
- item = TV_LIST_ITEM_NEXT(rettv->vval.v_list, item);
- }
- tv_clear(rettv);
- tv_list_set_ret(rettv, l);
+ if (n2 < 0) {
+ n2 = len + n2;
+ } else if (n2 >= len) {
+ n2 = len;
+ }
+ if (n1 >= len || n2 < 0 || n1 > n2) {
+ v = NULL;
} else {
- tv_copy(TV_LIST_ITEM_TV(tv_list_find(rettv->vval.v_list, (int)n1)), &var1);
- tv_clear(rettv);
- *rettv = var1;
+ v = xmemdupz(s + n1, (size_t)n2 - (size_t)n1 + 1);
}
- break;
- case VAR_DICT: {
- if (range) {
- if (verbose) {
- emsg(_(e_dictrange));
- }
- if (len == -1) {
- tv_clear(&var1);
- }
- return FAIL;
+ } else {
+ // The resulting variable is a string of a single
+ // character. If the index is too big or negative the
+ // result is empty.
+ if (n1 >= len || n1 < 0) {
+ v = NULL;
+ } else {
+ v = xmemdupz(s + n1, 1);
}
+ }
+ tv_clear(rettv);
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = v;
+ break;
+ }
- if (len == -1) {
- key = (char *)tv_get_string_chk(&var1);
- if (key == NULL) {
- tv_clear(&var1);
- return FAIL;
- }
- }
+ case VAR_BLOB:
+ tv_blob_slice_or_index(rettv->vval.v_blob, is_range, n1, n2, exclusive, rettv);
+ break;
- dictitem_T *const item = tv_dict_find(rettv->vval.v_dict,
- (const char *)key, len);
+ case VAR_LIST:
+ if (var1 == NULL) {
+ n1 = 0;
+ }
+ if (var2 == NULL) {
+ n2 = VARNUMBER_MAX;
+ }
+ if (tv_list_slice_or_index(rettv->vval.v_list,
+ is_range, n1, n2, exclusive, rettv, verbose) == FAIL) {
+ return FAIL;
+ }
+ break;
- if (item == NULL && verbose) {
- semsg(_(e_dictkey), key);
- }
- if (len == -1) {
- tv_clear(&var1);
- }
- if (item == NULL || tv_is_luafunc(&item->di_tv)) {
+ case VAR_DICT: {
+ if (key == NULL) {
+ key = tv_get_string_chk(var1);
+ if (key == NULL) {
return FAIL;
}
+ }
- tv_copy(&item->di_tv, &var1);
- tv_clear(rettv);
- *rettv = var1;
- break;
+ dictitem_T *const item = tv_dict_find(rettv->vval.v_dict, key, keylen);
+
+ if (item == NULL && verbose) {
+ semsg(_(e_dictkey), key);
}
- case VAR_BOOL:
- case VAR_SPECIAL:
- case VAR_FUNC:
- case VAR_FLOAT:
- case VAR_PARTIAL:
- case VAR_UNKNOWN:
- break; // Not evaluating, skipping over subscript
+ if (item == NULL || tv_is_luafunc(&item->di_tv)) {
+ return FAIL;
}
- }
+ typval_T tmp;
+ tv_copy(&item->di_tv, &tmp);
+ tv_clear(rettv);
+ *rettv = tmp;
+ break;
+ }
+ }
return OK;
}
@@ -3573,7 +3755,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
/// @param[in] evaluate If not true, rettv is not populated.
///
/// @return OK or FAIL.
-int get_option_tv(const char **const arg, typval_T *const rettv, const bool evaluate)
+int eval_option(const char **const arg, typval_T *const rettv, const bool evaluate)
FUNC_ATTR_NONNULL_ARG(1)
{
const bool working = (**arg == '+'); // has("+option")
@@ -3593,38 +3775,38 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval
return OK;
}
- long numval;
- char *stringval;
int ret = OK;
-
+ bool hidden;
char c = *option_end;
*option_end = NUL;
- getoption_T opt_type = get_option_value(*arg, &numval,
- rettv == NULL ? NULL : &stringval, NULL, scope);
+ OptVal value = get_option_value(*arg, NULL, scope, &hidden);
- if (opt_type == gov_unknown) {
- if (rettv != NULL) {
+ if (rettv != NULL) {
+ switch (value.type) {
+ case kOptValTypeNil:
semsg(_("E113: Unknown option: %s"), *arg);
- }
- ret = FAIL;
- } else if (rettv != NULL) {
- if (opt_type == gov_hidden_string) {
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
- } else if (opt_type == gov_hidden_bool || opt_type == gov_hidden_number) {
+ ret = FAIL;
+ break;
+ case kOptValTypeBoolean:
rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = 0;
- } else if (opt_type == gov_bool || opt_type == gov_number) {
+ rettv->vval.v_number = value.data.boolean;
+ break;
+ case kOptValTypeNumber:
rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = numval;
- } else { // string option
+ rettv->vval.v_number = value.data.number;
+ break;
+ case kOptValTypeString:
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = stringval;
+ rettv->vval.v_string = value.data.string.data;
+ break;
+ }
+ } else {
+ // Value isn't being used, free it.
+ optval_free(value);
+
+ if (value.type == kOptValTypeNil || (working && hidden)) {
+ ret = FAIL;
}
- } 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
@@ -3636,7 +3818,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval
/// 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)
+static int eval_number(char **arg, typval_T *rettv, bool evaluate, bool want_string)
{
char *p = skipdigits(*arg + 1);
bool get_float = false;
@@ -3702,7 +3884,7 @@ static int get_number_tv(char **arg, typval_T *rettv, bool evaluate, bool want_s
// decimal, hex or octal number
int len;
varnumber_T n;
- vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, true);
+ vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, true, NULL);
if (len == 0) {
if (evaluate) {
semsg(_(e_invexpr2), *arg);
@@ -3718,60 +3900,88 @@ static int get_number_tv(char **arg, typval_T *rettv, bool evaluate, bool want_s
return OK;
}
-/// Allocate a variable for a string constant.
+/// Evaluate a string constant and put the result in "rettv".
+/// "*arg" points to the double quote or to after it when "interpolate" is true.
+/// When "interpolate" is true reduce "{{" to "{", reduce "}}" to "}" and stop
+/// at a single "{".
///
/// @return OK or FAIL.
-static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
+static int eval_string(char **arg, typval_T *rettv, bool evaluate, bool interpolate)
{
char *p;
- unsigned int extra = 0;
+ const char *const arg_end = *arg + strlen(*arg);
+ unsigned extra = interpolate ? 1 : 0;
+ const int off = interpolate ? 0 : 1;
// Find the end of the string, skipping backslashed characters.
- for (p = *arg + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p)) {
+ for (p = *arg + off; *p != NUL && *p != '"'; MB_PTR_ADV(p)) {
if (*p == '\\' && p[1] != NUL) {
p++;
// A "\<x>" form occupies at least 4 characters, and produces up
// to 9 characters (6 for the char and 3 for a modifier):
// reserve space for 5 extra.
if (*p == '<') {
+ int modifiers = 0;
+ int flags = FSK_KEYCODE | FSK_IN_STRING;
+
extra += 5;
+
+ // Skip to the '>' to avoid using '{' inside for string
+ // interpolation.
+ if (p[1] != '*') {
+ flags |= FSK_SIMPLIFY;
+ }
+ if (find_special_key((const char **)&p, (size_t)(arg_end - p),
+ &modifiers, flags, NULL) != 0) {
+ p--; // leave "p" on the ">"
+ }
+ }
+ } else if (interpolate && (*p == '{' || *p == '}')) {
+ if (*p == '{' && p[1] != '{') { // start of expression
+ break;
}
+ p++;
+ if (p[-1] == '}' && *p != '}') { // single '}' is an error
+ semsg(_(e_stray_closing_curly_str), *arg);
+ return FAIL;
+ }
+ extra--; // "{{" becomes "{", "}}" becomes "}"
}
}
- if (*p != '"') {
+ if (*p != '"' && !(interpolate && *p == '{')) {
semsg(_("E114: Missing quote: %s"), *arg);
return FAIL;
}
// If only parsing, set *arg and return here
if (!evaluate) {
- *arg = p + 1;
+ *arg = p + off;
return OK;
}
// Copy the string into allocated memory, handling backslashed
// characters.
- const int len = (int)(p - *arg + extra);
- char *name = xmalloc((size_t)len);
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = name;
+ const int len = (int)(p - *arg + extra);
+ rettv->vval.v_string = xmalloc((size_t)len);
+ char *end = rettv->vval.v_string;
- for (p = *arg + 1; *p != NUL && *p != '"';) {
+ for (p = *arg + off; *p != NUL && *p != '"';) {
if (*p == '\\') {
switch (*++p) {
case 'b':
- *name++ = BS; ++p; break;
+ *end++ = BS; ++p; break;
case 'e':
- *name++ = ESC; ++p; break;
+ *end++ = ESC; ++p; break;
case 'f':
- *name++ = FF; ++p; break;
+ *end++ = FF; ++p; break;
case 'n':
- *name++ = NL; ++p; break;
+ *end++ = NL; ++p; break;
case 'r':
- *name++ = CAR; ++p; break;
+ *end++ = CAR; ++p; break;
case 't':
- *name++ = TAB; ++p; break;
+ *end++ = TAB; ++p; break;
case 'X': // hex: "\x1", "\x12"
case 'x':
@@ -3797,9 +4007,9 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
// For "\u" store the number according to
// 'encoding'.
if (c != 'X') {
- name += utf_char2bytes(nr, name);
+ end += utf_char2bytes(nr, end);
} else {
- *name++ = (char)nr;
+ *end++ = (char)nr;
}
}
break;
@@ -3813,14 +4023,14 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
case '5':
case '6':
case '7':
- *name = (char)(*p++ - '0');
+ *end = (char)(*p++ - '0');
if (*p >= '0' && *p <= '7') {
- *name = (char)((*name << 3) + *p++ - '0');
+ *end = (char)((*end << 3) + *p++ - '0');
if (*p >= '0' && *p <= '7') {
- *name = (char)((*name << 3) + *p++ - '0');
+ *end = (char)((*end << 3) + *p++ - '0');
}
}
- name++;
+ end++;
break;
// Special key, e.g.: "\<C-W>"
@@ -3830,11 +4040,12 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
if (p[1] != '*') {
flags |= FSK_SIMPLIFY;
}
- extra = trans_special((const char **)&p, strlen(p), name, flags, false, NULL);
+ extra = trans_special((const char **)&p, (size_t)(arg_end - p),
+ end, flags, false, NULL);
if (extra != 0) {
- name += extra;
- if (name >= rettv->vval.v_string + len) {
- iemsg("get_string_tv() used more space than allocated");
+ end += extra;
+ if (end >= rettv->vval.v_string + len) {
+ iemsg("eval_string() used more space than allocated");
}
break;
}
@@ -3842,15 +4053,21 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
FALLTHROUGH;
default:
- mb_copy_char((const char **)&p, &name);
+ mb_copy_char((const char **)&p, &end);
break;
}
} else {
- mb_copy_char((const char **)&p, &name);
+ if (interpolate && (*p == '{' || *p == '}')) {
+ if (*p == '{' && p[1] != '{') { // start of expression
+ break;
+ }
+ p++; // reduce "{{" to "{" and "}}" to "}"
+ }
+ mb_copy_char((const char **)&p, &end);
}
}
- *name = NUL;
- if (*p != NUL) { // just in case
+ *end = NUL;
+ if (*p == '"' && !interpolate) {
p++;
}
*arg = p;
@@ -3859,63 +4076,146 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
}
/// Allocate a variable for a 'str''ing' constant.
+/// When "interpolate" is true reduce "{{" to "{" and stop at a single "{".
///
-/// @return OK or FAIL.
-static int get_lit_string_tv(char **arg, typval_T *rettv, int evaluate)
+/// @return OK when a "rettv" was set to the string.
+/// FAIL on error, "rettv" is not set.
+static int eval_lit_string(char **arg, typval_T *rettv, bool evaluate, bool interpolate)
{
char *p;
- int reduce = 0;
+ int reduce = interpolate ? -1 : 0;
+ const int off = interpolate ? 0 : 1;
// Find the end of the string, skipping ''.
- for (p = *arg + 1; *p != NUL; MB_PTR_ADV(p)) {
+ for (p = *arg + off; *p != NUL; MB_PTR_ADV(p)) {
if (*p == '\'') {
if (p[1] != '\'') {
break;
}
reduce++;
p++;
+ } else if (interpolate) {
+ if (*p == '{') {
+ if (p[1] != '{') {
+ break;
+ }
+ p++;
+ reduce++;
+ } else if (*p == '}') {
+ p++;
+ if (*p != '}') {
+ semsg(_(e_stray_closing_curly_str), *arg);
+ return FAIL;
+ }
+ reduce++;
+ }
}
}
- if (*p != '\'') {
+ if (*p != '\'' && !(interpolate && *p == '{')) {
semsg(_("E115: Missing quote: %s"), *arg);
return FAIL;
}
// If only parsing return after setting "*arg"
if (!evaluate) {
- *arg = p + 1;
+ *arg = p + off;
return OK;
}
- // Copy the string into allocated memory, handling '' to ' reduction.
+ // Copy the string into allocated memory, handling '' to ' reduction and
+ // any expressions.
char *str = xmalloc((size_t)((p - *arg) - reduce));
rettv->v_type = VAR_STRING;
rettv->vval.v_string = str;
- for (p = *arg + 1; *p != NUL;) {
+ for (p = *arg + off; *p != NUL;) {
if (*p == '\'') {
if (p[1] != '\'') {
break;
}
p++;
+ } else if (interpolate && (*p == '{' || *p == '}')) {
+ if (*p == '{' && p[1] != '{') {
+ break;
+ }
+ p++;
}
mb_copy_char((const char **)&p, &str);
}
*str = NUL;
- *arg = p + 1;
+ *arg = p + off;
return OK;
}
+/// Evaluate a single or double quoted string possibly containing expressions.
+/// "arg" points to the '$'. The result is put in "rettv".
+///
+/// @return OK or FAIL.
+int eval_interp_string(char **arg, typval_T *rettv, bool evaluate)
+{
+ int ret = OK;
+
+ garray_T ga;
+ ga_init(&ga, 1, 80);
+
+ // *arg is on the '$' character, move it to the first string character.
+ (*arg)++;
+ const int quote = (uint8_t)(**arg);
+ (*arg)++;
+
+ while (true) {
+ typval_T tv;
+ // Get the string up to the matching quote or to a single '{'.
+ // "arg" is advanced to either the quote or the '{'.
+ if (quote == '"') {
+ ret = eval_string(arg, &tv, evaluate, true);
+ } else {
+ ret = eval_lit_string(arg, &tv, evaluate, true);
+ }
+ if (ret == FAIL) {
+ break;
+ }
+ if (evaluate) {
+ ga_concat(&ga, tv.vval.v_string);
+ tv_clear(&tv);
+ }
+
+ if (**arg != '{') {
+ // found terminating quote
+ (*arg)++;
+ break;
+ }
+ char *p = eval_one_expr_in_str(*arg, &ga, evaluate);
+ if (p == NULL) {
+ ret = FAIL;
+ break;
+ }
+ *arg = p;
+ }
+
+ rettv->v_type = VAR_STRING;
+ if (ret != FAIL && evaluate) {
+ ga_append(&ga, NUL);
+ }
+ rettv->vval.v_string = ga.ga_data;
+ return OK;
+}
+
/// @return the function name of the partial.
char *partial_name(partial_T *pt)
FUNC_ATTR_PURE
{
- if (pt->pt_name != NULL) {
- return pt->pt_name;
+ if (pt != NULL) {
+ if (pt->pt_name != NULL) {
+ return pt->pt_name;
+ }
+ if (pt->pt_func != NULL) {
+ return pt->pt_func->uf_name;
+ }
}
- return (char *)pt->pt_func->uf_name;
+ return "";
}
static void partial_free(partial_T *pt)
@@ -3945,9 +4245,11 @@ void partial_unref(partial_T *pt)
/// Allocate a variable for a List and fill it from "*arg".
///
+/// @param arg "*arg" points to the "[".
/// @return OK or FAIL.
-static int get_list_tv(char **arg, typval_T *rettv, int evaluate)
+static int eval_list(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{
+ const bool evaluate = evalarg == NULL ? false : evalarg->eval_flags & EVAL_EVALUATE;
list_T *l = NULL;
if (evaluate) {
@@ -3957,7 +4259,7 @@ static int get_list_tv(char **arg, typval_T *rettv, int evaluate)
*arg = skipwhite(*arg + 1);
while (**arg != ']' && **arg != NUL) {
typval_T tv;
- if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive!
+ if (eval1(arg, &tv, evalarg) == FAIL) { // Recursive!
goto failret;
}
if (evaluate) {
@@ -3965,14 +4267,20 @@ static int get_list_tv(char **arg, typval_T *rettv, int evaluate)
tv_list_append_owned_tv(l, tv);
}
+ // the comma must come after the value
+ bool had_comma = **arg == ',';
+ if (had_comma) {
+ *arg = skipwhite(*arg + 1);
+ }
+
if (**arg == ']') {
break;
}
- if (**arg != ',') {
+
+ if (!had_comma) {
semsg(_("E696: Missing comma in List: %s"), *arg);
goto failret;
}
- *arg = skipwhite(*arg + 1);
}
if (**arg != ']') {
@@ -4119,7 +4427,7 @@ bool garbage_collect(bool testing)
ABORTING(set_ref_in_previous_funccal)(copyID);
// script-local variables
- for (int i = 1; i <= ga_scripts.ga_len; i++) {
+ for (int i = 1; i <= script_items.ga_len; i++) {
ABORTING(set_ref_in_ht)(&SCRIPT_VARS(i), copyID, NULL);
}
@@ -4368,7 +4676,7 @@ bool set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack)
ht_stack_T *ht_stack = NULL;
hashtab_T *cur_ht = ht;
- for (;;) {
+ while (true) {
if (!abort) {
// Mark each item in the hashtab. If the item contains a hashtab
// it is added to ht_stack, if it contains a list it is added to
@@ -4406,7 +4714,7 @@ bool set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack)
list_stack_T *list_stack = NULL;
list_T *cur_l = l;
- for (;;) {
+ while (true) {
// Mark each item in the list. If the item contains a hashtab
// it is added to ht_stack, if it contains a list it is added to
// list_stack.
@@ -4582,18 +4890,21 @@ static int get_literal_key(char **arg, typval_T *tv)
}
for (p = *arg; ASCII_ISALNUM(*p) || *p == '_' || *p == '-'; p++) {}
tv->v_type = VAR_STRING;
- tv->vval.v_string = xstrnsave(*arg, (size_t)(p - *arg));
+ tv->vval.v_string = xmemdupz(*arg, (size_t)(p - *arg));
*arg = skipwhite(p);
return OK;
}
/// Allocate a variable for a Dictionary and fill it from "*arg".
-/// "literal" is true for #{key: val}
+///
+/// @param arg "*arg" points to the "{".
+/// @param literal true for #{key: val}
///
/// @return OK or FAIL. Returns NOTDONE for {expr}.
-static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal)
+static int eval_dict(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool literal)
{
+ const bool evaluate = evalarg == NULL ? false : evalarg->eval_flags & EVAL_EVALUATE;
typval_T tv;
char *key = NULL;
char *curly_expr = skipwhite(*arg + 1);
@@ -4607,7 +4918,7 @@ static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal)
// "#{abc}" is never a curly-braces expression.
if (*curly_expr != '}'
&& !literal
- && eval1(&curly_expr, &tv, false) == OK
+ && eval1(&curly_expr, &tv, NULL) == OK
&& *skipwhite(curly_expr) == '}') {
return NOTDONE;
}
@@ -4624,7 +4935,7 @@ static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal)
while (**arg != '}' && **arg != NUL) {
if ((literal
? get_literal_key(arg, &tvkey)
- : eval1(arg, &tvkey, evaluate)) == FAIL) { // recursive!
+ : eval1(arg, &tvkey, evalarg)) == FAIL) { // recursive!
goto failret;
}
if (**arg != ':') {
@@ -4642,21 +4953,21 @@ static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal)
}
*arg = skipwhite(*arg + 1);
- if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive!
+ if (eval1(arg, &tv, evalarg) == FAIL) { // Recursive!
if (evaluate) {
tv_clear(&tvkey);
}
goto failret;
}
if (evaluate) {
- dictitem_T *item = tv_dict_find(d, (const char *)key, -1);
+ dictitem_T *item = tv_dict_find(d, key, -1);
if (item != NULL) {
semsg(_("E721: Duplicate key in Dictionary: \"%s\""), key);
tv_clear(&tvkey);
tv_clear(&tv);
goto failret;
}
- item = tv_dict_item_alloc((const char *)key);
+ item = tv_dict_item_alloc(key);
item->di_tv = tv;
item->di_tv.v_lock = VAR_UNLOCKED;
if (tv_dict_add(d, item) == FAIL) {
@@ -4665,14 +4976,19 @@ static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal)
}
tv_clear(&tvkey);
+ // the comma must come after the value
+ bool had_comma = **arg == ',';
+ if (had_comma) {
+ *arg = skipwhite(*arg + 1);
+ }
+
if (**arg == '}') {
break;
}
- if (**arg != ',') {
+ if (!had_comma) {
semsg(_("E722: Missing comma in Dictionary: %s"), *arg);
goto failret;
}
- *arg = skipwhite(*arg + 1);
}
if (**arg != '}') {
@@ -4729,7 +5045,7 @@ size_t string2float(const char *const text, float_T *const ret_value)
/// @param arg Points to the '$'. It is advanced to after the name.
///
/// @return FAIL if the name is invalid.
-static int get_env_tv(char **arg, typval_T *rettv, int evaluate)
+static int eval_env_var(char **arg, typval_T *rettv, int evaluate)
{
(*arg)++;
char *name = *arg;
@@ -4755,6 +5071,7 @@ static int get_env_tv(char **arg, typval_T *rettv, int evaluate)
name[len] = (char)cc;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = string;
+ rettv->v_lock = VAR_UNLOCKED;
}
return OK;
@@ -4769,44 +5086,280 @@ void assert_error(garray_T *gap)
// Make sure v:errors is a list.
set_vim_var_list(VV_ERRORS, tv_list_alloc(1));
}
- tv_list_append_string(vimvars[VV_ERRORS].vv_list,
- (const char *)gap->ga_data, (ptrdiff_t)gap->ga_len);
+ tv_list_append_string(vimvars[VV_ERRORS].vv_list, gap->ga_data, (ptrdiff_t)gap->ga_len);
}
-/// Implementation of map() and filter().
-void filter_map(typval_T *argvars, typval_T *rettv, int map)
+/// Implementation of map() and filter() for a Dict.
+static void filter_map_dict(dict_T *d, filtermap_T filtermap, const char *func_name,
+ const char *arg_errmsg, typval_T *expr, typval_T *rettv)
{
- list_T *l = NULL;
- dict_T *d = NULL;
- blob_T *b = NULL;
- int rem = false;
- char *ermsg = map ? "map()" : "filter()";
- const char *const arg_errmsg = (map
- ? N_("map() argument")
- : N_("filter() argument"));
- int save_did_emsg;
+ if (filtermap == FILTERMAP_MAPNEW) {
+ rettv->v_type = VAR_DICT;
+ rettv->vval.v_dict = NULL;
+ }
+ if (d == NULL
+ || (filtermap == FILTERMAP_FILTER
+ && value_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) {
+ return;
+ }
+
+ dict_T *d_ret = NULL;
+
+ if (filtermap == FILTERMAP_MAPNEW) {
+ tv_dict_alloc_ret(rettv);
+ d_ret = rettv->vval.v_dict;
+ }
+
+ vimvars[VV_KEY].vv_type = VAR_STRING;
+
+ const VarLockStatus prev_lock = d->dv_lock;
+ if (d->dv_lock == VAR_UNLOCKED) {
+ d->dv_lock = VAR_LOCKED;
+ }
+ hash_lock(&d->dv_hashtab);
+ TV_DICT_ITER(d, di, {
+ if (filtermap == FILTERMAP_MAP
+ && (value_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE)
+ || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) {
+ break;
+ }
+ vimvars[VV_KEY].vv_str = xstrdup(di->di_key);
+ typval_T newtv;
+ bool rem;
+ int r = filter_map_one(&di->di_tv, expr, filtermap, &newtv, &rem);
+ tv_clear(&vimvars[VV_KEY].vv_tv);
+ if (r == FAIL || did_emsg) {
+ tv_clear(&newtv);
+ break;
+ }
+ if (filtermap == FILTERMAP_MAP) {
+ // map(): replace the dict item value
+ tv_clear(&di->di_tv);
+ newtv.v_lock = VAR_UNLOCKED;
+ di->di_tv = newtv;
+ } else if (filtermap == FILTERMAP_MAPNEW) {
+ // mapnew(): add the item value to the new dict
+ r = tv_dict_add_tv(d_ret, di->di_key, strlen(di->di_key), &newtv);
+ tv_clear(&newtv);
+ if (r == FAIL) {
+ break;
+ }
+ } else if (filtermap == FILTERMAP_FILTER && rem) {
+ // filter(false): remove the item from the dict
+ if (var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE)
+ || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) {
+ break;
+ }
+ tv_dict_item_remove(d, di);
+ }
+ });
+ hash_unlock(&d->dv_hashtab);
+ d->dv_lock = prev_lock;
+}
+
+/// Implementation of map() and filter() for a Blob.
+static void filter_map_blob(blob_T *blob_arg, filtermap_T filtermap, typval_T *expr,
+ const char *arg_errmsg, typval_T *rettv)
+{
+ if (filtermap == FILTERMAP_MAPNEW) {
+ rettv->v_type = VAR_BLOB;
+ rettv->vval.v_blob = NULL;
+ }
+ blob_T *b = blob_arg;
+ if (b == NULL
+ || (filtermap == FILTERMAP_FILTER
+ && value_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE))) {
+ return;
+ }
+
+ blob_T *b_ret = b;
+
+ if (filtermap == FILTERMAP_MAPNEW) {
+ tv_blob_copy(b, rettv);
+ b_ret = rettv->vval.v_blob;
+ }
+
+ vimvars[VV_KEY].vv_type = VAR_NUMBER;
+
+ const VarLockStatus prev_lock = b->bv_lock;
+ if (b->bv_lock == 0) {
+ b->bv_lock = VAR_LOCKED;
+ }
+
+ for (int i = 0, idx = 0; i < b->bv_ga.ga_len; i++) {
+ const varnumber_T val = tv_blob_get(b, i);
+ typval_T tv = {
+ .v_type = VAR_NUMBER,
+ .v_lock = VAR_UNLOCKED,
+ .vval.v_number = val,
+ };
+ vimvars[VV_KEY].vv_nr = idx;
+ typval_T newtv;
+ bool rem;
+ if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL
+ || did_emsg) {
+ break;
+ }
+ if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL) {
+ tv_clear(&newtv);
+ emsg(_(e_invalblob));
+ break;
+ }
+ if (filtermap != FILTERMAP_FILTER) {
+ if (newtv.vval.v_number != val) {
+ tv_blob_set(b_ret, i, (uint8_t)newtv.vval.v_number);
+ }
+ } else if (rem) {
+ char *const p = (char *)blob_arg->bv_ga.ga_data;
+ memmove(p + i, p + i + 1, (size_t)(b->bv_ga.ga_len - i - 1));
+ b->bv_ga.ga_len--;
+ i--;
+ }
+ idx++;
+ }
+
+ b->bv_lock = prev_lock;
+}
+
+/// Implementation of map() and filter() for a String.
+static void filter_map_string(const char *str, filtermap_T filtermap, typval_T *expr,
+ typval_T *rettv)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+
+ vimvars[VV_KEY].vv_type = VAR_NUMBER;
+
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char), 80);
+ int len = 0;
int idx = 0;
+ for (const char *p = str; *p != NUL; p += len) {
+ len = utfc_ptr2len(p);
+ typval_T tv = {
+ .v_type = VAR_STRING,
+ .v_lock = VAR_UNLOCKED,
+ .vval.v_string = xmemdupz(p, (size_t)len),
+ };
- // Always return the first argument, also on failure.
- tv_copy(&argvars[0], rettv);
+ vimvars[VV_KEY].vv_nr = idx;
+ typval_T newtv;
+ bool rem;
+ if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL
+ || did_emsg) {
+ tv_clear(&newtv);
+ tv_clear(&tv);
+ break;
+ } else if (filtermap != FILTERMAP_FILTER) {
+ if (newtv.v_type != VAR_STRING) {
+ tv_clear(&newtv);
+ tv_clear(&tv);
+ emsg(_(e_stringreq));
+ break;
+ } else {
+ ga_concat(&ga, newtv.vval.v_string);
+ }
+ } else if (!rem) {
+ ga_concat(&ga, tv.vval.v_string);
+ }
- if (argvars[0].v_type == VAR_BLOB) {
- if ((b = argvars[0].vval.v_blob) == NULL) {
- return;
+ tv_clear(&newtv);
+ tv_clear(&tv);
+
+ idx++;
+ }
+ ga_append(&ga, NUL);
+ rettv->vval.v_string = ga.ga_data;
+}
+
+/// Implementation of map() and filter() for a List.
+static void filter_map_list(list_T *l, filtermap_T filtermap, const char *func_name,
+ const char *arg_errmsg, typval_T *expr, typval_T *rettv)
+{
+ if (filtermap == FILTERMAP_MAPNEW) {
+ rettv->v_type = VAR_LIST;
+ rettv->vval.v_list = NULL;
+ }
+ if (l == NULL
+ || (filtermap == FILTERMAP_FILTER
+ && value_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE))) {
+ return;
+ }
+
+ list_T *l_ret = NULL;
+
+ if (filtermap == FILTERMAP_MAPNEW) {
+ tv_list_alloc_ret(rettv, kListLenUnknown);
+ l_ret = rettv->vval.v_list;
+ }
+
+ vimvars[VV_KEY].vv_type = VAR_NUMBER;
+
+ const VarLockStatus prev_lock = tv_list_locked(l);
+ if (tv_list_locked(l) == VAR_UNLOCKED) {
+ tv_list_set_lock(l, VAR_LOCKED);
+ }
+
+ int idx = 0;
+ for (listitem_T *li = tv_list_first(l); li != NULL;) {
+ if (filtermap == FILTERMAP_MAP
+ && value_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, TV_TRANSLATE)) {
+ break;
}
- } else if (argvars[0].v_type == VAR_LIST) {
- if ((l = argvars[0].vval.v_list) == NULL
- || (!map
- && value_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE))) {
- return;
+ vimvars[VV_KEY].vv_nr = idx;
+ typval_T newtv;
+ bool rem;
+ if (filter_map_one(TV_LIST_ITEM_TV(li), expr, filtermap, &newtv, &rem) == FAIL) {
+ break;
}
- } else if (argvars[0].v_type == VAR_DICT) {
- if ((d = argvars[0].vval.v_dict) == NULL
- || (!map && value_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) {
- return;
+ if (did_emsg) {
+ tv_clear(&newtv);
+ break;
}
- } else {
- semsg(_(e_listdictblobarg), ermsg);
+ if (filtermap == FILTERMAP_MAP) {
+ // map(): replace the list item value
+ tv_clear(TV_LIST_ITEM_TV(li));
+ newtv.v_lock = VAR_UNLOCKED;
+ *TV_LIST_ITEM_TV(li) = newtv;
+ } else if (filtermap == FILTERMAP_MAPNEW) {
+ // mapnew(): append the list item value
+ tv_list_append_owned_tv(l_ret, newtv);
+ }
+ if (filtermap == FILTERMAP_FILTER && rem) {
+ li = tv_list_item_remove(l, li);
+ } else {
+ li = TV_LIST_ITEM_NEXT(l, li);
+ }
+ idx++;
+ }
+
+ tv_list_set_lock(l, prev_lock);
+}
+
+/// Implementation of map() and filter().
+static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
+{
+ const char *const func_name = (filtermap == FILTERMAP_MAP
+ ? "map()"
+ : (filtermap == FILTERMAP_MAPNEW
+ ? "mapnew()"
+ : "filter()"));
+ const char *const arg_errmsg = (filtermap == FILTERMAP_MAP
+ ? N_("map() argument")
+ : (filtermap == FILTERMAP_MAPNEW
+ ? N_("mapnew() argument")
+ : N_("filter() argument")));
+
+ // map() and filter() return the first argument, also on failure.
+ if (filtermap != FILTERMAP_MAPNEW && argvars[0].v_type != VAR_STRING) {
+ tv_copy(&argvars[0], rettv);
+ }
+
+ if (argvars[0].v_type != VAR_BLOB
+ && argvars[0].v_type != VAR_LIST
+ && argvars[0].v_type != VAR_DICT
+ && argvars[0].v_type != VAR_STRING) {
+ semsg(_(e_argument_of_str_must_be_list_string_dictionary_or_blob), func_name);
return;
}
@@ -4820,104 +5373,22 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
// We reset "did_emsg" to be able to detect whether an error
// occurred during evaluation of the expression.
- save_did_emsg = did_emsg;
+ int save_did_emsg = did_emsg;
did_emsg = false;
typval_T save_key;
prepare_vimvar(VV_KEY, &save_key);
if (argvars[0].v_type == VAR_DICT) {
- vimvars[VV_KEY].vv_type = VAR_STRING;
-
- const VarLockStatus prev_lock = d->dv_lock;
- if (map && d->dv_lock == VAR_UNLOCKED) {
- d->dv_lock = VAR_LOCKED;
- }
- hashtab_T *ht = &d->dv_hashtab;
- hash_lock(ht);
- int todo = (int)ht->ht_used;
- for (hashitem_T *hi = ht->ht_array; todo > 0; hi++) {
- if (!HASHITEM_EMPTY(hi)) {
- todo--;
-
- dictitem_T *di = TV_DICT_HI2DI(hi);
- if (map
- && (value_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE)
- || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) {
- break;
- }
-
- 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) {
- break;
- }
- if (!map && rem) {
- if (var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE)
- || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) {
- break;
- }
- tv_dict_item_remove(d, di);
- }
- }
- }
- hash_unlock(ht);
- d->dv_lock = prev_lock;
+ filter_map_dict(argvars[0].vval.v_dict, filtermap, func_name,
+ arg_errmsg, expr, rettv);
} else if (argvars[0].v_type == VAR_BLOB) {
- vimvars[VV_KEY].vv_type = VAR_NUMBER;
-
- for (int i = 0; i < b->bv_ga.ga_len; i++) {
- typval_T tv;
- tv.v_type = VAR_NUMBER;
- const varnumber_T val = tv_blob_get(b, i);
- tv.vval.v_number = val;
- vimvars[VV_KEY].vv_nr = idx;
- if (filter_map_one(&tv, expr, map, &rem) == FAIL || did_emsg) {
- break;
- }
- if (tv.v_type != VAR_NUMBER) {
- emsg(_(e_invalblob));
- return;
- }
- if (map) {
- if (tv.vval.v_number != val) {
- 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;
- memmove(p + i, p + i + 1, (size_t)(b->bv_ga.ga_len - i - 1));
- b->bv_ga.ga_len--;
- i--;
- }
- idx++;
- }
+ filter_map_blob(argvars[0].vval.v_blob, filtermap, expr, arg_errmsg, rettv);
+ } else if (argvars[0].v_type == VAR_STRING) {
+ filter_map_string(tv_get_string(&argvars[0]), filtermap, expr, rettv);
} else {
assert(argvars[0].v_type == VAR_LIST);
- vimvars[VV_KEY].vv_type = VAR_NUMBER;
-
- const VarLockStatus prev_lock = tv_list_locked(l);
- if (map && tv_list_locked(l) == VAR_UNLOCKED) {
- tv_list_set_lock(l, VAR_LOCKED);
- }
- for (listitem_T *li = tv_list_first(l); li != NULL;) {
- if (map
- && value_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg,
- TV_TRANSLATE)) {
- break;
- }
- vimvars[VV_KEY].vv_nr = idx;
- if (filter_map_one(TV_LIST_ITEM_TV(li), expr, map, &rem) == FAIL
- || did_emsg) {
- break;
- }
- if (!map && rem) {
- li = tv_list_item_remove(l, li);
- } else {
- li = TV_LIST_ITEM_NEXT(l, li);
- }
- idx++;
- }
- tv_list_set_lock(l, prev_lock);
+ filter_map_list(argvars[0].vval.v_list, filtermap, func_name,
+ arg_errmsg, expr, rettv);
}
restore_vimvar(VV_KEY, &save_key);
@@ -4927,30 +5398,32 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
}
}
-static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
- FUNC_ATTR_NONNULL_ARG(1, 2)
+/// Handle one item for map() and filter().
+/// Sets v:val to "tv". Caller must set v:key.
+///
+/// @param tv original value
+/// @param expr callback
+/// @param newtv for map() an mapnew(): new value
+/// @param remp for filter(): remove flag
+static int filter_map_one(typval_T *tv, typval_T *expr, const filtermap_T filtermap,
+ typval_T *newtv, bool *remp)
+ FUNC_ATTR_NONNULL_ALL
{
- typval_T rettv;
typval_T argv[3];
int retval = FAIL;
tv_copy(tv, &vimvars[VV_VAL].vv_tv);
argv[0] = vimvars[VV_KEY].vv_tv;
argv[1] = vimvars[VV_VAL].vv_tv;
- if (eval_expr_typval(expr, argv, 2, &rettv) == FAIL) {
+ if (eval_expr_typval(expr, false, argv, 2, newtv) == FAIL) {
goto theend;
}
- if (map) {
- // map(): replace the list item value.
- tv_clear(tv);
- rettv.v_lock = VAR_UNLOCKED;
- *tv = rettv;
- } else {
+ if (filtermap == FILTERMAP_FILTER) {
bool error = false;
// filter(): when expr is zero remove the item
- *remp = (tv_get_number_chk(&rettv, &error) == 0);
- tv_clear(&rettv);
+ *remp = (tv_get_number_chk(newtv, &error) == 0);
+ tv_clear(newtv);
// On type error, nothing has been removed; return FAIL to stop the
// loop. The error message was given by tv_get_number_chk().
if (error) {
@@ -4963,6 +5436,26 @@ theend:
return retval;
}
+/// "filter()" function
+void f_filter(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ filter_map(argvars, rettv, FILTERMAP_FILTER);
+}
+
+/// "map()" function
+void f_map(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ filter_map(argvars, rettv, FILTERMAP_MAP);
+}
+
+/// "mapnew()" function
+void f_mapnew(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ filter_map(argvars, rettv, FILTERMAP_MAPNEW);
+}
+
+/// "function()" function
+/// "funcref()" function
void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref)
{
char *s;
@@ -4996,14 +5489,12 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref)
}
if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s))
|| (is_funcref && trans_name == NULL)) {
- semsg(_(e_invarg2), (use_string
- ? tv_get_string(&argvars[0])
- : (const char *)s));
+ semsg(_(e_invarg2), (use_string ? tv_get_string(&argvars[0]) : s));
// Don't check an autoload name for existence here.
} else if (trans_name != NULL
&& (is_funcref
? find_func(trans_name) == NULL
- : !translated_function_exists((const char *)trans_name))) {
+ : !translated_function_exists(trans_name))) {
semsg(_("E700: Unknown function: %s"), s);
} else {
int dict_idx = 0;
@@ -5032,8 +5523,7 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref)
arg_idx = 1;
}
if (dict_idx > 0) {
- if (argvars[dict_idx].v_type != VAR_DICT) {
- emsg(_("E922: expected a dict"));
+ if (tv_check_for_dict_arg(argvars, dict_idx) == FAIL) {
xfree(name);
goto theend;
}
@@ -5052,7 +5542,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, s);
+ emsg_funcname(e_toomanyarg, s);
xfree(name);
goto theend;
}
@@ -5122,7 +5612,7 @@ theend:
xfree(trans_name);
}
-/// Get the line number from VimL object
+/// Get the line number from Vimscript object
///
/// @note Unlike tv_get_lnum(), this one supports only "$" special string.
///
@@ -5156,7 +5646,7 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
- const char *prompt = "";
+ const char *prompt;
const char *defstr = "";
typval_T *cancelreturn = NULL;
typval_T cancelreturn_strarg2 = TV_INITIAL_VALUE;
@@ -5247,7 +5737,7 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
p = lastnl + 1;
msg_start();
msg_clr_eos();
- msg_puts_attr_len(prompt, p - prompt, echo_attr);
+ msg_puts_len(prompt, p - prompt, echo_attr);
msg_didout = false;
msg_starthere();
}
@@ -5275,9 +5765,9 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
cmd_silent = cmd_silent_save;
}
-/// Builds a process argument vector from a VimL object (typval_T).
+/// Builds a process argument vector from a Vimscript object (typval_T).
///
-/// @param[in] cmd_tv VimL object
+/// @param[in] cmd_tv Vimscript object
/// @param[out] cmd Returns the command or executable name.
/// @param[out] executable Returns `false` if argv[0] is not executable.
///
@@ -5379,15 +5869,15 @@ void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv)
return;
}
const void *iter = NULL;
+ const char *appname = get_appname();
do {
size_t dir_len;
const char *dir;
iter = vim_env_iter(ENV_SEPCHAR, dirs, iter, &dir, &dir_len);
if (dir != NULL && dir_len > 0) {
char *dir_with_nvim = xmemdupz(dir, dir_len);
- dir_with_nvim = concat_fnames_realloc(dir_with_nvim, "nvim", true);
- tv_list_append_string(list, dir_with_nvim, (ssize_t)strlen(dir_with_nvim));
- xfree(dir_with_nvim);
+ dir_with_nvim = concat_fnames_realloc(dir_with_nvim, appname, true);
+ tv_list_append_allocated_string(list, dir_with_nvim);
}
} while (iter != NULL);
xfree(dirs);
@@ -5429,7 +5919,7 @@ void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist
char **argv = tv_to_argv(&argvars[0], NULL, &executable);
if (!argv) {
if (!executable) {
- set_vim_var_nr(VV_SHELL_ERROR, (long)-1);
+ set_vim_var_nr(VV_SHELL_ERROR, -1);
}
xfree(input);
return; // Already did emsg.
@@ -5438,7 +5928,7 @@ void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist
if (p_verbose > 3) {
char *cmdstr = shell_argv_to_str(argv);
verbose_enter_scroll();
- smsg(_("Executing command: \"%s\""), cmdstr);
+ smsg(0, _("Executing command: \"%s\""), cmdstr);
msg_puts("\n\n");
verbose_leave_scroll();
xfree(cmdstr);
@@ -5459,7 +5949,7 @@ void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist
xfree(input);
- set_vim_var_nr(VV_SHELL_ERROR, (long)status);
+ set_vim_var_nr(VV_SHELL_ERROR, status);
if (res == NULL) {
if (retlist) {
@@ -5559,11 +6049,23 @@ bool callback_from_typval(Callback *const callback, const typval_T *const arg)
return true;
}
+static int callback_depth = 0;
+
+int get_callback_depth(void)
+{
+ return callback_depth;
+}
+
/// @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
{
+ if (callback_depth > MAX_CALLBACK_DEPTH) {
+ emsg(_(e_command_too_recursive));
+ return false;
+ }
+
partial_T *partial;
char *name;
Array args = ARRAY_DICT_INIT;
@@ -5596,9 +6098,6 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co
case kCallbackNone:
return false;
break;
-
- default:
- abort();
}
funcexe_T funcexe = FUNCEXE_INIT;
@@ -5606,7 +6105,11 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co
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);
+
+ callback_depth++;
+ int ret = call_func(name, -1, rettv, argcount_in, argvars_in, &funcexe);
+ callback_depth--;
+ return ret;
}
bool set_ref_in_callback(Callback *callback, int copyID, ht_stack_T **ht_stack,
@@ -5624,7 +6127,7 @@ bool set_ref_in_callback(Callback *callback, int copyID, ht_stack_T **ht_stack,
return set_ref_in_item(&tv, copyID, ht_stack, list_stack);
break;
- default:
+ case kCallbackLua:
abort();
}
return false;
@@ -5678,7 +6181,7 @@ void add_timer_info_all(typval_T *rettv)
tv_list_alloc_ret(rettv, map_size(&timers));
timer_T *timer;
map_foreach_value(&timers, timer, {
- if (!timer->stopped) {
+ if (!timer->stopped || timer->refcount > 1) {
add_timer_info(rettv, timer);
}
})
@@ -5735,7 +6238,7 @@ void timer_due_cb(TimeWatcher *tw, void *data)
timer_decref(timer);
}
-uint64_t timer_start(const long timeout, const int repeat_count, const Callback *const callback)
+uint64_t timer_start(const int64_t timeout, const int repeat_count, const Callback *const callback)
{
timer_T *timer = xmalloc(sizeof *timer);
timer->refcount = 1;
@@ -5775,7 +6278,7 @@ static void timer_close_cb(TimeWatcher *tw, void *data)
timer_T *timer = (timer_T *)data;
multiqueue_free(timer->tw.events);
callback_free(&timer->callback);
- pmap_del(uint64_t)(&timers, (uint64_t)timer->timer_id);
+ pmap_del(uint64_t)(&timers, (uint64_t)timer->timer_id, NULL);
timer_decref(timer);
}
@@ -5883,27 +6386,63 @@ write_blob_error:
return false;
}
-/// Read a blob from a file `fd`.
+/// Read blob from file "fd".
+/// Caller has allocated a blob in "rettv".
///
/// @param[in] fd File to read from.
-/// @param[in,out] blob Blob to write to.
+/// @param[in,out] rettv Blob to write to.
+/// @param[in] offset Read the file from the specified offset.
+/// @param[in] size Read the specified size, or -1 if no limit.
///
-/// @return true on success, or false on failure.
-bool read_blob(FILE *const fd, blob_T *const blob)
+/// @return OK on success, or FAIL on failure.
+int read_blob(FILE *const fd, typval_T *rettv, off_T offset, off_T size_arg)
FUNC_ATTR_NONNULL_ALL
{
+ blob_T *const blob = rettv->vval.v_blob;
FileInfo file_info;
if (!os_fileinfo_fd(fileno(fd), &file_info)) {
- return false;
+ return FAIL; // can't read the file, error
}
- const int size = (int)os_fileinfo_size(&file_info);
- ga_grow(&blob->bv_ga, size);
- blob->bv_ga.ga_len = size;
+
+ int whence;
+ off_T size = size_arg;
+ const off_T file_size = (off_T)os_fileinfo_size(&file_info);
+ if (offset >= 0) {
+ // The size defaults to the whole file. If a size is given it is
+ // limited to not go past the end of the file.
+ if (size == -1 || (size > file_size - offset && !S_ISCHR(file_info.stat.st_mode))) {
+ // size may become negative, checked below
+ size = (off_T)os_fileinfo_size(&file_info) - offset;
+ }
+ whence = SEEK_SET;
+ } else {
+ // limit the offset to not go before the start of the file
+ if (-offset > file_size && !S_ISCHR(file_info.stat.st_mode)) {
+ offset = -file_size;
+ }
+ // Size defaults to reading until the end of the file.
+ if (size == -1 || size > -offset) {
+ size = -offset;
+ }
+ whence = SEEK_END;
+ }
+ if (size <= 0) {
+ return OK;
+ }
+ if (offset != 0 && vim_fseek(fd, offset, whence) != 0) {
+ return OK;
+ }
+
+ ga_grow(&blob->bv_ga, (int)size);
+ blob->bv_ga.ga_len = (int)size;
if (fread(blob->bv_ga.ga_data, 1, (size_t)blob->bv_ga.ga_len, fd)
< (size_t)blob->bv_ga.ga_len) {
- return false;
+ // An empty blob is returned on error.
+ tv_blob_free(rettv->vval.v_blob);
+ rettv->vval.v_blob = NULL;
+ return FAIL;
}
- return true;
+ return OK;
}
/// Saves a typval_T as a string.
@@ -5914,7 +6453,7 @@ bool read_blob(FILE *const fd, blob_T *const blob)
/// @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
+/// @returns an allocated string if `tv` represents a Vimscript string, list, or
/// number; NULL otherwise.
char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl, bool crlf)
FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
@@ -5941,7 +6480,7 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl, bool crl
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 = ml_get_buf(buf, lnum, false); *p != NUL; p++) {
+ for (char *p = ml_get_buf(buf, lnum); *p != NUL; p++) {
*len += 1;
}
*len += 1;
@@ -5959,7 +6498,7 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl, bool crl
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 = ml_get_buf(buf, lnum, false); *p != NUL; p++) {
+ for (char *p = ml_get_buf(buf, lnum); *p != NUL; p++) {
*end++ = (*p == '\n') ? NUL : *p;
}
*end++ = '\n';
@@ -6011,7 +6550,7 @@ int buf_byteidx_to_charidx(buf_T *buf, linenr_T lnum, int byteidx)
lnum = buf->b_ml.ml_line_count;
}
- char *str = ml_get_buf(buf, lnum, false);
+ char *str = ml_get_buf(buf, lnum);
if (*str == NUL) {
return 0;
@@ -6049,7 +6588,7 @@ int buf_charidx_to_byteidx(buf_T *buf, linenr_T lnum, int charidx)
lnum = buf->b_ml.ml_line_count;
}
- char *str = ml_get_buf(buf, lnum, false);
+ char *str = ml_get_buf(buf, lnum);
// Convert the character offset to a byte offset
char *t = str;
@@ -6060,7 +6599,7 @@ int buf_charidx_to_byteidx(buf_T *buf, linenr_T lnum, int charidx)
return (int)(t - str);
}
-/// Translate a VimL object into a position
+/// Translate a Vimscript object into a position
///
/// Accepts VAR_LIST and VAR_STRING objects. Does not give an error for invalid
/// type.
@@ -6087,14 +6626,14 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
}
// Get the line number.
- pos.lnum = (linenr_T)tv_list_find_nr(l, 0L, &error);
+ pos.lnum = (linenr_T)tv_list_find_nr(l, 0, &error);
if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) {
// Invalid line number.
return NULL;
}
// Get the column number.
- pos.col = (colnr_T)tv_list_find_nr(l, 1L, &error);
+ pos.col = (colnr_T)tv_list_find_nr(l, 1, &error);
if (error) {
return NULL;
}
@@ -6106,7 +6645,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
}
// We accept "$" for the column number: last column.
- listitem_T *li = tv_list_find(l, 1L);
+ listitem_T *li = tv_list_find(l, 1);
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) {
@@ -6121,7 +6660,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
pos.col--;
// Get the virtual offset. Defaults to zero.
- pos.coladd = (colnr_T)tv_list_find_nr(l, 2L, &error);
+ pos.coladd = (colnr_T)tv_list_find_nr(l, 2, &error);
if (error) {
pos.coladd = 0;
}
@@ -6154,7 +6693,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
}
pos = fm->mark;
// Vimscript behavior, only provide fnum if mark is global.
- *ret_fnum = ASCII_ISUPPER(mname) || ascii_isdigit(mname) ? fm->fnum: *ret_fnum;
+ *ret_fnum = ASCII_ISUPPER(mname) || ascii_isdigit(mname) ? fm->fnum : *ret_fnum;
}
if (pos.lnum != 0) {
if (charcol) {
@@ -6166,6 +6705,10 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
pos.coladd = 0;
if (name[0] == 'w' && dollar_lnum) {
+ // the "w_valid" flags are not reset when moving the cursor, but they
+ // do matter for update_topline() and validate_botline().
+ check_cursor_moved(curwin);
+
pos.col = 0;
if (name[1] == '0') { // "w0": first visible line
update_topline(curwin);
@@ -6220,25 +6763,25 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool c
}
int i = 0;
- long n;
+ int n;
if (fnump != NULL) {
- n = tv_list_find_nr(l, i++, NULL); // fnum
+ n = (int)tv_list_find_nr(l, i++, NULL); // fnum
if (n < 0) {
return FAIL;
}
if (n == 0) {
n = curbuf->b_fnum; // Current buffer.
}
- *fnump = (int)n;
+ *fnump = n;
}
- n = tv_list_find_nr(l, i++, NULL); // lnum
+ n = (int)tv_list_find_nr(l, i++, NULL); // lnum
if (n < 0) {
return FAIL;
}
- posp->lnum = (linenr_T)n;
+ posp->lnum = n;
- n = tv_list_find_nr(l, i++, NULL); // col
+ n = (int)tv_list_find_nr(l, i++, NULL); // col
if (n < 0) {
return FAIL;
}
@@ -6252,15 +6795,15 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool c
}
n = buf_charidx_to_byteidx(buf,
posp->lnum == 0 ? curwin->w_cursor.lnum : posp->lnum,
- (int)n) + 1;
+ n) + 1;
}
- posp->col = (colnr_T)n;
+ posp->col = n;
- n = tv_list_find_nr(l, i, NULL); // off
+ n = (int)tv_list_find_nr(l, i, NULL); // off
if (n < 0) {
posp->coladd = 0;
} else {
- posp->coladd = (colnr_T)n;
+ posp->coladd = n;
}
if (curswantp != NULL) {
@@ -6314,7 +6857,7 @@ int get_id_len(const char **const arg)
}
len = (int)(p - *arg);
- *arg = (const char *)skipwhite(p);
+ *arg = skipwhite(p);
return len;
}
@@ -6352,7 +6895,7 @@ int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbo
if (expr_start != NULL) {
if (!evaluate) {
len += (int)(p - *arg);
- *arg = (const char *)skipwhite(p);
+ *arg = skipwhite(p);
return len;
}
@@ -6363,7 +6906,7 @@ int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbo
return -1;
}
*alias = temp_string;
- *arg = (const char *)skipwhite(p);
+ *arg = skipwhite(p);
return (int)strlen(temp_string);
}
@@ -6482,15 +7025,14 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex
}
char *retval = NULL;
- char *nextcmd = NULL;
*expr_start = NUL;
*expr_end = NUL;
char c1 = *in_end;
*in_end = NUL;
- char *temp_result = eval_to_string(expr_start + 1, &nextcmd, false);
- if (temp_result != NULL && nextcmd == NULL) {
+ char *temp_result = eval_to_string(expr_start + 1, false);
+ if (temp_result != NULL) {
retval = xmalloc(strlen(temp_result) + (size_t)(expr_start - in_start)
+ (size_t)(in_end - expr_end) + 1);
STRCPY(retval, in_start);
@@ -6578,7 +7120,7 @@ dict_T *get_vim_var_dict(int idx) FUNC_ATTR_PURE
/// Set v:char to character "c".
void set_vim_var_char(int c)
{
- char buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXCHAR + 1];
buf[utf_char2bytes(c, buf)] = NUL;
set_vim_var_string(VV_CHAR, buf, -1);
@@ -6587,7 +7129,7 @@ void set_vim_var_char(int c)
/// Set v:count to "count" and v:count1 to "count1".
///
/// @param set_prevcount if true, first set v:prevcount from v:count.
-void set_vcount(long count, long count1, int set_prevcount)
+void set_vcount(int64_t count, int64_t count1, bool set_prevcount)
{
if (set_prevcount) {
vimvars[VV_PREVCOUNT].vv_nr = vimvars[VV_COUNT].vv_nr;
@@ -6681,14 +7223,23 @@ void set_vim_var_dict(const VimVarIndex idx, dict_T *const val)
tv_dict_set_keys_readonly(val);
}
+/// Set v:variable to tv.
+///
+/// @param[in] idx Index of variable to set.
+/// @param[in] val Value to set to. Will be copied.
+void set_vim_var_tv(const VimVarIndex idx, typval_T *const tv)
+{
+ tv_clear(&vimvars[idx].vv_di.di_tv);
+ tv_copy(tv, &vimvars[idx].vv_di.di_tv);
+}
+
/// Set the v:argv list.
void set_argv_var(char **argv, int argc)
{
list_T *l = tv_list_alloc(argc);
- int i;
tv_list_set_lock(l, VAR_FIXED);
- for (i = 0; i < argc; i++) {
+ for (int i = 0; i < argc; i++) {
tv_list_append_string(l, (const char *const)argv[i], -1);
TV_LIST_ITEM_TV(tv_list_last(l))->v_lock = VAR_FIXED;
}
@@ -6747,20 +7298,18 @@ char *set_cmdarg(exarg_T *eap, char *oldarg)
{
char *oldval = vimvars[VV_CMDARG].vv_str;
if (eap == NULL) {
- xfree(oldval);
- vimvars[VV_CMDARG].vv_str = oldarg;
- return NULL;
+ goto error;
}
size_t len = 0;
if (eap->force_bin == FORCE_BIN) {
- len = 6;
+ len += 6; // " ++bin"
} else if (eap->force_bin == FORCE_NOBIN) {
- len = 8;
+ len += 8; // " ++nobin"
}
if (eap->read_edit) {
- len += 7;
+ len += 7; // " ++edit"
}
if (eap->force_ff != 0) {
@@ -6773,48 +7322,89 @@ char *set_cmdarg(exarg_T *eap, char *oldarg)
len += 7 + 4; // " ++bad=" + "keep" or "drop"
}
if (eap->mkdir_p != 0) {
- len += 4;
+ len += 4; // " ++p"
}
const size_t newval_len = len + 1;
char *newval = xmalloc(newval_len);
+ size_t xlen = 0;
+ int rc = 0;
if (eap->force_bin == FORCE_BIN) {
- snprintf(newval, newval_len, " ++bin");
+ rc = snprintf(newval, newval_len, " ++bin");
} else if (eap->force_bin == FORCE_NOBIN) {
- snprintf(newval, newval_len, " ++nobin");
+ rc = snprintf(newval, newval_len, " ++nobin");
} else {
*newval = NUL;
}
+ if (rc < 0) {
+ goto error;
+ }
+ xlen += (size_t)rc;
if (eap->read_edit) {
- STRCAT(newval, " ++edit");
+ rc = snprintf(newval + xlen, newval_len - xlen, " ++edit");
+ if (rc < 0) {
+ goto error;
+ }
+ xlen += (size_t)rc;
}
if (eap->force_ff != 0) {
- snprintf(newval + strlen(newval), newval_len, " ++ff=%s",
- eap->force_ff == 'u' ? "unix" :
- eap->force_ff == 'd' ? "dos" : "mac");
+ rc = snprintf(newval + xlen,
+ newval_len - xlen,
+ " ++ff=%s",
+ eap->force_ff == 'u' ? "unix"
+ : eap->force_ff == 'd' ? "dos" : "mac");
+ if (rc < 0) {
+ goto error;
+ }
+ xlen += (size_t)rc;
}
if (eap->force_enc != 0) {
- snprintf(newval + strlen(newval), newval_len, " ++enc=%s",
- eap->cmd + eap->force_enc);
+ rc = snprintf(newval + (xlen), newval_len - xlen, " ++enc=%s", eap->cmd + eap->force_enc);
+ if (rc < 0) {
+ goto error;
+ }
+ xlen += (size_t)rc;
}
+
if (eap->bad_char == BAD_KEEP) {
- STRCPY(newval + strlen(newval), " ++bad=keep");
+ rc = snprintf(newval + xlen, newval_len - xlen, " ++bad=keep");
+ if (rc < 0) {
+ goto error;
+ }
+ xlen += (size_t)rc;
} else if (eap->bad_char == BAD_DROP) {
- STRCPY(newval + strlen(newval), " ++bad=drop");
+ rc = snprintf(newval + xlen, newval_len - xlen, " ++bad=drop");
+ if (rc < 0) {
+ goto error;
+ }
+ xlen += (size_t)rc;
} else if (eap->bad_char != 0) {
- snprintf(newval + strlen(newval), newval_len, " ++bad=%c",
- eap->bad_char);
+ rc = snprintf(newval + xlen, newval_len - xlen, " ++bad=%c", eap->bad_char);
+ if (rc < 0) {
+ goto error;
+ }
+ xlen += (size_t)rc;
}
- if (eap->mkdir_p) {
- snprintf(newval, newval_len, " ++p");
+ if (eap->mkdir_p != 0) {
+ rc = snprintf(newval + xlen, newval_len - xlen, " ++p");
+ if (rc < 0) {
+ goto error;
+ }
+ xlen += (size_t)rc;
}
+ assert(xlen <= newval_len);
vimvars[VV_CMDARG].vv_str = newval;
return oldval;
+
+error:
+ xfree(oldval);
+ vimvars[VV_CMDARG].vv_str = oldarg;
+ return NULL;
}
/// Check if variable "name[len]" is a local variable or an argument.
@@ -6871,6 +7461,88 @@ int check_luafunc_name(const char *const str, const bool paren)
return (int)(p - str);
}
+/// Return the character "str[index]" where "index" is the character index. If
+/// "index" is out of range NULL is returned.
+char *char_from_string(const char *str, varnumber_T index)
+{
+ size_t nbyte = 0;
+ varnumber_T nchar = index;
+
+ if (str == NULL || index < 0) {
+ return NULL;
+ }
+ size_t slen = strlen(str);
+ while (nchar > 0 && nbyte < slen) {
+ nbyte += (size_t)utf_ptr2len(str + nbyte);
+ nchar--;
+ }
+ if (nbyte >= slen) {
+ return NULL;
+ }
+ return xmemdupz(str + nbyte, (size_t)utf_ptr2len(str + nbyte));
+}
+
+/// Get the byte index for character index "idx" in string "str" with length
+/// "str_len".
+/// If going over the end return "str_len".
+/// If "idx" is negative count from the end, -1 is the last character.
+/// When going over the start return -1.
+static ssize_t char_idx2byte(const char *str, size_t str_len, varnumber_T idx)
+{
+ varnumber_T nchar = idx;
+ size_t nbyte = 0;
+
+ if (nchar >= 0) {
+ while (nchar > 0 && nbyte < str_len) {
+ nbyte += (size_t)utf_ptr2len(str + nbyte);
+ nchar--;
+ }
+ } else {
+ nbyte = str_len;
+ while (nchar < 0 && nbyte > 0) {
+ nbyte--;
+ nbyte -= (size_t)utf_head_off(str, str + nbyte);
+ nchar++;
+ }
+ if (nchar < 0) {
+ return -1;
+ }
+ }
+ return (ssize_t)nbyte;
+}
+
+/// Return the slice "str[first:last]" using character indexes.
+///
+/// @param exclusive true for slice().
+///
+/// Return NULL when the result is empty.
+char *string_slice(const char *str, varnumber_T first, varnumber_T last, bool exclusive)
+{
+ if (str == NULL) {
+ return NULL;
+ }
+ size_t slen = strlen(str);
+ ssize_t start_byte = char_idx2byte(str, slen, first);
+ if (start_byte < 0) {
+ start_byte = 0; // first index very negative: use zero
+ }
+ ssize_t end_byte;
+ if ((last == -1 && !exclusive) || last == VARNUMBER_MAX) {
+ end_byte = (ssize_t)slen;
+ } else {
+ end_byte = char_idx2byte(str, slen, last);
+ if (!exclusive && end_byte >= 0 && end_byte < (ssize_t)slen) {
+ // end index is inclusive
+ end_byte += utf_ptr2len(str + end_byte);
+ }
+ }
+
+ if (start_byte >= (ssize_t)slen || end_byte <= start_byte) {
+ return NULL;
+ }
+ return xmemdupz(str + start_byte, (size_t)(end_byte - start_byte));
+}
+
/// Handle:
/// - expr[expr], expr[expr:expr] subscript
/// - ".name" lookup
@@ -6879,13 +7551,13 @@ int check_luafunc_name(const char *const str, const bool paren)
///
/// Can all be combined in any order: dict.func(expr)[idx]['func'](expr)->len()
///
-/// @param evaluate do more than finding the end
/// @param verbose give error messages
/// @param start_leader start of '!' and '-' prefixes
/// @param end_leaderp end of '!' and '-' prefixes
-int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int verbose,
- const char *const start_leader, const char **const end_leaderp)
+int handle_subscript(const char **const arg, typval_T *rettv, evalarg_T *const evalarg,
+ bool verbose)
{
+ const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
int ret = OK;
dict_T *selfdict = NULL;
const char *lua_funcname = NULL;
@@ -6914,7 +7586,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int
&& !ascii_iswhite(*(*arg - 1)))
|| (**arg == '-' && (*arg)[1] == '>'))) {
if (**arg == '(') {
- ret = call_func_rettv((char **)arg, rettv, evaluate, selfdict, NULL, lua_funcname);
+ ret = call_func_rettv((char **)arg, evalarg, rettv, evaluate, selfdict, NULL, lua_funcname);
// Stop the expression evaluation when immediately aborting on
// error, or when an interrupt occurred or an exception was thrown
@@ -6928,19 +7600,12 @@ int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int
tv_dict_unref(selfdict);
selfdict = NULL;
} else if (**arg == '-') {
- // Expression "-1.0->method()" applies the leader "-" before
- // applying ->.
- if (evaluate && *end_leaderp > start_leader) {
- ret = eval7_leader(rettv, (char *)start_leader, end_leaderp);
- }
- if (ret == OK) {
- if ((*arg)[2] == '{') {
- // expr->{lambda}()
- ret = eval_lambda((char **)arg, rettv, evaluate, verbose);
- } else {
- // expr->name()
- ret = eval_method((char **)arg, rettv, evaluate, verbose);
- }
+ if ((*arg)[2] == '{') {
+ // expr->{lambda}()
+ ret = eval_lambda((char **)arg, rettv, evalarg, verbose);
+ } else {
+ // expr->name()
+ ret = eval_method((char **)arg, rettv, evalarg, verbose);
}
} else { // **arg == '[' || **arg == '.'
tv_dict_unref(selfdict);
@@ -6952,7 +7617,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int
} else {
selfdict = NULL;
}
- if (eval_index((char **)arg, rettv, evaluate, verbose) == FAIL) {
+ if (eval_index((char **)arg, rettv, evalarg, verbose) == FAIL) {
tv_clear(rettv);
ret = FAIL;
}
@@ -7105,7 +7770,7 @@ hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char
if (funccal == NULL) { // global variable
*d = &globvardict;
} else { // l: variable
- *d = &funccal->l_vars;
+ *d = &funccal->fc_l_vars;
}
goto end;
}
@@ -7129,13 +7794,13 @@ hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char
} else if (*name == 'v') { // v: variable
*d = &vimvardict;
} else if (*name == 'a' && funccal != NULL) { // function argument
- *d = &funccal->l_avars;
+ *d = &funccal->fc_l_avars;
} else if (*name == 'l' && funccal != NULL) { // local variable
- *d = &funccal->l_vars;
+ *d = &funccal->fc_l_vars;
} else if (*name == 's' // script variable
&& (current_sctx.sc_sid > 0 || current_sctx.sc_sid == SID_STR
|| current_sctx.sc_sid == SID_LUA)
- && current_sctx.sc_sid <= ga_scripts.ga_len) {
+ && current_sctx.sc_sid <= script_items.ga_len) {
// For anonymous scripts without a script item, create one now so script vars can be used
if (current_sctx.sc_sid == SID_LUA) {
// try to resolve lua filename & line no so it can be shown in lastset messages.
@@ -7186,27 +7851,9 @@ hashtab_T *find_var_ht(const char *name, const size_t name_len, const char **var
/// sourcing this script and when executing functions defined in the script.
void new_script_vars(scid_T id)
{
- scriptvar_T *sv;
-
- ga_grow(&ga_scripts, id - ga_scripts.ga_len);
-
- // Re-allocating ga_data means that an ht_array pointing to
- // ht_smallarray becomes invalid. We can recognize this: ht_mask is
- // at its init value. Also reset "v_dict", it's always the same.
- for (int i = 1; i <= ga_scripts.ga_len; i++) {
- hashtab_T *ht = &SCRIPT_VARS(i);
- if (ht->ht_mask == HT_INIT_SIZE - 1) {
- ht->ht_array = ht->ht_smallarray;
- }
- sv = SCRIPT_SV(i);
- sv->sv_var.di_tv.vval.v_dict = &sv->sv_dict;
- }
-
- while (ga_scripts.ga_len < id) {
- sv = SCRIPT_SV(ga_scripts.ga_len + 1) = xcalloc(1, sizeof(scriptvar_T));
- init_var_dict(&sv->sv_dict, &sv->sv_var, VAR_SCOPE);
- ga_scripts.ga_len++;
- }
+ scriptvar_T *sv = xcalloc(1, sizeof(scriptvar_T));
+ init_var_dict(&sv->sv_dict, &sv->sv_var, VAR_SCOPE);
+ SCRIPT_ITEM(id)->sn_vars = sv;
}
/// Initialize dictionary "dict" as a scope and set variable "dict_var" to
@@ -7259,7 +7906,7 @@ int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *c
int ret = OK;
if (recurse >= DICT_MAXNEST) {
- emsg(_("E698: variable nested too deep for making a copy"));
+ emsg(_(e_variable_nested_too_deep_for_making_copy));
return FAIL;
}
recurse++;
@@ -7305,7 +7952,7 @@ int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *c
}
break;
case VAR_BLOB:
- tv_blob_copy(from, to);
+ tv_blob_copy(from->vval.v_blob, to);
break;
case VAR_DICT:
to->v_type = VAR_DICT;
@@ -7342,6 +7989,9 @@ void ex_echo(exarg_T *eap)
bool need_clear = true;
const int did_emsg_before = did_emsg;
const int called_emsg_before = called_emsg;
+ evalarg_T evalarg;
+
+ fill_evalarg_from_eap(&evalarg, eap, eap->skip);
if (eap->skip) {
emsg_skip++;
@@ -7353,7 +8003,7 @@ void ex_echo(exarg_T *eap)
{
char *p = arg;
- if (eval1(&arg, &rettv, !eap->skip) == FAIL) {
+ if (eval1(&arg, &rettv, &evalarg) == FAIL) {
// Report the invalid expression unless the expression evaluation
// has been cancelled due to an aborting error, an interrupt, or an
// exception.
@@ -7385,7 +8035,7 @@ void ex_echo(exarg_T *eap)
char *tofree = encode_tv2echo(&rettv, NULL);
if (*tofree != NUL) {
msg_ext_set_kind("echo");
- msg_multiline_attr(tofree, echo_attr, true, &need_clear);
+ msg_multiline(tofree, echo_attr, true, &need_clear);
}
xfree(tofree);
}
@@ -7393,6 +8043,7 @@ void ex_echo(exarg_T *eap)
arg = skipwhite(arg);
}
eap->nextcmd = check_nextcmd(arg);
+ clear_evalarg(&evalarg, eap);
if (eap->skip) {
emsg_skip--;
@@ -7431,23 +8082,23 @@ void ex_execute(exarg_T *eap)
emsg_skip++;
}
while (*arg != NUL && *arg != '|' && *arg != '\n') {
- ret = eval1_emsg(&arg, &rettv, !eap->skip);
+ ret = eval1_emsg(&arg, &rettv, eap);
if (ret == FAIL) {
break;
}
if (!eap->skip) {
const char *const argstr = eap->cmdidx == CMD_execute
- ? tv_get_string(&rettv)
- : rettv.v_type == VAR_STRING
- ? encode_tv2echo(&rettv, NULL)
- : encode_tv2string(&rettv, NULL);
+ ? tv_get_string(&rettv)
+ : rettv.v_type == VAR_STRING
+ ? encode_tv2echo(&rettv, NULL)
+ : encode_tv2string(&rettv, NULL);
const size_t len = strlen(argstr);
ga_grow(&ga, (int)len + 2);
if (!GA_EMPTY(&ga)) {
- ((char_u *)(ga.ga_data))[ga.ga_len++] = ' ';
+ ((char *)(ga.ga_data))[ga.ga_len++] = ' ';
}
- memcpy((char_u *)(ga.ga_data) + ga.ga_len, argstr, len + 1);
+ memcpy((char *)(ga.ga_data) + ga.ga_len, argstr, len + 1);
if (eap->cmdidx != CMD_execute) {
xfree((void *)argstr);
}
@@ -7459,22 +8110,14 @@ void ex_execute(exarg_T *eap)
}
if (ret != FAIL && ga.ga_data != NULL) {
- if (eap->cmdidx == CMD_echomsg || eap->cmdidx == CMD_echoerr) {
- // Mark the already saved text as finishing the line, so that what
- // follows is displayed on a new line when scrolling back at the
- // more prompt.
- msg_sb_eol();
- }
-
if (eap->cmdidx == CMD_echomsg) {
msg_ext_set_kind("echomsg");
- msg_attr(ga.ga_data, echo_attr);
- ui_flush();
+ msg(ga.ga_data, echo_attr);
} else if (eap->cmdidx == CMD_echoerr) {
// We don't want to abort following commands, restore did_emsg.
int save_did_emsg = did_emsg;
msg_ext_set_kind("echoerr");
- emsg(ga.ga_data);
+ emsg_multiline(ga.ga_data, true);
if (!force_abort) {
did_emsg = save_did_emsg;
}
@@ -7528,80 +8171,6 @@ const char *find_option_end(const char **const arg, int *const scope)
return p;
}
-/// Return the autoload script name for a function or variable name
-/// Caller must make sure that "name" contains AUTOLOAD_CHAR.
-///
-/// @param[in] name Variable/function name.
-/// @param[in] name_len Name length.
-///
-/// @return [allocated] autoload script name.
-char *autoload_name(const char *const name, const size_t name_len)
- FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
-{
- // Get the script file name: replace '#' with '/', append ".vim".
- char *const scriptname = xmalloc(name_len + sizeof("autoload/.vim"));
- memcpy(scriptname, "autoload/", sizeof("autoload/") - 1);
- memcpy(scriptname + sizeof("autoload/") - 1, name, name_len);
- size_t auchar_idx = 0;
- for (size_t i = sizeof("autoload/") - 1;
- i - sizeof("autoload/") + 1 < name_len;
- i++) {
- if (scriptname[i] == AUTOLOAD_CHAR) {
- scriptname[i] = '/';
- auchar_idx = i;
- }
- }
- memcpy(scriptname + auchar_idx, ".vim", sizeof(".vim"));
-
- return scriptname;
-}
-
-/// If name has a package name try autoloading the script for it
-///
-/// @param[in] name Variable/function name.
-/// @param[in] name_len Name length.
-/// @param[in] reload If true, load script again when already loaded.
-///
-/// @return true if a package was loaded.
-bool script_autoload(const char *const name, const size_t name_len, const bool reload)
-{
- // If there is no '#' after name[0] there is no package name.
- const char *p = memchr(name, AUTOLOAD_CHAR, name_len);
- if (p == NULL || p == name) {
- return false;
- }
-
- bool ret = false;
- char *tofree = autoload_name(name, name_len);
- char *scriptname = tofree;
-
- // Find the name in the list of previously loaded package names. Skip
- // "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) {
- break;
- }
- }
- if (!reload && i < ga_loaded.ga_len) {
- ret = false; // Was loaded already.
- } else {
- // Remember the name if it wasn't loaded already.
- if (i == ga_loaded.ga_len) {
- GA_APPEND(char *, &ga_loaded, scriptname);
- tofree = NULL;
- }
-
- // Try loading the package from $VIMRUNTIME/autoload/<name>.vim
- if (source_runtime(scriptname, 0) == OK) {
- ret = true;
- }
- }
-
- xfree(tofree);
- return ret;
-}
-
static var_flavour_T var_flavour(char *varname)
FUNC_ATTR_PURE
{
@@ -7650,7 +8219,7 @@ const void *var_shada_iter(const void *const iter, const char **const name, typv
} else {
hi = (const hashitem_T *)iter;
}
- *name = (char *)TV_DICT_HI2DI(hi)->di_key;
+ *name = 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(hi->hi_key) & flavour)) {
@@ -7674,10 +8243,10 @@ int store_session_globals(FILE *fd)
TV_DICT_ITER(&globvardict, this_var, {
if ((this_var->di_tv.v_type == VAR_NUMBER
|| this_var->di_tv.v_type == VAR_STRING)
- && var_flavour((char *)this_var->di_key) == VAR_FLAVOUR_SESSION) {
+ && var_flavour(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(tv_get_string(&this_var->di_tv), "\\\"\n\r");
+ char *const p = vim_strsave_escaped(tv_get_string(&this_var->di_tv), "\\\"\n\r");
for (char *t = p; *t != NUL; t++) {
if (*t == '\n') {
*t = 'n';
@@ -7696,7 +8265,7 @@ int store_session_globals(FILE *fd)
}
xfree(p);
} else if (this_var->di_tv.v_type == VAR_FLOAT
- && var_flavour((char *)this_var->di_key) == VAR_FLAVOUR_SESSION) {
+ && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) {
float_T f = this_var->di_tv.vval.v_float;
int sign = ' ';
@@ -7737,7 +8306,7 @@ void option_last_set_msg(LastSet last_set)
msg_puts(p);
if (last_set.script_ctx.sc_lnum > 0) {
msg_puts(_(line_msg));
- msg_outnum((long)last_set.script_ctx.sc_lnum);
+ msg_outnum(last_set.script_ctx.sc_lnum);
}
if (should_free) {
xfree(p);
@@ -7843,7 +8412,7 @@ repeat:
// ":~" - path relative to the home directory
// ":8" - shortname path - postponed till after
while (src[*usedlen] == ':'
- && ((c = (char_u)src[*usedlen + 1]) == '.' || c == '~' || c == '8')) {
+ && ((c = (uint8_t)src[*usedlen + 1]) == '.' || c == '~' || c == '8')) {
*usedlen += 2;
if (c == '8') {
continue;
@@ -7987,7 +8556,7 @@ repeat:
// "path/to/this.file.ext" :r:r:r
// ^ ^------------- tail
// +--------------------- *fnamep
- if (s > MAX(tail, (char *)(*fnamep))) {
+ if (s > MAX(tail, *fnamep)) {
*fnamelen = (size_t)(s - *fnamep);
}
}
@@ -8013,13 +8582,13 @@ repeat:
// find end of pattern
p = vim_strchr(s, sep);
if (p != NULL) {
- char *const pat = xstrnsave(s, (size_t)(p - s));
+ char *const pat = xmemdupz(s, (size_t)(p - s));
s = p + 1;
// find end of substitution
p = vim_strchr(s, sep);
if (p != NULL) {
- char *const sub = xstrnsave(s, (size_t)(p - s));
- char *const str = xstrnsave(*fnamep, *fnamelen);
+ char *const sub = xmemdupz(s, (size_t)(p - s));
+ char *const str = xmemdupz(*fnamep, *fnamelen);
*usedlen = (size_t)(p + 1 - src);
s = do_string_sub(str, pat, sub, NULL, flags);
*fnamep = s;
@@ -8065,14 +8634,13 @@ repeat:
/// @return an allocated string, NULL for error.
char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char *flags)
{
- int sublen;
regmatch_T regmatch;
garray_T ga;
char *zero_width = NULL;
// Make 'cpoptions' empty, so that the 'l' flag doesn't work here
char *save_cpo = p_cpo;
- p_cpo = empty_option;
+ p_cpo = empty_string_option;
ga_init(&ga, 1, 200);
@@ -8081,6 +8649,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char
regmatch.rm_ic = p_ic;
regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL) {
+ int sublen;
char *tail = str;
char *end = str + strlen(str);
while (vim_regexec_nl(&regmatch, str, (colnr_T)(tail - str))) {
@@ -8089,7 +8658,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char
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);
+ memmove((char *)ga.ga_data + ga.ga_len, tail, (size_t)i);
ga.ga_len += i;
tail += i;
continue;
@@ -8103,12 +8672,16 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char
// - The substituted text.
// - The text after the match.
sublen = vim_regsub(&regmatch, sub, expr, tail, 0, REGSUB_MAGIC);
+ if (sublen <= 0) {
+ ga_clear(&ga);
+ break;
+ }
ga_grow(&ga, (int)((end - tail) + sublen -
(regmatch.endp[0] - regmatch.startp[0])));
// copy the text up to where the match is
int i = (int)(regmatch.startp[0] - tail);
- memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i);
+ memmove((char *)ga.ga_data + ga.ga_len, tail, (size_t)i);
// add the substituted text
(void)vim_regsub(&regmatch, sub, expr,
(char *)ga.ga_data + ga.ga_len + i, sublen,
@@ -8132,14 +8705,14 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char
char *ret = xstrdup(ga.ga_data == NULL ? str : ga.ga_data);
ga_clear(&ga);
- if (p_cpo == empty_option) {
+ if (p_cpo == empty_string_option) {
p_cpo = save_cpo;
} else {
// Darn, evaluating {sub} expression or {expr} changed the value.
// If it's still empty it was changed and restored, need to restore in
// the complicated way.
if (*p_cpo == NUL) {
- set_option_value_give_err("cpo", 0L, save_cpo, 0);
+ set_option_value_give_err("cpo", CSTR_AS_OPTVAL(save_cpo), 0);
}
free_string_option(save_cpo);
}
@@ -8203,7 +8776,7 @@ void script_host_eval(char *name, typval_T *argvars, typval_T *rettv)
}
list_T *args = tv_list_alloc(1);
- tv_list_append_string(args, (const char *)argvars[0].vval.v_string, -1);
+ tv_list_append_string(args, argvars[0].vval.v_string, -1);
*rettv = eval_call_provider(name, "eval", args, false);
}
@@ -8217,7 +8790,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, boo
return (typval_T){
.v_type = VAR_NUMBER,
.v_lock = VAR_UNLOCKED,
- .vval.v_number = (varnumber_T)0
+ .vval.v_number = 0
};
}
@@ -8231,6 +8804,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, boo
.es_entry = ((estack_T *)exestack.ga_data)[exestack.ga_len - 1],
.autocmd_fname = autocmd_fname,
.autocmd_match = autocmd_match,
+ .autocmd_fname_full = autocmd_fname_full,
.autocmd_bufnr = autocmd_bufnr,
.funccalp = (void *)get_current_funccal()
};
@@ -8281,7 +8855,7 @@ bool eval_has_provider(const char *feat)
return false;
}
- char name[32]; // Normalized: "python_compiled" => "python".
+ char name[32]; // Normalized: "python3_compiled" => "python3".
snprintf(name, sizeof(name), "%s", feat);
strchrsub(name, '_', '\0'); // Chop any "_xx" suffix.
@@ -8289,14 +8863,14 @@ bool eval_has_provider(const char *feat)
typval_T tv;
// Get the g:loaded_xx_provider variable.
int len = snprintf(buf, sizeof(buf), "g:loaded_%s_provider", name);
- if (get_var_tv(buf, len, &tv, NULL, false, true) == FAIL) {
+ if (eval_variable(buf, len, &tv, NULL, false, true) == FAIL) {
// Trigger autoload once.
len = snprintf(buf, sizeof(buf), "provider#%s#bogus", name);
script_autoload(buf, (size_t)len, false);
// Retry the (non-autoload-style) variable.
len = snprintf(buf, sizeof(buf), "g:loaded_%s_provider", name);
- if (get_var_tv(buf, len, &tv, NULL, false, true) == FAIL) {
+ if (eval_variable(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(buf) && p_lpl) {
@@ -8308,8 +8882,8 @@ bool eval_has_provider(const char *feat)
}
bool ok = (tv.v_type == VAR_NUMBER)
- ? 2 == tv.vval.v_number // Value of 2 means "loaded and working".
- : false;
+ ? 2 == tv.vval.v_number // Value of 2 means "loaded and working".
+ : false;
if (ok) {
// Call() must be defined if provider claims to be working.
@@ -8337,33 +8911,27 @@ void eval_fmt_source_name_line(char *buf, size_t bufsize)
/// ":checkhealth [plugins]"
void ex_checkhealth(exarg_T *eap)
{
- bool found = !!find_func("health#check");
- if (!found
- && script_autoload("health#check", sizeof("health#check") - 1, false)) {
- found = !!find_func("health#check");
+ Error err = ERROR_INIT;
+ MAXSIZE_TEMP_ARRAY(args, 1);
+ ADD_C(args, CSTR_AS_OBJ(eap->arg));
+ NLUA_EXEC_STATIC("return vim.health._check(...)", args, &err);
+ if (!ERROR_SET(&err)) {
+ return;
}
- if (!found) {
- const char *vimruntime_env = os_getenv("VIMRUNTIME");
- if (vimruntime_env == NULL) {
- emsg(_("E5009: $VIMRUNTIME is empty or unset"));
+
+ const char *vimruntime_env = os_getenv("VIMRUNTIME");
+ if (vimruntime_env == NULL) {
+ emsg(_("E5009: $VIMRUNTIME is empty or unset"));
+ } else {
+ bool rtp_ok = NULL != strstr(p_rtp, vimruntime_env);
+ if (rtp_ok) {
+ semsg(_("E5009: Invalid $VIMRUNTIME: %s"), vimruntime_env);
} else {
- bool rtp_ok = NULL != strstr(p_rtp, vimruntime_env);
- if (rtp_ok) {
- semsg(_("E5009: Invalid $VIMRUNTIME: %s"), vimruntime_env);
- } else {
- emsg(_("E5009: Invalid 'runtimepath'"));
- }
+ emsg(_("E5009: Invalid 'runtimepath'"));
}
- return;
}
-
- size_t bufsize = strlen(eap->arg) + sizeof("call health#check('')");
- char *buf = xmalloc(bufsize);
- snprintf(buf, bufsize, "call health#check('%s')", eap->arg);
-
- do_cmdline_cmd(buf);
-
- xfree(buf);
+ semsg_multiline(err.msg);
+ api_clear_error(&err);
}
void invoke_prompt_callback(void)
@@ -8425,7 +8993,7 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic)
const bool type_is = type == EXPR_IS || type == EXPR_ISNOT;
if (type_is && typ1->v_type != typ2->v_type) {
- // For "is" a different type always means false, for "notis"
+ // For "is" a different type always means false, for "isnot"
// it means true.
n1 = type == EXPR_ISNOT;
} else if (typ1->v_type == VAR_BLOB || typ2->v_type == VAR_BLOB) {
@@ -8506,8 +9074,9 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic)
}
if ((typ1->v_type == VAR_PARTIAL && typ1->vval.v_partial == NULL)
|| (typ2->v_type == VAR_PARTIAL && typ2->vval.v_partial == NULL)) {
- // when a partial is NULL assume not equal
- n1 = false;
+ // When both partials are NULL, then they are equal.
+ // Otherwise they are not equal.
+ n1 = (typ1->vval.v_partial == typ2->vval.v_partial);
} else if (type_is) {
if (typ1->v_type == VAR_FUNC && typ2->v_type == VAR_FUNC) {
// strings are considered the same if their value is