aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r--src/nvim/eval.c1706
1 files changed, 707 insertions, 999 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 64883e69a2..8682139b32 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -6,7 +6,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/stat.h>
#include <uv.h>
#include "auto/config.h"
@@ -21,9 +20,9 @@
#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"
+#include "nvim/errors.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
#include "nvim/eval/executor.h"
@@ -33,18 +32,15 @@
#include "nvim/eval/vars.h"
#include "nvim/event/loop.h"
#include "nvim/event/multiqueue.h"
-#include "nvim/event/process.h"
+#include "nvim/event/proc.h"
#include "nvim/event/time.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
-#include "nvim/ex_getln.h"
#include "nvim/garray.h"
#include "nvim/garray_defs.h"
-#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/grid_defs.h"
#include "nvim/hashtab.h"
#include "nvim/highlight_group.h"
#include "nvim/insexpand.h"
@@ -66,14 +62,11 @@
#include "nvim/option.h"
#include "nvim/option_vars.h"
#include "nvim/optionstr.h"
-#include "nvim/os/fileio.h"
#include "nvim/os/fs.h"
-#include "nvim/os/fs_defs.h"
#include "nvim/os/lang.h"
#include "nvim/os/os.h"
#include "nvim/os/os_defs.h"
#include "nvim/os/shell.h"
-#include "nvim/os/stdpaths_defs.h"
#include "nvim/path.h"
#include "nvim/pos_defs.h"
#include "nvim/profile.h"
@@ -82,14 +75,9 @@
#include "nvim/regexp_defs.h"
#include "nvim/runtime.h"
#include "nvim/runtime_defs.h"
-#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
#include "nvim/types_defs.h"
-#include "nvim/ui.h"
-#include "nvim/ui_compositor.h"
-#include "nvim/ui_defs.h"
-#include "nvim/usercmd.h"
#include "nvim/version.h"
#include "nvim/vim_defs.h"
#include "nvim/window.h"
@@ -106,7 +94,6 @@ 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 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[]
@@ -121,6 +108,8 @@ 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 const char e_cannot_use_partial_here[]
+ = N_("E1265: Cannot use a partial here");
static char * const namespace_char = "abglstvw";
@@ -151,6 +140,12 @@ typedef struct {
int fi_byte_idx; // byte index in fi_string
} forinfo_T;
+typedef enum {
+ GLV_FAIL,
+ GLV_OK,
+ GLV_STOP,
+} glv_status_T;
+
// values for vv_flags:
#define VV_COMPAT 1 // compatible, also used without "v:"
#define VV_RO 2 // read-only
@@ -330,7 +325,6 @@ static const char *const msgpack_type_names[] = {
[kMPInteger] = "integer",
[kMPFloat] = "float",
[kMPString] = "string",
- [kMPBinary] = "binary",
[kMPArray] = "array",
[kMPMap] = "map",
[kMPExt] = "ext",
@@ -341,7 +335,6 @@ const list_T *eval_msgpack_type_lists[] = {
[kMPInteger] = NULL,
[kMPFloat] = NULL,
[kMPString] = NULL,
- [kMPBinary] = NULL,
[kMPArray] = NULL,
[kMPMap] = NULL,
[kMPExt] = NULL,
@@ -696,7 +689,7 @@ int eval_charconvert(const char *const enc_from, const char *const enc_to,
}
bool err = false;
- if (eval_to_bool(p_ccv, &err, NULL, false)) {
+ if (eval_to_bool(p_ccv, &err, NULL, false, true)) {
err = true;
}
@@ -725,7 +718,7 @@ void eval_diff(const char *const origfile, const char *const newfile, const char
}
// errors are ignored
- typval_T *tv = eval_expr(p_dex, NULL);
+ typval_T *tv = eval_expr_ext(p_dex, NULL, true);
tv_free(tv);
set_vim_var_string(VV_FNAME_IN, NULL, -1);
@@ -747,7 +740,7 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch
}
// errors are ignored
- typval_T *tv = eval_expr(p_pex, NULL);
+ typval_T *tv = eval_expr_ext(p_pex, NULL, true);
tv_free(tv);
set_vim_var_string(VV_FNAME_IN, NULL, -1);
@@ -776,7 +769,8 @@ void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip)
/// @param skip only parse, don't execute
///
/// @return true or false.
-bool eval_to_bool(char *arg, bool *error, exarg_T *eap, bool skip)
+bool eval_to_bool(char *arg, bool *error, exarg_T *eap, const bool skip,
+ const bool use_simple_function)
{
typval_T tv;
bool retval = false;
@@ -787,7 +781,9 @@ bool eval_to_bool(char *arg, bool *error, exarg_T *eap, bool skip)
if (skip) {
emsg_skip++;
}
- if (eval0(arg, &tv, eap, &evalarg) == FAIL) {
+ int r = use_simple_function ? eval0_simple_funccal(arg, &tv, eap, &evalarg)
+ : eval0(arg, &tv, eap, &evalarg);
+ if (r == FAIL) {
*error = true;
} else {
*error = false;
@@ -840,6 +836,80 @@ 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));
}
+/// Evaluate a partial.
+/// Pass arguments "argv[argc]".
+/// Return the result in "rettv" and OK or FAIL.
+static int eval_expr_partial(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv)
+ FUNC_ATTR_NONNULL_ALL
+{
+ 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_T funcexe = FUNCEXE_INIT;
+ funcexe.fe_evaluate = true;
+ funcexe.fe_partial = partial;
+ if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) {
+ return FAIL;
+ }
+
+ return OK;
+}
+
+/// Evaluate an expression which is a function.
+/// Pass arguments "argv[argc]".
+/// Return the result in "rettv" and OK or FAIL.
+static int eval_expr_func(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv)
+ FUNC_ATTR_NONNULL_ALL
+{
+ char buf[NUMBUFLEN];
+ 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_T funcexe = FUNCEXE_INIT;
+ funcexe.fe_evaluate = true;
+ if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) {
+ return FAIL;
+ }
+
+ return OK;
+}
+
+/// Evaluate an expression, which is a string.
+/// Return the result in "rettv" and OK or FAIL.
+static int eval_expr_string(const typval_T *expr, typval_T *rettv)
+ FUNC_ATTR_NONNULL_ALL
+{
+ 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, NULL) == FAIL) {
+ return FAIL;
+ }
+
+ if (*skipwhite(s) != NUL) { // check for trailing chars after expr
+ tv_clear(rettv);
+ semsg(_(e_invexpr2), s);
+ return FAIL;
+ }
+
+ return OK;
+}
+
/// Evaluate an expression, which can be a function, partial or string.
/// Pass arguments "argv[argc]".
/// Return the result in "rettv" and OK or FAIL.
@@ -849,49 +919,14 @@ int eval_expr_typval(const typval_T *expr, bool want_func, typval_T *argv, int a
typval_T *rettv)
FUNC_ATTR_NONNULL_ALL
{
- char buf[NUMBUFLEN];
- funcexe_T funcexe = FUNCEXE_INIT;
-
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;
- }
+ return eval_expr_partial(expr, argv, argc, rettv);
} 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;
- if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) {
- return FAIL;
- }
+ return eval_expr_func(expr, argv, argc, rettv);
} else {
- char *s = (char *)tv_get_string_buf_chk(expr, buf);
- if (s == NULL) {
- return FAIL;
- }
- s = skipwhite(s);
- if (eval1_emsg(&s, rettv, NULL) == FAIL) {
- return FAIL;
- }
- if (*skipwhite(s) != NUL) { // check for trailing chars after expr
- tv_clear(rettv);
- semsg(_(e_invexpr2), s);
- return FAIL;
- }
+ return eval_expr_string(expr, rettv);
}
+
return OK;
}
@@ -996,14 +1031,17 @@ static char *typval2string(typval_T *tv, bool join_list)
/// @param join_list when true convert a List into a sequence of lines.
///
/// @return pointer to allocated memory, or NULL for failure.
-char *eval_to_string_eap(char *arg, bool join_list, exarg_T *eap)
+char *eval_to_string_eap(char *arg, const bool join_list, exarg_T *eap,
+ const bool use_simple_function)
{
typval_T tv;
char *retval;
evalarg_T evalarg;
fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip);
- if (eval0(arg, &tv, NULL, &evalarg) == FAIL) {
+ int r = use_simple_function ? eval0_simple_funccal(arg, &tv, NULL, &evalarg)
+ : eval0(arg, &tv, NULL, &evalarg);
+ if (r == FAIL) {
retval = NULL;
} else {
retval = typval2string(&tv, join_list);
@@ -1014,16 +1052,16 @@ char *eval_to_string_eap(char *arg, bool join_list, exarg_T *eap)
return retval;
}
-char *eval_to_string(char *arg, bool join_list)
+char *eval_to_string(char *arg, const bool join_list, const bool use_simple_function)
{
- return eval_to_string_eap(arg, join_list, NULL);
+ return eval_to_string_eap(arg, join_list, NULL, use_simple_function);
}
/// Call eval_to_string() without using current local variables and using
/// textlock.
///
/// @param use_sandbox when true, use the sandbox.
-char *eval_to_string_safe(char *arg, const bool use_sandbox)
+char *eval_to_string_safe(char *arg, const bool use_sandbox, const bool use_simple_function)
{
char *retval;
funccal_entry_T funccal_entry;
@@ -1033,7 +1071,7 @@ char *eval_to_string_safe(char *arg, const bool use_sandbox)
sandbox++;
}
textlock++;
- retval = eval_to_string(arg, false);
+ retval = eval_to_string(arg, false, use_simple_function);
if (use_sandbox) {
sandbox--;
}
@@ -1046,15 +1084,22 @@ char *eval_to_string_safe(char *arg, const bool use_sandbox)
/// Evaluates "expr" silently.
///
/// @return -1 for an error.
-varnumber_T eval_to_number(char *expr)
+varnumber_T eval_to_number(char *expr, const bool use_simple_function)
{
typval_T rettv;
varnumber_T retval;
char *p = skipwhite(expr);
+ int r = NOTDONE;
emsg_off++;
- if (eval1(&p, &rettv, &EVALARG_EVALUATE) == FAIL) {
+ if (use_simple_function) {
+ r = may_call_simple_func(expr, &rettv);
+ }
+ if (r == NOTDONE) {
+ r = eval1(&p, &rettv, &EVALARG_EVALUATE);
+ }
+ if (r == FAIL) {
retval = -1;
} else {
retval = tv_get_number_chk(&rettv, NULL);
@@ -1071,12 +1116,26 @@ varnumber_T eval_to_number(char *expr)
/// NULL when there is an error.
typval_T *eval_expr(char *arg, exarg_T *eap)
{
+ return eval_expr_ext(arg, eap, false);
+}
+
+static typval_T *eval_expr_ext(char *arg, exarg_T *eap, const bool use_simple_function)
+{
typval_T *tv = xmalloc(sizeof(*tv));
evalarg_T evalarg;
fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip);
- if (eval0(arg, tv, eap, &evalarg) == FAIL) {
+ int r = NOTDONE;
+
+ if (use_simple_function) {
+ r = eval0_simple_funccal(arg, tv, eap, &evalarg);
+ }
+ if (r == NOTDONE) {
+ r = eval0(arg, tv, eap, &evalarg);
+ }
+
+ if (r == FAIL) {
XFREE_CLEAR(tv);
}
@@ -1162,7 +1221,11 @@ list_T *eval_spell_expr(char *badword, char *expr)
current_sctx = *ctx;
}
- if (eval1(&p, &rettv, &EVALARG_EVALUATE) == OK) {
+ int r = may_call_simple_func(p, &rettv);
+ if (r == NOTDONE) {
+ r = eval1(&p, &rettv, &EVALARG_EVALUATE);
+ }
+ if (r == OK) {
if (rettv.v_type != VAR_LIST) {
tv_clear(&rettv);
} else {
@@ -1303,7 +1366,7 @@ int eval_foldexpr(win_T *wp, int *cp)
const sctx_T saved_sctx = current_sctx;
const bool use_sandbox = was_set_insecurely(wp, kOptFoldexpr, OPT_LOCAL);
- char *arg = wp->w_p_fde;
+ char *arg = skipwhite(wp->w_p_fde);
current_sctx = wp->w_p_script_ctx[WV_FDE].script_ctx;
emsg_off++;
@@ -1315,7 +1378,9 @@ int eval_foldexpr(win_T *wp, int *cp)
typval_T tv;
varnumber_T retval;
- if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
+ // Evaluate the expression. If the expression is "FuncName()" call the
+ // function directly.
+ if (eval0_simple_funccal(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
retval = 0;
} else {
// If the result is a number, just return the number.
@@ -1361,7 +1426,7 @@ Object eval_foldtext(win_T *wp)
typval_T tv;
Object retval;
- if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
+ if (eval0_simple_funccal(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
retval = STRING_OBJ(NULL_STRING);
} else {
if (tv.v_type == VAR_LIST) {
@@ -1382,22 +1447,232 @@ Object eval_foldtext(win_T *wp)
return retval;
}
+/// Find the end of a variable or function name. Unlike find_name_end() this
+/// does not recognize magic braces.
+/// When "use_namespace" is true recognize "b:", "s:", etc.
+/// Return a pointer to just after the name. Equal to "arg" if there is no
+/// valid name.
+static const char *to_name_end(const char *arg, bool use_namespace)
+{
+ // Quick check for valid starting character.
+ if (!eval_isnamec1(*arg)) {
+ return arg;
+ }
+
+ const char *p;
+ for (p = arg + 1; *p != NUL && eval_isnamec(*p); MB_PTR_ADV(p)) {
+ // Include a namespace such as "s:var" and "v:var". But "n:" is not
+ // and can be used in slice "[n:]".
+ if (*p == ':' && (p != arg + 1
+ || !use_namespace
+ || vim_strchr("bgstvw", *arg) == NULL)) {
+ break;
+ }
+ }
+ return p;
+}
+
+/// Get an Dict lval variable that can be assigned a value to: "name",
+/// "name[expr]", "name[expr][expr]", "name.key", "name.key[expr]" etc.
+/// "name" points to the start of the name.
+/// If "rettv" is not NULL it points to the value to be assigned.
+/// "unlet" is true for ":unlet": slightly different behavior when something is
+/// wrong; must end in space or cmd separator.
+///
+/// flags:
+/// GLV_QUIET: do not give error messages
+/// GLV_READ_ONLY: will not change the variable
+/// GLV_NO_AUTOLOAD: do not use script autoloading
+///
+/// The Dict is returned in 'lp'. Returns GLV_OK on success and GLV_FAIL on
+/// failure. Returns GLV_STOP to stop processing the characters following
+/// 'key_end'.
+static glv_status_T get_lval_dict_item(lval_T *lp, char *name, char *key, int len, char **key_end,
+ typval_T *var1, int flags, bool unlet, typval_T *rettv)
+{
+ bool quiet = flags & GLV_QUIET;
+ char *p = *key_end;
+
+ if (len == -1) {
+ // "[key]": get key from "var1"
+ key = (char *)tv_get_string(var1); // is number or string
+ }
+ lp->ll_list = NULL;
+
+ // a NULL dict is equivalent with an empty dict
+ if (lp->ll_tv->vval.v_dict == NULL) {
+ lp->ll_tv->vval.v_dict = tv_dict_alloc();
+ lp->ll_tv->vval.v_dict->dv_refcount++;
+ }
+ lp->ll_dict = lp->ll_tv->vval.v_dict;
+
+ 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;
+ if (len != -1) {
+ prevval = key[len];
+ key[len] = NUL;
+ } else {
+ prevval = 0; // Avoid compiler warning.
+ }
+ 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;
+ }
+ if (wrong) {
+ return GLV_FAIL;
+ }
+ }
+
+ if (lp->ll_di != NULL && tv_is_luafunc(&lp->ll_di->di_tv)
+ && len == -1 && rettv == NULL) {
+ semsg(e_illvar, "v:['lua']");
+ return GLV_FAIL;
+ }
+
+ if (lp->ll_di == NULL) {
+ // Can't add "v:" or "a:" variable.
+ if (lp->ll_dict == &vimvardict
+ || &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) {
+ semsg(_(e_illvar), name);
+ return GLV_FAIL;
+ }
+
+ // Key does not exist in dict: may need to add it.
+ if (*p == '[' || *p == '.' || unlet) {
+ if (!quiet) {
+ semsg(_(e_dictkey), key);
+ }
+ return GLV_FAIL;
+ }
+ if (len == -1) {
+ lp->ll_newkey = xstrdup(key);
+ } else {
+ lp->ll_newkey = xmemdupz(key, (size_t)len);
+ }
+ *key_end = p;
+ return GLV_STOP;
+ // existing variable, need to check if it can be changed
+ } else if (!(flags & GLV_READ_ONLY)
+ && (var_check_ro(lp->ll_di->di_flags, name, (size_t)(p - name))
+ || var_check_lock(lp->ll_di->di_flags, name, (size_t)(p - name)))) {
+ return GLV_FAIL;
+ }
+
+ lp->ll_tv = &lp->ll_di->di_tv;
+
+ return GLV_OK;
+}
+
+/// Get an blob lval variable that can be assigned a value to: "name",
+/// "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]", etc.
+///
+/// 'var1' specifies the starting blob index and 'var2' specifies the ending
+/// blob index. If the first index is not specified in a range, then 'empty1'
+/// is true. If 'quiet' is true, then error messages are not displayed for
+/// invalid indexes.
+///
+/// The blob is returned in 'lp'. Returns OK on success and FAIL on failure.
+static int get_lval_blob(lval_T *lp, typval_T *var1, typval_T *var2, bool empty1, bool quiet)
+{
+ const int bloblen = tv_blob_len(lp->ll_tv->vval.v_blob);
+
+ // Get the number and item for the only or first index of the List.
+ if (empty1) {
+ lp->ll_n1 = 0;
+ } else {
+ // Is number or string.
+ lp->ll_n1 = (int)tv_get_number(var1);
+ }
+
+ if (tv_blob_check_index(bloblen, lp->ll_n1, quiet) == FAIL) {
+ return FAIL;
+ }
+ if (lp->ll_range && !lp->ll_empty2) {
+ lp->ll_n2 = (int)tv_get_number(var2);
+ if (tv_blob_check_range(bloblen, lp->ll_n1, lp->ll_n2, quiet) == FAIL) {
+ return FAIL;
+ }
+ }
+
+ lp->ll_blob = lp->ll_tv->vval.v_blob;
+ lp->ll_tv = NULL;
+
+ return OK;
+}
+
+/// Get a List lval variable that can be assigned a value to: "name",
+/// "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]", etc.
+///
+/// 'var1' specifies the starting List index and 'var2' specifies the ending
+/// List index. If the first index is not specified in a range, then 'empty1'
+/// is true. If 'quiet' is true, then error messages are not displayed for
+/// invalid indexes.
+///
+/// The List is returned in 'lp'. Returns OK on success and FAIL on failure.
+static int get_lval_list(lval_T *lp, typval_T *var1, typval_T *var2, bool empty1, int flags,
+ bool quiet)
+{
+ // Get the number and item for the only or first index of the List.
+ if (empty1) {
+ lp->ll_n1 = 0;
+ } else {
+ // Is number or string.
+ lp->ll_n1 = (int)tv_get_number(var1);
+ }
+
+ lp->ll_dict = NULL;
+ lp->ll_list = lp->ll_tv->vval.v_list;
+ lp->ll_li = tv_list_check_range_index_one(lp->ll_list, &lp->ll_n1, quiet);
+ if (lp->ll_li == NULL) {
+ return FAIL;
+ }
+
+ // May need to find the item or absolute index for the second
+ // index of a range.
+ // 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 = (int)tv_get_number(var2); // Is number or string.
+ if (tv_list_check_range_index_two(lp->ll_list,
+ &lp->ll_n1, lp->ll_li,
+ &lp->ll_n2, quiet) == FAIL) {
+ return FAIL;
+ }
+ }
+
+ lp->ll_tv = TV_LIST_ITEM_TV(lp->ll_li);
+
+ return OK;
+}
+
/// Get the lval of a list/dict/blob subitem starting at "p". Loop
/// until no more [idx] or .key is following.
///
+/// If "rettv" is not NULL it points to the value to be assigned.
+/// "unlet" is true for ":unlet".
+///
/// @param[in] flags @see GetLvalFlags.
///
/// @return A pointer to the character after the subscript on success or NULL on
/// failure.
static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv, hashtab_T *ht,
- dictitem_T *v, int unlet, int flags)
+ dictitem_T *v, bool unlet, int flags)
{
- int quiet = flags & GLV_QUIET;
+ bool quiet = flags & GLV_QUIET;
typval_T var1;
var1.v_type = VAR_UNKNOWN;
typval_T var2;
var2.v_type = VAR_UNKNOWN;
bool empty1 = false;
+ int rc = FAIL;
// Loop until no more [idx] or .key is following.
while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.')) {
@@ -1427,7 +1702,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv
if (!quiet) {
emsg(_("E708: [:] must come last"));
}
- return NULL;
+ goto done;
}
int len = -1;
@@ -1451,12 +1726,11 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv
} else {
empty1 = false;
if (eval1(&p, &var1, &EVALARG_EVALUATE) == FAIL) { // Recursive!
- return NULL;
+ goto done;
}
if (!tv_check_str(&var1)) {
// Not a number or string.
- tv_clear(&var1);
- return NULL;
+ goto done;
}
p = skipwhite(p);
}
@@ -1467,8 +1741,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv
if (!quiet) {
emsg(_(e_cannot_slice_dictionary));
}
- tv_clear(&var1);
- return NULL;
+ goto done;
}
if (rettv != NULL
&& !(rettv->v_type == VAR_LIST && rettv->vval.v_list != NULL)
@@ -1476,8 +1749,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv
if (!quiet) {
emsg(_("E709: [:] requires a List or Blob value"));
}
- tv_clear(&var1);
- return NULL;
+ goto done;
}
p = skipwhite(p + 1);
if (*p == ']') {
@@ -1486,14 +1758,11 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv
lp->ll_empty2 = false;
// Recursive!
if (eval1(&p, &var2, &EVALARG_EVALUATE) == FAIL) {
- tv_clear(&var1);
- return NULL;
+ goto done;
}
if (!tv_check_str(&var2)) {
// Not a number or string.
- tv_clear(&var1);
- tv_clear(&var2);
- return NULL;
+ goto done;
}
}
lp->ll_range = true;
@@ -1505,9 +1774,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv
if (!quiet) {
emsg(_(e_missbrac));
}
- tv_clear(&var1);
- tv_clear(&var2);
- return NULL;
+ goto done;
}
// Skip to past ']'.
@@ -1515,142 +1782,37 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv
}
if (lp->ll_tv->v_type == VAR_DICT) {
- if (len == -1) {
- // "[key]": get key from "var1"
- key = (char *)tv_get_string(&var1); // is number or string
- }
- lp->ll_list = NULL;
- lp->ll_dict = lp->ll_tv->vval.v_dict;
- 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;
- if (len != -1) {
- prevval = key[len];
- key[len] = NUL;
- } else {
- prevval = 0; // Avoid compiler warning.
- }
- 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;
- }
- if (wrong) {
- tv_clear(&var1);
- return NULL;
- }
- }
-
- if (lp->ll_di != NULL && tv_is_luafunc(&lp->ll_di->di_tv)
- && len == -1 && rettv == NULL) {
- tv_clear(&var1);
- semsg(e_illvar, "v:['lua']");
- return NULL;
+ glv_status_T glv_status = get_lval_dict_item(lp, name, key, len, &p, &var1,
+ flags, unlet, rettv);
+ if (glv_status == GLV_FAIL) {
+ goto done;
}
-
- if (lp->ll_di == NULL) {
- // Can't add "v:" or "a:" variable.
- if (lp->ll_dict == &vimvardict
- || &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) {
- semsg(_(e_illvar), name);
- tv_clear(&var1);
- return NULL;
- }
-
- // Key does not exist in dict: may need to add it.
- if (*p == '[' || *p == '.' || unlet) {
- if (!quiet) {
- semsg(_(e_dictkey), key);
- }
- tv_clear(&var1);
- return NULL;
- }
- if (len == -1) {
- lp->ll_newkey = xstrdup(key);
- } else {
- lp->ll_newkey = xmemdupz(key, (size_t)len);
- }
- tv_clear(&var1);
+ if (glv_status == GLV_STOP) {
break;
- // existing variable, need to check if it can be changed
- } else if (!(flags & GLV_READ_ONLY)
- && (var_check_ro(lp->ll_di->di_flags, name, (size_t)(p - name))
- || var_check_lock(lp->ll_di->di_flags, name, (size_t)(p - name)))) {
- tv_clear(&var1);
- return NULL;
}
-
- tv_clear(&var1);
- lp->ll_tv = &lp->ll_di->di_tv;
} else if (lp->ll_tv->v_type == VAR_BLOB) {
- // Get the number and item for the only or first index of the List.
- if (empty1) {
- lp->ll_n1 = 0;
- } else {
- // Is number or string.
- lp->ll_n1 = (int)tv_get_number(&var1);
+ if (get_lval_blob(lp, &var1, &var2, empty1, quiet) == FAIL) {
+ goto done;
}
- tv_clear(&var1);
-
- const int bloblen = tv_blob_len(lp->ll_tv->vval.v_blob);
- 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 = (int)tv_get_number(&var2);
- tv_clear(&var2);
- if (tv_blob_check_range(bloblen, lp->ll_n1, lp->ll_n2, quiet) == FAIL) {
- return NULL;
- }
- }
- lp->ll_blob = lp->ll_tv->vval.v_blob;
- lp->ll_tv = NULL;
break;
} else {
- // Get the number and item for the only or first index of the List.
- if (empty1) {
- lp->ll_n1 = 0;
- } else {
- // Is number or string.
- 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_check_range_index_one(lp->ll_list, &lp->ll_n1, quiet);
- if (lp->ll_li == NULL) {
- tv_clear(&var2);
- return NULL;
- }
-
- // May need to find the item or absolute index for the second
- // index of a range.
- // 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 = (int)tv_get_number(&var2); // Is number or string.
- tv_clear(&var2);
- if (tv_list_check_range_index_two(lp->ll_list,
- &lp->ll_n1, lp->ll_li,
- &lp->ll_n2, quiet) == FAIL) {
- return NULL;
- }
+ if (get_lval_list(lp, &var1, &var2, empty1, flags, quiet) == FAIL) {
+ goto done;
}
-
- lp->ll_tv = TV_LIST_ITEM_TV(lp->ll_li);
}
+
+ tv_clear(&var1);
+ tv_clear(&var2);
+ var1.v_type = VAR_UNKNOWN;
+ var2.v_type = VAR_UNKNOWN;
}
+ rc = OK;
+
+done:
tv_clear(&var1);
- return p;
+ tv_clear(&var2);
+ return rc == OK ? p : NULL;
}
/// Get an lvalue
@@ -2381,9 +2543,10 @@ void clear_evalarg(evalarg_T *evalarg, exarg_T *eap)
}
}
-/// 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.
+/// The "eval" functions have an "evalarg" argument: When NULL or
+/// "evalarg->eval_flags" does not have EVAL_EVALUATE, then the argument is only
+/// parsed but not executed. The functions may return OK, but the rettv will be
+/// of type VAR_UNKNOWN. The functions still returns FAIL for a syntax error.
/// Handle zero level expression.
/// This calls eval1() and handles error message and nextcmd.
@@ -2442,6 +2605,42 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
return ret;
}
+/// If "arg" is a simple function call without arguments then call it and return
+/// the result. Otherwise return NOTDONE.
+static int may_call_simple_func(const char *arg, typval_T *rettv)
+{
+ const char *parens = strstr(arg, "()");
+ int r = NOTDONE;
+
+ // If the expression is "FuncName()" then we can skip a lot of overhead.
+ if (parens != NULL && *skipwhite(parens + 2) == NUL) {
+ if (strnequal(arg, "v:lua.", 6)) {
+ const char *p = arg + 6;
+ if (p != parens && skip_luafunc_name(p) == parens) {
+ r = call_simple_luafunc(p, (size_t)(parens - p), rettv);
+ }
+ } else {
+ const char *p = strncmp(arg, "<SNR>", 5) == 0 ? skipdigits(arg + 5) : arg;
+ if (to_name_end(p, true) == parens) {
+ r = call_simple_func(arg, (size_t)(parens - arg), rettv);
+ }
+ }
+ }
+ return r;
+}
+
+/// Handle zero level expression with optimization for a simple function call.
+/// Same arguments and return value as eval0().
+static int eval0_simple_funccal(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
+{
+ int r = may_call_simple_func(arg, rettv);
+
+ if (r == NOTDONE) {
+ r = eval0(arg, rettv, eap, evalarg);
+ }
+ return r;
+}
+
/// Handle top level expression:
/// expr2 ? expr1 : expr1
/// expr2 ?? expr1
@@ -2832,6 +3031,93 @@ static int eval_addlist(typval_T *tv1, typval_T *tv2)
return OK;
}
+/// Concatenate strings "tv1" and "tv2" and store the result in "tv1".
+static int eval_concat_str(typval_T *tv1, typval_T *tv2)
+{
+ char buf1[NUMBUFLEN];
+ char buf2[NUMBUFLEN];
+ // s1 already checked
+ const char *const s1 = tv_get_string_buf(tv1, buf1);
+ const char *const s2 = tv_get_string_buf_chk(tv2, buf2);
+ if (s2 == NULL) { // Type error?
+ tv_clear(tv1);
+ tv_clear(tv2);
+ return FAIL;
+ }
+
+ char *p = concat_str(s1, s2);
+ tv_clear(tv1);
+ tv1->v_type = VAR_STRING;
+ tv1->vval.v_string = p;
+
+ return OK;
+}
+
+/// Add or subtract numbers "tv1" and "tv2" and store the result in "tv1".
+/// The numbers can be whole numbers or floats.
+static int eval_addsub_number(typval_T *tv1, typval_T *tv2, int op)
+{
+ bool error = false;
+ varnumber_T n1, n2;
+ float_T f1 = 0;
+ float_T f2 = 0;
+
+ if (tv1->v_type == VAR_FLOAT) {
+ f1 = tv1->vval.v_float;
+ n1 = 0;
+ } else {
+ n1 = tv_get_number_chk(tv1, &error);
+ if (error) {
+ // This can only happen for "list + non-list" or
+ // "blob + non-blob". For "non-list + ..." or
+ // "something - ...", we returned before evaluating the
+ // 2nd operand.
+ tv_clear(tv1);
+ tv_clear(tv2);
+ return FAIL;
+ }
+ if (tv2->v_type == VAR_FLOAT) {
+ f1 = (float_T)n1;
+ }
+ }
+ if (tv2->v_type == VAR_FLOAT) {
+ f2 = tv2->vval.v_float;
+ n2 = 0;
+ } else {
+ n2 = tv_get_number_chk(tv2, &error);
+ if (error) {
+ tv_clear(tv1);
+ tv_clear(tv2);
+ return FAIL;
+ }
+ if (tv1->v_type == VAR_FLOAT) {
+ f2 = (float_T)n2;
+ }
+ }
+ tv_clear(tv1);
+
+ // If there is a float on either side the result is a float.
+ if (tv1->v_type == VAR_FLOAT || tv2->v_type == VAR_FLOAT) {
+ if (op == '+') {
+ f1 = f1 + f2;
+ } else {
+ f1 = f1 - f2;
+ }
+ tv1->v_type = VAR_FLOAT;
+ tv1->vval.v_float = f1;
+ } else {
+ if (op == '+') {
+ n1 = n1 + n2;
+ } else {
+ n1 = n1 - n2;
+ }
+ tv1->v_type = VAR_NUMBER;
+ tv1->vval.v_number = n1;
+ }
+
+ return OK;
+}
+
/// Handle fourth level expression:
/// + number addition, concatenation of list or blob
/// - number subtraction
@@ -2887,20 +3173,9 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg)
if (evaluate) {
// Compute the result.
if (op == '.') {
- char buf1[NUMBUFLEN];
- char buf2[NUMBUFLEN];
- // s1 already checked
- const char *const s1 = tv_get_string_buf(rettv, buf1);
- const char *const s2 = tv_get_string_buf_chk(&var2, buf2);
- if (s2 == NULL) { // Type error?
- tv_clear(rettv);
- tv_clear(&var2);
+ if (eval_concat_str(rettv, &var2) == FAIL) {
return FAIL;
}
- 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) {
eval_addblob(rettv, &var2);
} else if (op == '+' && rettv->v_type == VAR_LIST && var2.v_type == VAR_LIST) {
@@ -2908,62 +3183,8 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg)
return FAIL;
}
} else {
- bool error = false;
- varnumber_T n1, n2;
- float_T f1 = 0;
- float_T f2 = 0;
-
- if (rettv->v_type == VAR_FLOAT) {
- f1 = rettv->vval.v_float;
- n1 = 0;
- } else {
- n1 = tv_get_number_chk(rettv, &error);
- if (error) {
- // This can only happen for "list + non-list" or
- // "blob + non-blob". For "non-list + ..." or
- // "something - ...", we returned before evaluating the
- // 2nd operand.
- tv_clear(rettv);
- tv_clear(&var2);
- return FAIL;
- }
- if (var2.v_type == VAR_FLOAT) {
- f1 = (float_T)n1;
- }
- }
- if (var2.v_type == VAR_FLOAT) {
- f2 = var2.vval.v_float;
- n2 = 0;
- } else {
- n2 = tv_get_number_chk(&var2, &error);
- if (error) {
- tv_clear(rettv);
- tv_clear(&var2);
- return FAIL;
- }
- if (rettv->v_type == VAR_FLOAT) {
- f2 = (float_T)n2;
- }
- }
- tv_clear(rettv);
-
- // If there is a float on either side the result is a float.
- if (rettv->v_type == VAR_FLOAT || var2.v_type == VAR_FLOAT) {
- if (op == '+') {
- f1 = f1 + f2;
- } else {
- f1 = f1 - f2;
- }
- rettv->v_type = VAR_FLOAT;
- rettv->vval.v_float = f1;
- } else {
- if (op == '+') {
- n1 = n1 + n2;
- } else {
- n1 = n1 - n2;
- }
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = n1;
+ if (eval_addsub_number(rettv, &var2, op) == FAIL) {
+ return FAIL;
}
}
tv_clear(&var2);
@@ -2972,6 +3193,85 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg)
return OK;
}
+/// Multiply or divide or compute the modulo of numbers "tv1" and "tv2" and
+/// store the result in "tv1". The numbers can be whole numbers or floats.
+static int eval_multdiv_number(typval_T *tv1, typval_T *tv2, int op)
+ FUNC_ATTR_NO_SANITIZE_UNDEFINED
+{
+ varnumber_T n1, n2;
+ bool use_float = false;
+
+ float_T f1 = 0;
+ float_T f2 = 0;
+ bool error = false;
+ if (tv1->v_type == VAR_FLOAT) {
+ f1 = tv1->vval.v_float;
+ use_float = true;
+ n1 = 0;
+ } else {
+ n1 = tv_get_number_chk(tv1, &error);
+ }
+ tv_clear(tv1);
+ if (error) {
+ tv_clear(tv2);
+ return FAIL;
+ }
+
+ if (tv2->v_type == VAR_FLOAT) {
+ if (!use_float) {
+ f1 = (float_T)n1;
+ use_float = true;
+ }
+ f2 = tv2->vval.v_float;
+ n2 = 0;
+ } else {
+ n2 = tv_get_number_chk(tv2, &error);
+ tv_clear(tv2);
+ if (error) {
+ return FAIL;
+ }
+ if (use_float) {
+ f2 = (float_T)n2;
+ }
+ }
+
+ // Compute the result.
+ // When either side is a float the result is a float.
+ if (use_float) {
+ if (op == '*') {
+ f1 = f1 * f2;
+ } else if (op == '/') {
+ // uncrustify:off
+
+ // Division by zero triggers error from AddressSanitizer
+ f1 = (f2 == 0 ? (
+#ifdef NAN
+ f1 == 0 ? (float_T)NAN :
+#endif
+ (f1 > 0 ? (float_T)INFINITY : (float_T)-INFINITY)) : f1 / f2);
+
+ // uncrustify:on
+ } else {
+ emsg(_("E804: Cannot use '%' with Float"));
+ return FAIL;
+ }
+ tv1->v_type = VAR_FLOAT;
+ tv1->vval.v_float = f1;
+ } else {
+ if (op == '*') {
+ n1 = n1 * n2;
+ } else if (op == '/') {
+ n1 = num_divide(n1, n2);
+ } else {
+ n1 = num_modulus(n1, n2);
+ }
+ tv1->v_type = VAR_NUMBER;
+ tv1->vval.v_number = n1;
+ }
+
+ return OK;
+}
+
/// Handle fifth level expression:
/// - * number multiplication
/// - / number division
@@ -2985,10 +3285,7 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg)
/// float
/// @return OK or FAIL.
static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool want_string)
- FUNC_ATTR_NO_SANITIZE_UNDEFINED
{
- bool use_float = false;
-
// Get the first variable.
if (eval7(arg, rettv, evalarg, want_string) == FAIL) {
return FAIL;
@@ -3001,26 +3298,7 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
break;
}
- varnumber_T n1, n2;
- float_T f1 = 0;
- float_T 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;
- use_float = true;
- n1 = 0;
- } else {
- n1 = tv_get_number_chk(rettv, &error);
- }
- tv_clear(rettv);
- if (error) {
- return FAIL;
- }
- } else {
- n1 = 0;
- }
// Get the second variable.
*arg = skipwhite(*arg + 1);
@@ -3030,56 +3308,9 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
}
if (evaluate) {
- if (var2.v_type == VAR_FLOAT) {
- if (!use_float) {
- f1 = (float_T)n1;
- use_float = true;
- }
- f2 = var2.vval.v_float;
- n2 = 0;
- } else {
- n2 = tv_get_number_chk(&var2, &error);
- tv_clear(&var2);
- if (error) {
- return FAIL;
- }
- if (use_float) {
- f2 = (float_T)n2;
- }
- }
-
// Compute the result.
- // When either side is a float the result is a float.
- if (use_float) {
- if (op == '*') {
- f1 = f1 * f2;
- } else if (op == '/') {
- // uncrustify:off
-
- // Division by zero triggers error from AddressSanitizer
- f1 = (f2 == 0 ? (
-#ifdef NAN
- f1 == 0 ? (float_T)NAN :
-#endif
- (f1 > 0 ? (float_T)INFINITY : (float_T)-INFINITY)) : f1 / f2);
-
- // uncrustify:on
- } else {
- emsg(_("E804: Cannot use '%' with Float"));
- return FAIL;
- }
- rettv->v_type = VAR_FLOAT;
- rettv->vval.v_float = f1;
- } else {
- if (op == '*') {
- n1 = n1 * n2;
- } else if (op == '/') {
- n1 = num_divide(n1, n2);
- } else {
- n1 = num_modulus(n1, n2);
- }
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = n1;
+ if (eval_multdiv_number(rettv, &var2, op) == FAIL) {
+ return FAIL;
}
}
}
@@ -3183,14 +3414,9 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
ret = eval_list(arg, rettv, evalarg);
break;
- // Dictionary: #{key: val, key: val}
+ // Literal Dictionary: #{key: val, key: val}
case '#':
- if ((*arg)[1] == '{') {
- (*arg)++;
- ret = eval_dict(arg, rettv, evalarg, true);
- } else {
- ret = NOTDONE;
- }
+ ret = eval_lit_dict(arg, rettv, evalarg);
break;
// Lambda: {arg, arg -> expr}
@@ -3480,20 +3706,22 @@ static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const
int len;
char *name = *arg;
char *lua_funcname = NULL;
+ char *alias = NULL;
if (strnequal(name, "v:lua.", 6)) {
lua_funcname = name + 6;
*arg = (char *)skip_luafunc_name(lua_funcname);
*arg = skipwhite(*arg); // to detect trailing whitespace later
len = (int)(*arg - lua_funcname);
} else {
- char *alias;
len = get_name_len((const char **)arg, &alias, evaluate, true);
if (alias != NULL) {
name = alias;
}
}
- int ret;
+ char *tofree = NULL;
+ int ret = OK;
+
if (len <= 0) {
if (verbose) {
if (lua_funcname == NULL) {
@@ -3504,25 +3732,79 @@ static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const
}
ret = FAIL;
} else {
- if (**arg != '(') {
- if (verbose) {
- semsg(_(e_missingparen), name);
- }
- ret = FAIL;
- } else if (ascii_iswhite((*arg)[-1])) {
- if (verbose) {
- emsg(_(e_nowhitespace));
+ *arg = skipwhite(*arg);
+
+ // If there is no "(" immediately following, but there is further on,
+ // it can be "dict.Func()", "list[nr]", etc.
+ // Does not handle anything where "(" is part of the expression.
+ char *paren;
+ if (**arg != '(' && lua_funcname == NULL && alias == NULL
+ && (paren = vim_strchr(*arg, '(')) != NULL) {
+ *arg = name;
+ *paren = NUL;
+ typval_T ref;
+ ref.v_type = VAR_UNKNOWN;
+ if (eval7(arg, &ref, evalarg, false) == FAIL) {
+ *arg = name + len;
+ ret = FAIL;
+ } else if (*skipwhite(*arg) != NUL) {
+ if (verbose) {
+ semsg(_(e_trailing_arg), *arg);
+ }
+ ret = FAIL;
+ } else if (ref.v_type == VAR_FUNC && ref.vval.v_string != NULL) {
+ name = ref.vval.v_string;
+ ref.vval.v_string = NULL;
+ tofree = name;
+ len = (int)strlen(name);
+ } else if (ref.v_type == VAR_PARTIAL && ref.vval.v_partial != NULL) {
+ if (ref.vval.v_partial->pt_argc > 0 || ref.vval.v_partial->pt_dict != NULL) {
+ if (verbose) {
+ emsg(_(e_cannot_use_partial_here));
+ }
+ ret = FAIL;
+ } else {
+ name = xstrdup(partial_name(ref.vval.v_partial));
+ tofree = name;
+ if (name == NULL) {
+ ret = FAIL;
+ name = *arg;
+ } else {
+ len = (int)strlen(name);
+ }
+ }
+ } else {
+ if (verbose) {
+ semsg(_(e_not_callable_type_str), name);
+ }
+ ret = FAIL;
}
- ret = FAIL;
- } else if (lua_funcname != NULL) {
- if (evaluate) {
- rettv->v_type = VAR_PARTIAL;
- rettv->vval.v_partial = vvlua_partial;
- rettv->vval.v_partial->pt_refcount++;
+ tv_clear(&ref);
+ *paren = '(';
+ }
+
+ if (ret == OK) {
+ if (**arg != '(') {
+ if (verbose) {
+ semsg(_(e_missingparen), name);
+ }
+ ret = FAIL;
+ } else if (ascii_iswhite((*arg)[-1])) {
+ if (verbose) {
+ emsg(_(e_nowhitespace));
+ }
+ ret = FAIL;
+ } else if (lua_funcname != NULL) {
+ if (evaluate) {
+ rettv->v_type = VAR_PARTIAL;
+ rettv->vval.v_partial = vvlua_partial;
+ rettv->vval.v_partial->pt_refcount++;
+ }
+ ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base, lua_funcname);
+ } else {
+ ret = eval_func(arg, evalarg, name, len, rettv,
+ evaluate ? EVAL_EVALUATE : 0, &base);
}
- ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base, lua_funcname);
- } else {
- ret = eval_func(arg, evalarg, name, len, rettv, evaluate ? EVAL_EVALUATE : 0, &base);
}
}
@@ -3531,6 +3813,11 @@ static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const
if (evaluate) {
tv_clear(&base);
}
+ xfree(tofree);
+
+ if (alias != NULL) {
+ xfree(alias);
+ }
return ret;
}
@@ -3669,7 +3956,7 @@ static int check_can_index(typval_T *rettv, bool evaluate, bool verbose)
/// slice() function
void f_slice(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- if (check_can_index(argvars, true, false) != OK) {
+ if (check_can_index(&argvars[0], true, false) != OK) {
return;
}
@@ -4386,7 +4673,7 @@ bool func_equal(typval_T *tv1, typval_T *tv2, bool ic)
if (d1 != d2) {
return false;
}
- } else if (!tv_dict_equal(d1, d2, ic, true)) {
+ } else if (!tv_dict_equal(d1, d2, ic)) {
return false;
}
@@ -4398,7 +4685,7 @@ bool func_equal(typval_T *tv1, typval_T *tv2, bool ic)
}
for (int i = 0; i < a1; i++) {
if (!tv_equal(tv1->vval.v_partial->pt_argv + i,
- tv2->vval.v_partial->pt_argv + i, ic, true)) {
+ tv2->vval.v_partial->pt_argv + i, ic)) {
return false;
}
}
@@ -4493,19 +4780,6 @@ bool garbage_collect(bool testing)
FOR_ALL_BUFFERS(buf) {
// buffer-local variables
ABORTING(set_ref_in_item)(&buf->b_bufvar.di_tv, copyID, NULL, NULL);
- // buffer marks (ShaDa additional data)
- ABORTING(set_ref_in_fmark)(buf->b_last_cursor, copyID);
- ABORTING(set_ref_in_fmark)(buf->b_last_insert, copyID);
- ABORTING(set_ref_in_fmark)(buf->b_last_change, copyID);
- for (size_t i = 0; i < NMARKS; i++) {
- ABORTING(set_ref_in_fmark)(buf->b_namedm[i], copyID);
- }
- // buffer change list (ShaDa additional data)
- for (int i = 0; i < buf->b_changelistlen; i++) {
- ABORTING(set_ref_in_fmark)(buf->b_changelist[i], copyID);
- }
- // buffer ShaDa additional data
- ABORTING(set_ref_dict)(buf->additional_data, copyID);
// buffer callback functions
ABORTING(set_ref_in_callback)(&buf->b_prompt_callback, copyID, NULL, NULL);
@@ -4528,10 +4802,6 @@ bool garbage_collect(bool testing)
FOR_ALL_TAB_WINDOWS(tp, wp) {
// window-local variables
ABORTING(set_ref_in_item)(&wp->w_winvar.di_tv, copyID, NULL, NULL);
- // window jump list (ShaDa additional data)
- for (int i = 0; i < wp->w_jumplistlen; i++) {
- ABORTING(set_ref_in_fmark)(wp->w_jumplist[i].fmark, copyID);
- }
}
// window-local variables in autocmd windows
for (int i = 0; i < AUCMD_WIN_COUNT; i++) {
@@ -4548,9 +4818,6 @@ bool garbage_collect(bool testing)
char name = NUL;
bool is_unnamed = false;
reg_iter = op_global_reg_iter(reg_iter, &name, &reg, &is_unnamed);
- if (name != NUL) {
- ABORTING(set_ref_dict)(reg.additional_data, copyID);
- }
} while (reg_iter != ITER_REGISTER_NULL);
}
@@ -4561,9 +4828,6 @@ bool garbage_collect(bool testing)
xfmark_T fm;
char name = NUL;
mark_iter = mark_global_iter(mark_iter, &name, &fm);
- if (name != NUL) {
- ABORTING(set_ref_dict)(fm.fmark.additional_data, copyID);
- }
} while (mark_iter != NULL);
}
@@ -4605,36 +4869,6 @@ bool garbage_collect(bool testing)
// v: vars
ABORTING(set_ref_in_ht)(&vimvarht, copyID, NULL);
- // history items (ShaDa additional elements)
- if (p_hi) {
- for (int i = 0; i < HIST_COUNT; i++) {
- const void *iter = NULL;
- do {
- histentry_T hist;
- iter = hist_iter(iter, (uint8_t)i, false, &hist);
- if (hist.hisstr != NULL) {
- ABORTING(set_ref_list)(hist.additional_elements, copyID);
- }
- } while (iter != NULL);
- }
- }
-
- // previously used search/substitute patterns (ShaDa additional data)
- {
- SearchPattern pat;
- get_search_pattern(&pat);
- ABORTING(set_ref_dict)(pat.additional_data, copyID);
- get_substitute_pattern(&pat);
- ABORTING(set_ref_dict)(pat.additional_data, copyID);
- }
-
- // previously used replacement string
- {
- SubReplacementString sub;
- sub_get_replacement(&sub);
- ABORTING(set_ref_list)(sub.additional_elements, copyID);
- }
-
ABORTING(set_ref_in_quickfix)(copyID);
bool did_free = false;
@@ -4916,52 +5150,6 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack
return abort;
}
-/// Mark all lists and dicts referenced in given mark
-///
-/// @return true if setting references failed somehow.
-static inline bool set_ref_in_fmark(fmark_T fm, int copyID)
- FUNC_ATTR_WARN_UNUSED_RESULT
-{
- if (fm.additional_data != NULL
- && fm.additional_data->dv_copyID != copyID) {
- fm.additional_data->dv_copyID = copyID;
- return set_ref_in_ht(&fm.additional_data->dv_hashtab, copyID, NULL);
- }
- return false;
-}
-
-/// Mark all lists and dicts referenced in given list and the list itself
-///
-/// @return true if setting references failed somehow.
-static inline bool set_ref_list(list_T *list, int copyID)
- FUNC_ATTR_WARN_UNUSED_RESULT
-{
- if (list != NULL) {
- typval_T tv = (typval_T) {
- .v_type = VAR_LIST,
- .vval = { .v_list = list }
- };
- return set_ref_in_item(&tv, copyID, NULL, NULL);
- }
- return false;
-}
-
-/// Mark all lists and dicts referenced in given dict and the dict itself
-///
-/// @return true if setting references failed somehow.
-static inline bool set_ref_dict(dict_T *dict, int copyID)
- FUNC_ATTR_WARN_UNUSED_RESULT
-{
- if (dict != NULL) {
- typval_T tv = (typval_T) {
- .v_type = VAR_DICT,
- .vval = { .v_dict = dict }
- };
- return set_ref_in_item(&tv, copyID, NULL, NULL);
- }
- return false;
-}
-
/// Get the key for #{key: val} into "tv" and advance "arg".
///
/// @return FAIL when there is no valid key.
@@ -5093,6 +5281,24 @@ failret:
return OK;
}
+/// Evaluate a literal dictionary: #{key: val, key: val}
+/// "*arg" points to the "#".
+/// On return, "*arg" points to the character after the Dict.
+/// Return OK or FAIL. Returns NOTDONE for {expr}.
+static int eval_lit_dict(char **arg, typval_T *rettv, evalarg_T *const evalarg)
+{
+ int ret = OK;
+
+ if ((*arg)[1] == '{') {
+ (*arg)++;
+ ret = eval_dict(arg, rettv, evalarg, true);
+ } else {
+ ret = NOTDONE;
+ }
+
+ return ret;
+}
+
/// Convert the string to a floating point number
///
/// This uses strtod(). setlocale(LC_NUMERIC, "C") has been used earlier to
@@ -5569,317 +5775,6 @@ void f_foreach(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
filter_map(argvars, rettv, FILTERMAP_FOREACH);
}
-/// "function()" function
-/// "funcref()" function
-void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref)
-{
- char *s;
- char *name;
- bool use_string = false;
- partial_T *arg_pt = NULL;
- char *trans_name = NULL;
-
- if (argvars[0].v_type == VAR_FUNC) {
- // function(MyFunc, [arg], dict)
- s = argvars[0].vval.v_string;
- } else if (argvars[0].v_type == VAR_PARTIAL
- && argvars[0].vval.v_partial != NULL) {
- // function(dict.MyFunc, [arg])
- arg_pt = argvars[0].vval.v_partial;
- s = partial_name(arg_pt);
- // TODO(bfredl): do the entire nlua_is_table_from_lua dance
- } else {
- // function('MyFunc', [arg], dict)
- s = (char *)tv_get_string(&argvars[0]);
- use_string = true;
- }
-
- if ((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) || is_funcref) {
- name = s;
- trans_name = save_function_name(&name, false,
- TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL);
- if (*name != NUL) {
- s = NULL;
- }
- }
- 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]) : 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(trans_name))) {
- semsg(_("E700: Unknown function: %s"), s);
- } else {
- int dict_idx = 0;
- int arg_idx = 0;
- list_T *list = NULL;
- if (strncmp(s, "s:", 2) == 0 || strncmp(s, "<SID>", 5) == 0) {
- // Expand s: and <SID> into <SNR>nr_, so that the function can
- // also be called from another script. Using trans_function_name()
- // would also work, but some plugins depend on the name being
- // printable text.
- name = get_scriptlocal_funcname(s);
- } else {
- name = xstrdup(s);
- }
-
- if (argvars[1].v_type != VAR_UNKNOWN) {
- if (argvars[2].v_type != VAR_UNKNOWN) {
- // function(name, [args], dict)
- arg_idx = 1;
- dict_idx = 2;
- } else if (argvars[1].v_type == VAR_DICT) {
- // function(name, dict)
- dict_idx = 1;
- } else {
- // function(name, [args])
- arg_idx = 1;
- }
- if (dict_idx > 0) {
- if (tv_check_for_dict_arg(argvars, dict_idx) == FAIL) {
- xfree(name);
- goto theend;
- }
- if (argvars[dict_idx].vval.v_dict == NULL) {
- dict_idx = 0;
- }
- }
- if (arg_idx > 0) {
- if (argvars[arg_idx].v_type != VAR_LIST) {
- emsg(_("E923: Second argument of function() must be "
- "a list or a dict"));
- xfree(name);
- goto theend;
- }
- list = argvars[arg_idx].vval.v_list;
- if (tv_list_len(list) == 0) {
- arg_idx = 0;
- } else if (tv_list_len(list) > MAX_FUNC_ARGS) {
- emsg_funcname(e_toomanyarg, s);
- xfree(name);
- goto theend;
- }
- }
- }
- if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL || is_funcref) {
- partial_T *const pt = xcalloc(1, sizeof(*pt));
-
- // result is a VAR_PARTIAL
- if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0)) {
- const int arg_len = (arg_pt == NULL ? 0 : arg_pt->pt_argc);
- const int lv_len = tv_list_len(list);
-
- pt->pt_argc = arg_len + lv_len;
- pt->pt_argv = xmalloc(sizeof(pt->pt_argv[0]) * (size_t)pt->pt_argc);
- int i = 0;
- for (; i < arg_len; i++) {
- tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]);
- }
- if (lv_len > 0) {
- TV_LIST_ITER(list, li, {
- tv_copy(TV_LIST_ITEM_TV(li), &pt->pt_argv[i++]);
- });
- }
- }
-
- // For "function(dict.func, [], dict)" and "func" is a partial
- // use "dict". That is backwards compatible.
- if (dict_idx > 0) {
- // The dict is bound explicitly, pt_auto is false
- pt->pt_dict = argvars[dict_idx].vval.v_dict;
- (pt->pt_dict->dv_refcount)++;
- } else if (arg_pt != NULL) {
- // If the dict was bound automatically the result is also
- // bound automatically.
- pt->pt_dict = arg_pt->pt_dict;
- pt->pt_auto = arg_pt->pt_auto;
- if (pt->pt_dict != NULL) {
- (pt->pt_dict->dv_refcount)++;
- }
- }
-
- pt->pt_refcount = 1;
- if (arg_pt != NULL && arg_pt->pt_func != NULL) {
- pt->pt_func = arg_pt->pt_func;
- func_ptr_ref(pt->pt_func);
- xfree(name);
- } else if (is_funcref) {
- pt->pt_func = find_func(trans_name);
- func_ptr_ref(pt->pt_func);
- xfree(name);
- } else {
- pt->pt_name = name;
- func_ref(name);
- }
-
- rettv->v_type = VAR_PARTIAL;
- rettv->vval.v_partial = pt;
- } else {
- // result is a VAR_FUNC
- rettv->v_type = VAR_FUNC;
- rettv->vval.v_string = name;
- func_ref(name);
- }
- }
-theend:
- xfree(trans_name);
-}
-
-/// Get the line number from Vimscript object
-///
-/// @note Unlike tv_get_lnum(), this one supports only "$" special string.
-///
-/// @param[in] tv Object to get value from. Is expected to be a number or
-/// a special string "$".
-/// @param[in] buf Buffer to take last line number from in case tv is "$". May
-/// be NULL, in this case "$" results in zero return.
-///
-/// @return Line number or 0 in case of error.
-linenr_T tv_get_lnum_buf(const typval_T *const tv, const buf_T *const buf)
- FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT
-{
- if (tv->v_type == VAR_STRING
- && tv->vval.v_string != NULL
- && tv->vval.v_string[0] == '$'
- && tv->vval.v_string[1] == NUL
- && buf != NULL) {
- return buf->b_ml.ml_line_count;
- }
- return (linenr_T)tv_get_number_chk(tv, NULL);
-}
-
-/// This function is used by f_input() and f_inputdialog() functions. The third
-/// argument to f_input() specifies the type of completion to use at the
-/// prompt. The third argument to f_inputdialog() specifies the value to return
-/// when the user cancels the prompt.
-void get_user_input(const typval_T *const argvars, typval_T *const rettv, const bool inputdialog,
- const bool secret)
- FUNC_ATTR_NONNULL_ALL
-{
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
-
- const char *prompt;
- const char *defstr = "";
- typval_T *cancelreturn = NULL;
- typval_T cancelreturn_strarg2 = TV_INITIAL_VALUE;
- const char *xp_name = NULL;
- Callback input_callback = { .type = kCallbackNone };
- char prompt_buf[NUMBUFLEN];
- char defstr_buf[NUMBUFLEN];
- char cancelreturn_buf[NUMBUFLEN];
- char xp_name_buf[NUMBUFLEN];
- char def[1] = { 0 };
- if (argvars[0].v_type == VAR_DICT) {
- if (argvars[1].v_type != VAR_UNKNOWN) {
- emsg(_("E5050: {opts} must be the only argument"));
- return;
- }
- dict_T *const dict = argvars[0].vval.v_dict;
- prompt = tv_dict_get_string_buf_chk(dict, S_LEN("prompt"), prompt_buf, "");
- if (prompt == NULL) {
- return;
- }
- defstr = tv_dict_get_string_buf_chk(dict, S_LEN("default"), defstr_buf, "");
- if (defstr == NULL) {
- return;
- }
- dictitem_T *cancelreturn_di = tv_dict_find(dict, S_LEN("cancelreturn"));
- if (cancelreturn_di != NULL) {
- cancelreturn = &cancelreturn_di->di_tv;
- }
- xp_name = tv_dict_get_string_buf_chk(dict, S_LEN("completion"),
- xp_name_buf, def);
- if (xp_name == NULL) { // error
- return;
- }
- if (xp_name == def) { // default to NULL
- xp_name = NULL;
- }
- if (!tv_dict_get_callback(dict, S_LEN("highlight"), &input_callback)) {
- return;
- }
- } else {
- prompt = tv_get_string_buf_chk(&argvars[0], prompt_buf);
- if (prompt == NULL) {
- return;
- }
- if (argvars[1].v_type != VAR_UNKNOWN) {
- defstr = tv_get_string_buf_chk(&argvars[1], defstr_buf);
- if (defstr == NULL) {
- return;
- }
- if (argvars[2].v_type != VAR_UNKNOWN) {
- const char *const strarg2 = tv_get_string_buf_chk(&argvars[2], cancelreturn_buf);
- if (strarg2 == NULL) {
- return;
- }
- if (inputdialog) {
- cancelreturn_strarg2.v_type = VAR_STRING;
- cancelreturn_strarg2.vval.v_string = (char *)strarg2;
- cancelreturn = &cancelreturn_strarg2;
- } else {
- xp_name = strarg2;
- }
- }
- }
- }
-
- int xp_type = EXPAND_NOTHING;
- char *xp_arg = NULL;
- if (xp_name != NULL) {
- // input() with a third argument: completion
- const int xp_namelen = (int)strlen(xp_name);
-
- uint32_t argt = 0;
- if (parse_compl_arg(xp_name, xp_namelen, &xp_type,
- &argt, &xp_arg) == FAIL) {
- return;
- }
- }
-
- const bool cmd_silent_save = cmd_silent;
-
- cmd_silent = false; // Want to see the prompt.
- // Only the part of the message after the last NL is considered as
- // prompt for the command line, unlsess cmdline is externalized
- const char *p = prompt;
- if (!ui_has(kUICmdline)) {
- const char *lastnl = strrchr(prompt, '\n');
- if (lastnl != NULL) {
- p = lastnl + 1;
- msg_start();
- msg_clr_eos();
- msg_puts_len(prompt, p - prompt, echo_attr);
- msg_didout = false;
- msg_starthere();
- }
- }
- cmdline_row = msg_row;
-
- stuffReadbuffSpec(defstr);
-
- const int save_ex_normal_busy = ex_normal_busy;
- ex_normal_busy = 0;
- rettv->vval.v_string = getcmdline_prompt(secret ? NUL : '@', p, echo_attr, xp_type, xp_arg,
- input_callback);
- ex_normal_busy = save_ex_normal_busy;
- callback_free(&input_callback);
-
- if (rettv->vval.v_string == NULL && cancelreturn != NULL) {
- tv_copy(cancelreturn, rettv);
- }
-
- xfree(xp_arg);
-
- // Since the user typed this, no need to wait for return.
- need_wait_return = false;
- msg_didout = false;
- cmd_silent = cmd_silent_save;
-}
-
/// Builds a process argument vector from a Vimscript object (typval_T).
///
/// @param[in] cmd_tv Vimscript object
@@ -5948,56 +5843,6 @@ char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable)
return argv;
}
-void return_register(int regname, typval_T *rettv)
-{
- char buf[2] = { (char)regname, 0 };
-
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = xstrdup(buf);
-}
-
-void screenchar_adjust(ScreenGrid **grid, int *row, int *col)
-{
- // TODO(bfredl): this is a hack for legacy tests which use screenchar()
- // to check printed messages on the screen (but not floats etc
- // as these are not legacy features). If the compositor is refactored to
- // have its own buffer, this should just read from it instead.
- msg_scroll_flush();
-
- *grid = ui_comp_get_grid_at_coord(*row, *col);
-
- // Make `row` and `col` relative to the grid
- *row -= (*grid)->comp_row;
- *col -= (*grid)->comp_col;
-}
-
-/// "stdpath()" helper for list results
-void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv)
- FUNC_ATTR_NONNULL_ALL
-{
- list_T *const list = tv_list_alloc(kListLenShouldKnow);
- rettv->v_type = VAR_LIST;
- rettv->vval.v_list = list;
- tv_list_ref(list);
- char *const dirs = stdpaths_get_xdg_var(xdg);
- if (dirs == NULL) {
- 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, appname, true);
- tv_list_append_allocated_string(list, dir_with_nvim);
- }
- } while (iter != NULL);
- xfree(dirs);
-}
-
static list_T *string_to_list(const char *str, size_t len, const bool keepempty)
{
if (!keepempty && str[len - 1] == NL) {
@@ -6417,149 +6262,6 @@ void timer_teardown(void)
timer_stop_all();
}
-/// Write "list" of strings to file "fd".
-///
-/// @param fp File to write to.
-/// @param[in] list List to write.
-/// @param[in] binary Whether to write in binary mode.
-///
-/// @return true in case of success, false otherwise.
-bool write_list(FileDescriptor *const fp, const list_T *const list, const bool binary)
- FUNC_ATTR_NONNULL_ARG(1)
-{
- int error = 0;
- TV_LIST_ITER_CONST(list, li, {
- const char *const s = tv_get_string_chk(TV_LIST_ITEM_TV(li));
- if (s == NULL) {
- return false;
- }
- const char *hunk_start = s;
- for (const char *p = hunk_start;; p++) {
- if (*p == NUL || *p == NL) {
- if (p != hunk_start) {
- const ptrdiff_t written = file_write(fp, hunk_start,
- (size_t)(p - hunk_start));
- if (written < 0) {
- error = (int)written;
- goto write_list_error;
- }
- }
- if (*p == NUL) {
- break;
- } else {
- hunk_start = p + 1;
- const ptrdiff_t written = file_write(fp, (char[]){ NUL }, 1);
- if (written < 0) {
- error = (int)written;
- break;
- }
- }
- }
- }
- if (!binary || TV_LIST_ITEM_NEXT(list, li) != NULL) {
- const ptrdiff_t written = file_write(fp, "\n", 1);
- if (written < 0) {
- error = (int)written;
- goto write_list_error;
- }
- }
- });
- if ((error = file_flush(fp)) != 0) {
- goto write_list_error;
- }
- return true;
-write_list_error:
- semsg(_(e_write2), os_strerror(error));
- return false;
-}
-
-/// Write a blob to file with descriptor `fp`.
-///
-/// @param[in] fp File to write to.
-/// @param[in] blob Blob to write.
-///
-/// @return true on success, or false on failure.
-bool write_blob(FileDescriptor *const fp, const blob_T *const blob)
- FUNC_ATTR_NONNULL_ARG(1)
-{
- int error = 0;
- const int len = tv_blob_len(blob);
- if (len > 0) {
- const ptrdiff_t written = file_write(fp, blob->bv_ga.ga_data, (size_t)len);
- if (written < (ptrdiff_t)len) {
- error = (int)written;
- goto write_blob_error;
- }
- }
- error = file_flush(fp);
- if (error != 0) {
- goto write_blob_error;
- }
- return true;
-write_blob_error:
- semsg(_(e_write2), os_strerror(error));
- return false;
-}
-
-/// Read blob from file "fd".
-/// Caller has allocated a blob in "rettv".
-///
-/// @param[in] fd File to read from.
-/// @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 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 FAIL; // can't read the file, error
- }
-
- 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) {
- // An empty blob is returned on error.
- tv_blob_free(rettv->vval.v_blob);
- rettv->vval.v_blob = NULL;
- return FAIL;
- }
- return OK;
-}
-
/// Saves a typval_T as a string.
///
/// For lists or buffers, replaces NLs with NUL and separates items with NLs.
@@ -7146,13 +6848,13 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex
char c1 = *in_end;
*in_end = NUL;
- char *temp_result = eval_to_string(expr_start + 1, false);
+ char *temp_result = eval_to_string(expr_start + 1, false, 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);
- STRCAT(retval, temp_result);
- STRCAT(retval, expr_end + 1);
+ strcat(retval, temp_result);
+ strcat(retval, expr_end + 1);
}
xfree(temp_result);
@@ -8201,6 +7903,12 @@ void ex_echohl(exarg_T *eap)
echo_attr = syn_name2attr(eap->arg);
}
+/// Returns the :echo attribute
+int get_echo_attr(void)
+{
+ return echo_attr;
+}
+
/// ":execute expr1 ..." execute the result of an expression.
/// ":echomsg expr1 ..." Print a message
/// ":echoerr expr1 ..." Print an error
@@ -8796,7 +8504,7 @@ Channel *find_job(uint64_t id, bool show_error)
{
Channel *data = find_channel(id);
if (!data || data->streamtype != kChannelStreamProc
- || process_is_stopped(&data->stream.proc)) {
+ || proc_is_stopped(&data->stream.proc)) {
if (show_error) {
if (data && data->streamtype != kChannelStreamProc) {
emsg(_(e_invchanjob));
@@ -8907,7 +8615,7 @@ bool eval_has_provider(const char *feat, bool throw_if_fast)
char name[32]; // Normalized: "python3_compiled" => "python3".
snprintf(name, sizeof(name), "%s", feat);
- strchrsub(name, '_', '\0'); // Chop any "_xx" suffix.
+ strchrsub(name, '_', NUL); // Chop any "_xx" suffix.
char buf[256];
typval_T tv;
@@ -9062,7 +8770,7 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic)
return FAIL;
} else {
// Compare two Lists for being equal or unequal.
- n1 = tv_list_equal(typ1->vval.v_list, typ2->vval.v_list, ic, false);
+ n1 = tv_list_equal(typ1->vval.v_list, typ2->vval.v_list, ic);
if (type == EXPR_NEQUAL) {
n1 = !n1;
}
@@ -9085,7 +8793,7 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic)
return FAIL;
} else {
// Compare two Dictionaries for being equal or unequal.
- n1 = tv_dict_equal(typ1->vval.v_dict, typ2->vval.v_dict, ic, false);
+ n1 = tv_dict_equal(typ1->vval.v_dict, typ2->vval.v_dict, ic);
if (type == EXPR_NEQUAL) {
n1 = !n1;
}
@@ -9106,14 +8814,14 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic)
if (typ1->v_type == VAR_FUNC && typ2->v_type == VAR_FUNC) {
// strings are considered the same if their value is
// the same
- n1 = tv_equal(typ1, typ2, ic, false);
+ n1 = tv_equal(typ1, typ2, ic);
} else if (typ1->v_type == VAR_PARTIAL && typ2->v_type == VAR_PARTIAL) {
n1 = typ1->vval.v_partial == typ2->vval.v_partial;
} else {
n1 = false;
}
} else {
- n1 = tv_equal(typ1, typ2, ic, false);
+ n1 = tv_equal(typ1, typ2, ic);
}
if (type == EXPR_NEQUAL || type == EXPR_ISNOT) {
n1 = !n1;