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.c13226
1 files changed, 6279 insertions, 6947 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 17e89e5757..d6ee13857a 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1,8 +1,12 @@
+// 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 <float.h>
#include <inttypes.h>
#include <stdarg.h>
#include <string.h>
@@ -30,6 +34,7 @@
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
+#include "nvim/os/fileio.h"
#include "nvim/func_attr.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
@@ -42,6 +47,7 @@
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/menu.h"
#include "nvim/message.h"
#include "nvim/misc1.h"
#include "nvim/keymap.h"
@@ -93,7 +99,13 @@
#include "nvim/lib/kvec.h"
#include "nvim/lib/khash.h"
#include "nvim/lib/queue.h"
-#include "nvim/eval/typval_encode.h"
+#include "nvim/lua/executor.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/executor.h"
+#include "nvim/eval/gc.h"
+#include "nvim/macros.h"
+
+// TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead
#define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */
@@ -131,31 +143,28 @@
* "newkey" is the key for the new item.
*/
typedef struct lval_S {
- char_u *ll_name; /* start of variable name (can be NULL) */
- char_u *ll_exp_name; /* NULL or expanded name in allocated memory. */
- typval_T *ll_tv; /* Typeval of item being used. If "newkey"
- isn't NULL it's the Dict to which to add
- the item. */
- listitem_T *ll_li; /* The list item or NULL. */
- list_T *ll_list; /* The list or NULL. */
- int ll_range; /* TRUE when a [i:j] range was used */
- long ll_n1; /* First index for list */
- long ll_n2; /* Second index for list range */
- int ll_empty2; /* Second index is empty: [i:] */
- dict_T *ll_dict; /* The Dictionary or NULL */
- dictitem_T *ll_di; /* The dictitem or NULL */
- char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */
+ const char *ll_name; ///< Start of variable name (can be NULL).
+ size_t ll_name_len; ///< Length of the .ll_name.
+ char *ll_exp_name; ///< NULL or expanded name in allocated memory.
+ typval_T *ll_tv; ///< Typeval of item being used. If "newkey"
+ ///< isn't NULL it's the Dict to which to add the item.
+ listitem_T *ll_li; ///< The list item or NULL.
+ list_T *ll_list; ///< The list or NULL.
+ int ll_range; ///< TRUE when a [i:j] range was used.
+ long ll_n1; ///< First index for list.
+ long ll_n2; ///< Second index for list range.
+ int ll_empty2; ///< Second index is empty: [i:].
+ dict_T *ll_dict; ///< The Dictionary or NULL.
+ dictitem_T *ll_di; ///< The dictitem or NULL.
+ char_u *ll_newkey; ///< New key for Dict in allocated memory or NULL.
} lval_T;
static char *e_letunexp = N_("E18: Unexpected characters in :let");
-static char *e_listidx = N_("E684: list index out of range: %" PRId64);
-static char *e_undefvar = N_("E121: Undefined variable: %s");
static char *e_missbrac = N_("E111: Missing ']'");
static char *e_listarg = N_("E686: Argument of %s must be a List");
static char *e_listdictarg = N_(
"E712: Argument of %s must be a List or Dictionary");
-static char *e_emptykey = N_("E713: Cannot use empty key for Dictionary");
static char *e_listreq = N_("E714: List required");
static char *e_dictreq = N_("E715: Dictionary required");
static char *e_stringreq = N_("E928: String required");
@@ -166,15 +175,20 @@ static char *e_funcexts = N_(
static char *e_funcdict = N_("E717: Dictionary entry already exists");
static char *e_funcref = N_("E718: Funcref required");
static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary");
-static char *e_letwrong = N_("E734: Wrong variable type for %s=");
static char *e_nofunc = N_("E130: Unknown function: %s");
static char *e_illvar = N_("E461: Illegal variable name: %s");
-static char *e_float_as_string = N_("E806: using Float as a String");
+static const char *e_readonlyvar = N_(
+ "E46: Cannot change read-only variable \"%.*s\"");
+
+// TODO(ZyX-I): move to eval/executor
+static char *e_letwrong = N_("E734: Wrong variable type for %s=");
-static char_u * const empty_string = (char_u *)"";
static char_u * const namespace_char = (char_u *)"abglstvw";
-static dictitem_T globvars_var; /* variable used for g: */
+/// Variable used for g:
+static ScopeDictDictItem globvars_var;
+
+/// g: value
#define globvarht globvardict.dv_hashtab
/*
@@ -185,12 +199,15 @@ static hashtab_T compat_hashtab;
hashtab_T func_hashtab;
+// Used for checking if local variables or arguments used in a lambda.
+static int *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 {
- dictitem_T sv_var;
+ ScopeDictDictItem sv_var;
dict_T sv_dict;
} scriptvar_T;
@@ -200,61 +217,72 @@ static garray_T ga_scripts = {0, 0, sizeof(scriptvar_T *), 4, NULL};
static int echo_attr = 0; /* attributes used for ":echo" */
-/* Values for trans_function_name() argument: */
-#define TFN_INT 1 /* internal function name OK */
-#define TFN_QUIET 2 /* no error messages */
-#define TFN_NO_AUTOLOAD 4 /* do not use script autoloading */
-
-/* Values for get_lval() flags argument: */
-#define GLV_QUIET TFN_QUIET /* no error messages */
-#define GLV_NO_AUTOLOAD TFN_NO_AUTOLOAD /* do not use script autoloading */
-
-/* function flags */
-#define FC_ABORT 1 /* abort function on error */
-#define FC_RANGE 2 /* function accepts range */
-#define FC_DICT 4 /* Dict function, uses "self" */
-
-/* The names of packages that once were loaded are remembered. */
-static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL};
-
-// List heads for garbage collection. Although there can be a reference loop
-// from partial to dict to partial, we don't need to keep track of the partial,
-// since it will get freed when the dict is unused and gets freed.
-static dict_T *first_dict = NULL; // list of all dicts
-static list_T *first_list = NULL; // list of all lists
+/// trans_function_name() flags
+typedef enum {
+ TFN_INT = 1, ///< May use internal function name
+ TFN_QUIET = 2, ///< Do not emit error messages.
+ TFN_NO_AUTOLOAD = 4, ///< Do not use script autoloading.
+ TFN_NO_DEREF = 8, ///< Do not dereference a Funcref.
+ TFN_READ_ONLY = 16, ///< Will not change the variable.
+} TransFunctionNameFlags;
+
+/// get_lval() flags
+typedef enum {
+ GLV_QUIET = TFN_QUIET, ///< Do not emit error messages.
+ GLV_NO_AUTOLOAD = TFN_NO_AUTOLOAD, ///< Do not use script autoloading.
+ GLV_READ_ONLY = TFN_READ_ONLY, ///< Indicates that caller will not change
+ ///< the value (prevents error message).
+} GetLvalFlags;
+
+// function flags
+#define FC_ABORT 0x01 // abort function on error
+#define FC_RANGE 0x02 // function accepts range
+#define FC_DICT 0x04 // Dict function, uses "self"
+#define FC_CLOSURE 0x08 // closure, uses outer scope variables
+#define FC_DELETED 0x10 // :delfunction used while uf_refcount > 0
+#define FC_REMOVED 0x20 // function redefined while uf_refcount > 0
+
+// The names of packages that once were loaded are remembered.
+static garray_T ga_loaded = { 0, 0, sizeof(char_u *), 4, NULL };
#define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j]
#define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j]
-#define VAR_SHORT_LEN 20 /* short variable name length */
-#define FIXVAR_CNT 12 /* number of fixed variables */
-
-/* structure to hold info for a function that is currently being executed. */
-typedef struct funccall_S funccall_T;
+/// Short variable name length
+#define VAR_SHORT_LEN 20
+/// Number of fixed variables used for arguments
+#define FIXVAR_CNT 12
struct funccall_S {
- ufunc_T *func; /* function being called */
- int linenr; /* next line to be executed */
- int returned; /* ":return" used */
- struct /* fixed variables for arguments */
- {
- dictitem_T var; /* variable (without room for name) */
- char_u room[VAR_SHORT_LEN]; /* room for the name */
- } fixvar[FIXVAR_CNT];
- dict_T l_vars; /* l: local function variables */
- dictitem_T l_vars_var; /* variable for l: scope */
- dict_T l_avars; /* a: argument variables */
- dictitem_T l_avars_var; /* variable for a: scope */
- list_T l_varlist; /* list for a:000 */
- listitem_T l_listitems[MAX_FUNC_ARGS]; /* listitems for a:000 */
- typval_T *rettv; /* return value */
- linenr_T breakpoint; /* next line with breakpoint or zero */
- int dbg_tick; /* debug_tick when breakpoint was set */
- int level; /* top nesting level of executed function */
- proftime_T prof_child; /* time spent in a child */
- funccall_T *caller; /* calling function or NULL */
+ ufunc_T *func; ///< Function being called.
+ int linenr; ///< Next line to be executed.
+ int returned; ///< ":return" used.
+ /// Fixed variables for arguments.
+ TV_DICTITEM_STRUCT(VAR_SHORT_LEN + 1) fixvar[FIXVAR_CNT];
+ dict_T l_vars; ///< l: local function variables.
+ ScopeDictDictItem l_vars_var; ///< Variable for l: scope.
+ dict_T l_avars; ///< a: argument variables.
+ ScopeDictDictItem l_avars_var; ///< Variable for a: scope.
+ list_T l_varlist; ///< List for a:000.
+ listitem_T l_listitems[MAX_FUNC_ARGS]; ///< List items for a:000.
+ typval_T *rettv; ///< Return value.
+ linenr_T breakpoint; ///< Next line with breakpoint or zero.
+ int dbg_tick; ///< Debug_tick when breakpoint was set.
+ int level; ///< Top nesting level of executed function.
+ proftime_T prof_child; ///< Time spent in a child.
+ funccall_T *caller; ///< Calling function or NULL.
+ int fc_refcount; ///< Number of user functions that reference this funccall.
+ int fc_copyID; ///< CopyID used for garbage collection.
+ garray_T fc_funcs; ///< List of ufunc_T* which keep a reference to "func".
};
+///< Structure used by trans_function_name()
+typedef struct {
+ dict_T *fd_dict; ///< Dictionary used.
+ char_u *fd_newkey; ///< New key in "dict" in allocated memory.
+ dictitem_T *fd_di; ///< Dictionary item used.
+} funcdict_T;
+
/*
* Info used by a ":for" loop.
*/
@@ -266,15 +294,6 @@ typedef struct {
} forinfo_T;
/*
- * Struct used by trans_function_name()
- */
-typedef struct {
- dict_T *fd_dict; /* Dictionary used */
- char_u *fd_newkey; /* new key in "dict" in allocated memory */
- dictitem_T *fd_di; /* Dictionary item used */
-} funcdict_T;
-
-/*
* enum used by var_flavour()
*/
typedef enum {
@@ -305,8 +324,8 @@ typedef enum {
// variables with the VV_ defines.
static struct vimvar {
char *vv_name; ///< Name of the variable, without v:.
- dictitem16_T vv_di; ///< Value and name for key (max 16 chars)
- char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX.
+ TV_DICTITEM_STRUCT(17) vv_di; ///< Value and name for key (max 16 chars).
+ char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX.
} vimvars[] =
{
// VV_ tails differing from upcased string literals:
@@ -389,6 +408,7 @@ static struct vimvar {
VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO),
VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO),
VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO),
+ VV(VV_TESTING, "testing", VAR_NUMBER, 0),
VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO),
VV(VV_TYPE_STRING, "t_string", VAR_NUMBER, VV_RO),
VV(VV_TYPE_FUNC, "t_func", VAR_NUMBER, VV_RO),
@@ -410,23 +430,11 @@ static struct vimvar {
#define vv_dict vv_di.di_tv.vval.v_dict
#define vv_tv vv_di.di_tv
-static dictitem_T vimvars_var; // variable used for v:
-#define vimvarht vimvardict.dv_hashtab
+/// Variable used for v:
+static ScopeDictDictItem vimvars_var;
-typedef enum {
- kCallbackNone,
- kCallbackFuncref,
- kCallbackPartial,
-} CallbackType;
-
-typedef struct {
- union {
- char_u *funcref;
- partial_T *partial;
- } data;
- CallbackType type;
-} Callback;
-#define CALLBACK_NONE ((Callback){ .type = kCallbackNone })
+/// v: hashtab
+#define vimvarht vimvardict.dv_hashtab
typedef struct {
union {
@@ -440,18 +448,11 @@ typedef struct {
bool rpc;
int refcount;
Callback on_stdout, on_stderr, on_exit;
- int *status_ptr;
+ varnumber_T *status_ptr;
uint64_t id;
MultiQueue *events;
} TerminalJobData;
-typedef struct dict_watcher {
- Callback callback;
- char *key_pattern;
- QUEUE node;
- bool busy; // prevent recursion if the dict is changed in the callback
-} DictWatcher;
-
typedef struct {
TerminalJobData *data;
Callback *callback;
@@ -467,6 +468,7 @@ typedef struct {
int refcount;
long timeout;
bool stopped;
+ bool paused;
Callback callback;
} timer_T;
@@ -486,6 +488,24 @@ typedef struct fst {
KHASH_MAP_INIT_STR(functions, VimLFuncDef)
+/// Type of assert_* check being performed
+typedef enum
+{
+ ASSERT_EQUAL,
+ ASSERT_NOTEQUAL,
+ ASSERT_MATCH,
+ ASSERT_NOTMATCH,
+ ASSERT_INRANGE,
+ ASSERT_OTHER,
+} assert_type_T;
+
+/// Type for dict_list function
+typedef enum {
+ kDictListKeys, ///< List dictionary keys.
+ kDictListValues, ///< List dictionary values.
+ kDictListItems, ///< List dictionary contents: [keys, values].
+} DictListType;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval.c.generated.h"
#endif
@@ -498,6 +518,14 @@ static PMap(uint64_t) *jobs = NULL;
static uint64_t last_timer_id = 0;
static PMap(uint64_t) *timers = NULL;
+/// Dummy va_list for passing to vim_snprintf
+///
+/// Used because:
+/// - passing a NULL pointer doesn't work when va_list isn't a pointer
+/// - locally in the function results in a "used before set" warning
+/// - using va_start() to initialize it gives "function with fixed args" error
+static va_list dummy_ap;
+
static const char *const msgpack_type_names[] = {
[kMPNil] = "nil",
[kMPBoolean] = "boolean",
@@ -558,19 +586,19 @@ void eval_init(void)
}
vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
- dict_T *const msgpack_types_dict = dict_alloc();
+ dict_T *const msgpack_types_dict = tv_dict_alloc();
for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) {
- list_T *const type_list = list_alloc();
+ list_T *const type_list = tv_list_alloc();
type_list->lv_lock = VAR_FIXED;
type_list->lv_refcount = 1;
- dictitem_T *const di = dictitem_alloc((char_u *) msgpack_type_names[i]);
- di->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
+ dictitem_T *const di = tv_dict_item_alloc(msgpack_type_names[i]);
+ di->di_flags |= DI_FLAGS_RO|DI_FLAGS_FIX;
di->di_tv = (typval_T) {
.v_type = VAR_LIST,
.vval = { .v_list = type_list, },
};
eval_msgpack_type_lists[i] = type_list;
- if (dict_add(msgpack_types_dict, di) == FAIL) {
+ if (tv_dict_add(msgpack_types_dict, di) == FAIL) {
// There must not be duplicate items in this dictionary by definition.
assert(false);
}
@@ -578,12 +606,12 @@ void eval_init(void)
msgpack_types_dict->dv_lock = VAR_FIXED;
set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict);
- set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc());
+ set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc());
- dict_T *v_event = dict_alloc();
+ dict_T *v_event = tv_dict_alloc();
v_event->dv_lock = VAR_FIXED;
set_vim_var_dict(VV_EVENT, v_event);
- set_vim_var_list(VV_ERRORS, list_alloc());
+ set_vim_var_list(VV_ERRORS, tv_list_alloc());
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
set_vim_var_nr(VV_HLSEARCH, 1L);
set_vim_var_nr(VV_COUNT1, 1);
@@ -614,7 +642,7 @@ void eval_clear(void)
xfree(p->vv_str);
p->vv_str = NULL;
} else if (p->vv_di.di_tv.v_type == VAR_LIST) {
- list_unref(p->vv_list);
+ tv_list_unref(p->vv_list);
p->vv_list = NULL;
}
}
@@ -640,12 +668,11 @@ void eval_clear(void)
xfree(SCRIPT_SV(i));
ga_clear(&ga_scripts);
- /* unreferenced lists and dicts */
- (void)garbage_collect();
+ // unreferenced lists and dicts
+ (void)garbage_collect(false);
- /* functions */
+ // functions
free_all_functions();
- hash_clear(&func_hashtab);
}
#endif
@@ -703,18 +730,17 @@ int current_func_returned(void)
*/
void set_internal_string_var(char_u *name, char_u *value)
{
- char_u *val = vim_strsave(value);
- typval_T *tvp = xcalloc(1, sizeof(typval_T));
+ const typval_T tv = {
+ .v_type = VAR_STRING,
+ .vval.v_string = value,
+ };
- tvp->v_type = VAR_STRING;
- tvp->vval.v_string = val;
- set_var(name, tvp, FALSE);
- free_tv(tvp);
+ set_var((const char *)name, STRLEN(name), (typval_T *)&tv, true);
}
static lval_T *redir_lval = NULL;
-static garray_T redir_ga; /* only valid when redir_lval is not NULL */
-static char_u *redir_endp = NULL;
+static garray_T redir_ga; // Only valid when redir_lval is not NULL.
+static char_u *redir_endp = NULL;
static char_u *redir_varname = NULL;
/*
@@ -745,11 +771,11 @@ var_redir_start (
/* The output is stored in growarray "redir_ga" until redirection ends. */
ga_init(&redir_ga, (int)sizeof(char), 500);
- /* Parse the variable name (can be a dict or list entry). */
- redir_endp = get_lval(redir_varname, NULL, redir_lval, FALSE, FALSE, 0,
- FNE_CHECK_START);
- if (redir_endp == NULL || redir_lval->ll_name == NULL || *redir_endp !=
- NUL) {
+ // Parse the variable name (can be a dict or list entry).
+ redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval, false, false,
+ 0, FNE_CHECK_START);
+ if (redir_endp == NULL || redir_lval->ll_name == NULL
+ || *redir_endp != NUL) {
clear_lval(redir_lval);
if (redir_endp != NULL && *redir_endp != NUL)
/* Trailing characters are present after the variable name */
@@ -823,12 +849,13 @@ void var_redir_stop(void)
ga_append(&redir_ga, NUL); /* Append the trailing NUL. */
tv.v_type = VAR_STRING;
tv.vval.v_string = redir_ga.ga_data;
- /* Call get_lval() again, if it's inside a Dict or List it may
- * have changed. */
- redir_endp = get_lval(redir_varname, NULL, redir_lval,
- FALSE, FALSE, 0, FNE_CHECK_START);
- if (redir_endp != NULL && redir_lval->ll_name != NULL)
- set_var_lval(redir_lval, redir_endp, &tv, FALSE, (char_u *)".");
+ // Call get_lval() again, if it's inside a Dict or List it may
+ // have changed.
+ redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval,
+ false, false, 0, FNE_CHECK_START);
+ if (redir_endp != NULL && redir_lval->ll_name != NULL) {
+ set_var_lval(redir_lval, redir_endp, &tv, false, (char_u *)".");
+ }
clear_lval(redir_lval);
}
@@ -846,7 +873,7 @@ 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)
{
- int err = false;
+ bool err = false;
set_vim_var_string(VV_CC_FROM, enc_from, -1);
set_vim_var_string(VV_CC_TO, enc_to, -1);
@@ -868,7 +895,7 @@ int eval_charconvert(const char *const enc_from, const char *const enc_to,
int eval_printexpr(const char *const fname, const char *const args)
{
- int err = false;
+ bool err = false;
set_vim_var_string(VV_FNAME_IN, fname, -1);
set_vim_var_string(VV_CMDARG, args, -1);
@@ -888,7 +915,7 @@ int eval_printexpr(const char *const fname, const char *const args)
void eval_diff(const char *const origfile, const char *const newfile,
const char *const outfile)
{
- int err = FALSE;
+ bool err = false;
set_vim_var_string(VV_FNAME_IN, origfile, -1);
set_vim_var_string(VV_FNAME_NEW, newfile, -1);
@@ -902,7 +929,7 @@ void eval_diff(const char *const origfile, const char *const newfile,
void eval_patch(const char *const origfile, const char *const difffile,
const char *const outfile)
{
- int err;
+ bool err = false;
set_vim_var_string(VV_FNAME_IN, origfile, -1);
set_vim_var_string(VV_FNAME_DIFF, difffile, -1);
@@ -921,56 +948,61 @@ void eval_patch(const char *const origfile, const char *const difffile,
int
eval_to_bool (
char_u *arg,
- int *error,
+ bool *error,
char_u **nextcmd,
int skip /* only parse, don't execute */
)
{
typval_T tv;
- int retval = FALSE;
+ bool retval = false;
- if (skip)
- ++emsg_skip;
- if (eval0(arg, &tv, nextcmd, !skip) == FAIL)
- *error = TRUE;
- else {
- *error = FALSE;
+ if (skip) {
+ emsg_skip++;
+ }
+ if (eval0(arg, &tv, nextcmd, !skip) == FAIL) {
+ *error = true;
+ } else {
+ *error = false;
if (!skip) {
- retval = (get_tv_number_chk(&tv, error) != 0);
- clear_tv(&tv);
+ retval = (tv_get_number_chk(&tv, error) != 0);
+ tv_clear(&tv);
}
}
- if (skip)
- --emsg_skip;
+ if (skip) {
+ emsg_skip--;
+ }
return retval;
}
-/*
- * Top level evaluation function, returning a string. If "skip" is TRUE,
- * only parsing to "nextcmd" is done, without reporting errors. Return
- * pointer to allocated memory, or NULL for failure or when "skip" is TRUE.
- */
-char_u *
-eval_to_string_skip (
- char_u *arg,
- char_u **nextcmd,
- int skip /* only parse, don't execute */
-)
+/// 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)
+ FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT
{
typval_T tv;
- char_u *retval;
+ char *retval;
- if (skip)
- ++emsg_skip;
- if (eval0(arg, &tv, nextcmd, !skip) == FAIL || skip)
+ if (skip) {
+ emsg_skip++;
+ }
+ if (eval0((char_u *)arg, &tv, (char_u **)nextcmd, !skip) == FAIL || skip) {
retval = NULL;
- else {
- retval = vim_strsave(get_tv_string(&tv));
- clear_tv(&tv);
+ } else {
+ retval = xstrdup(tv_get_string(&tv));
+ tv_clear(&tv);
+ }
+ if (skip) {
+ emsg_skip--;
}
- if (skip)
- --emsg_skip;
return retval;
}
@@ -996,31 +1028,33 @@ int skip_expr(char_u **pp)
char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert)
{
typval_T tv;
- char_u *retval;
+ char *retval;
garray_T ga;
- char_u numbuf[NUMBUFLEN];
- if (eval0(arg, &tv, nextcmd, TRUE) == FAIL)
+ if (eval0(arg, &tv, nextcmd, true) == FAIL) {
retval = NULL;
- else {
+ } else {
if (convert && tv.v_type == VAR_LIST) {
ga_init(&ga, (int)sizeof(char), 80);
if (tv.vval.v_list != NULL) {
- list_join(&ga, tv.vval.v_list, "\n");
- if (tv.vval.v_list->lv_len > 0)
+ tv_list_join(&ga, tv.vval.v_list, "\n");
+ if (tv.vval.v_list->lv_len > 0) {
ga_append(&ga, NL);
+ }
}
ga_append(&ga, NUL);
- retval = (char_u *)ga.ga_data;
+ retval = (char *)ga.ga_data;
} else if (convert && tv.v_type == VAR_FLOAT) {
- vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv.vval.v_float);
- retval = vim_strsave(numbuf);
- } else
- retval = vim_strsave(get_tv_string(&tv));
- clear_tv(&tv);
+ char numbuf[NUMBUFLEN];
+ vim_snprintf(numbuf, NUMBUFLEN, "%g", tv.vval.v_float);
+ retval = xstrdup(numbuf);
+ } else {
+ retval = xstrdup(tv_get_string(&tv));
+ }
+ tv_clear(&tv);
}
- return retval;
+ return (char_u *)retval;
}
/*
@@ -1049,19 +1083,19 @@ char_u *eval_to_string_safe(char_u *arg, char_u **nextcmd, int use_sandbox)
* Evaluates "expr" silently.
* Returns -1 for an error.
*/
-int eval_to_number(char_u *expr)
+varnumber_T eval_to_number(char_u *expr)
{
typval_T rettv;
- int retval;
+ varnumber_T retval;
char_u *p = skipwhite(expr);
++emsg_off;
- if (eval1(&p, &rettv, TRUE) == FAIL)
+ if (eval1(&p, &rettv, true) == FAIL) {
retval = -1;
- else {
- retval = get_tv_number_chk(&rettv, NULL);
- clear_tv(&rettv);
+ } else {
+ retval = tv_get_number_chk(&rettv, NULL);
+ tv_clear(&rettv);
}
--emsg_off;
@@ -1118,11 +1152,12 @@ list_T *eval_spell_expr(char_u *badword, char_u *expr)
if (p_verbose == 0)
++emsg_off;
- if (eval1(&p, &rettv, TRUE) == OK) {
- if (rettv.v_type != VAR_LIST)
- clear_tv(&rettv);
- else
+ if (eval1(&p, &rettv, true) == OK) {
+ if (rettv.v_type != VAR_LIST) {
+ tv_clear(&rettv);
+ } else {
list = rettv.vval.v_list;
+ }
}
if (p_verbose == 0)
@@ -1138,54 +1173,39 @@ list_T *eval_spell_expr(char_u *badword, char_u *expr)
* Return -1 if anything isn't right.
* Used to get the good word and score from the eval_spell_expr() result.
*/
-int get_spellword(list_T *list, char_u **pp)
+int get_spellword(list_T *list, const char **pp)
{
listitem_T *li;
li = list->lv_first;
- if (li == NULL)
+ if (li == NULL) {
return -1;
- *pp = get_tv_string(&li->li_tv);
+ }
+ *pp = tv_get_string(&li->li_tv);
li = li->li_next;
- if (li == NULL)
+ if (li == NULL) {
return -1;
- return get_tv_number(&li->li_tv);
-}
-
-/*
- * Top level evaluation function.
- * Returns an allocated typval_T with the result.
- * Returns NULL when there is an error.
- */
-typval_T *eval_expr(char_u *arg, char_u **nextcmd)
-{
- typval_T *tv = xmalloc(sizeof(typval_T));
-
- if (eval0(arg, tv, nextcmd, TRUE) == FAIL) {
- xfree(tv);
- return NULL;
}
-
- return tv;
+ return tv_get_number(&li->li_tv);
}
-// Call some vimL function and return the result in "*rettv".
+// Call some vim script function and return the result in "*rettv".
// Uses argv[argc] for the function arguments. Only Number and String
// arguments are currently supported.
//
// Return OK or FAIL.
int call_vim_function(
- char_u *func,
+ const char_u *func,
int argc,
- char_u **argv,
+ const char_u *const *const argv,
int safe, // use the sandbox
int str_arg_only, // all arguments are strings
typval_T *rettv
)
{
- long n;
+ varnumber_T n;
int len;
int doesrange;
void *save_funccalp = NULL;
@@ -1212,7 +1232,7 @@ int call_vim_function(
argvars[i].vval.v_number = n;
} else {
argvars[i].v_type = VAR_STRING;
- argvars[i].vval.v_string = argv[i];
+ argvars[i].vval.v_string = (char_u *)argv[i];
}
}
@@ -1221,8 +1241,8 @@ int call_vim_function(
++sandbox;
}
- rettv->v_type = VAR_UNKNOWN; /* clear_tv() uses this */
- ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars,
+ rettv->v_type = VAR_UNKNOWN; // tv_clear() uses this.
+ ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars, NULL,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&doesrange, true, NULL, NULL);
if (safe) {
@@ -1232,74 +1252,72 @@ int call_vim_function(
xfree(argvars);
if (ret == FAIL) {
- clear_tv(rettv);
+ tv_clear(rettv);
}
return ret;
}
-/*
- * Call vimL function "func" and return the result as a number.
- * Returns -1 when calling the function fails.
- * Uses argv[argc] for the function arguments.
- */
-long
-call_func_retnr (
- char_u *func,
- int argc,
- char_u **argv,
- int safe /* use the sandbox */
-)
+/// Call Vim script function and return the result as a number
+///
+/// @param[in] func Function name.
+/// @param[in] argc Number of arguments.
+/// @param[in] argv Array with string arguments.
+/// @param[in] safe Use with sandbox.
+///
+/// @return -1 when calling function fails, result of function otherwise.
+varnumber_T call_func_retnr(char_u *func, int argc,
+ const char_u *const *const argv, int safe)
{
typval_T rettv;
- long retval;
+ varnumber_T retval;
/* All arguments are passed as strings, no conversion to number. */
if (call_vim_function(func, argc, argv, safe, TRUE, &rettv) == FAIL)
return -1;
- retval = get_tv_number_chk(&rettv, NULL);
- clear_tv(&rettv);
+ retval = tv_get_number_chk(&rettv, NULL);
+ tv_clear(&rettv);
return retval;
}
-/*
- * Call vimL function "func" and return the result as a string.
- * Returns NULL when calling the function fails.
- * Uses argv[argc] for the function arguments.
- */
-void *
-call_func_retstr (
- char_u *func,
- int argc,
- char_u **argv,
- int safe /* use the sandbox */
-)
+/// Call Vim script function and return the result as a string
+///
+/// @param[in] func Function name.
+/// @param[in] argc Number of arguments.
+/// @param[in] argv Array with string arguments.
+/// @param[in] safe Use the sandbox.
+///
+/// @return [allocated] NULL when calling function fails, allocated string
+/// otherwise.
+char *call_func_retstr(const char *const func, const int argc,
+ const char_u *const *const argv,
+ const bool safe)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
{
typval_T rettv;
- char_u *retval;
-
- /* All arguments are passed as strings, no conversion to number. */
- if (call_vim_function(func, argc, argv, safe, TRUE, &rettv) == FAIL)
+ // All arguments are passed as strings, no conversion to number.
+ if (call_vim_function((const char_u *)func, argc, argv, safe, true, &rettv)
+ == FAIL) {
return NULL;
+ }
- retval = vim_strsave(get_tv_string(&rettv));
- clear_tv(&rettv);
+ char *const retval = xstrdup(tv_get_string(&rettv));
+ tv_clear(&rettv);
return retval;
}
-/*
- * Call vimL function "func" and return the result as a List.
- * Uses argv[argc] for the function arguments.
- * Returns NULL when there is something wrong.
- */
-void *
-call_func_retlist (
- char_u *func,
- int argc,
- char_u **argv,
- int safe /* use the sandbox */
-)
+/// Call Vim script function and return the result as a List
+///
+/// @param[in] func Function name.
+/// @param[in] argc Number of arguments.
+/// @param[in] argv Array with string arguments.
+/// @param[in] safe Use the sandbox.
+///
+/// @return [allocated] NULL when calling function fails or return tv is not a
+/// List, allocated List otherwise.
+void *call_func_retlist(char_u *func, int argc, const char_u *const *const argv,
+ int safe)
{
typval_T rettv;
@@ -1308,7 +1326,7 @@ call_func_retlist (
return NULL;
if (rettv.v_type != VAR_LIST) {
- clear_tv(&rettv);
+ tv_clear(&rettv);
return NULL;
}
@@ -1380,7 +1398,7 @@ void prof_child_exit(proftime_T *tm /* where waittime was stored */
int eval_foldexpr(char_u *arg, int *cp)
{
typval_T tv;
- int retval;
+ varnumber_T retval;
char_u *s;
int use_sandbox = was_set_insecurely((char_u *)"foldexpr",
OPT_LOCAL);
@@ -1406,14 +1424,14 @@ int eval_foldexpr(char_u *arg, int *cp)
*cp = *s++;
retval = atol((char *)s);
}
- clear_tv(&tv);
+ tv_clear(&tv);
}
--emsg_off;
if (use_sandbox)
--sandbox;
--textlock;
- return retval;
+ return (int)retval;
}
/*
@@ -1437,22 +1455,24 @@ void ex_let(exarg_T *eap)
char_u *argend;
int first = TRUE;
- argend = skip_var_list(arg, &var_count, &semicolon);
- if (argend == NULL)
+ argend = (char_u *)skip_var_list(arg, &var_count, &semicolon);
+ if (argend == NULL) {
return;
- if (argend > arg && argend[-1] == '.') /* for var.='str' */
- --argend;
+ }
+ if (argend > arg && argend[-1] == '.') { // For var.='str'.
+ argend--;
+ }
expr = skipwhite(argend);
if (*expr != '=' && !(vim_strchr((char_u *)"+-.", *expr) != NULL
&& expr[1] == '=')) {
// ":let" without "=": list variables
- if (*arg == '[')
+ if (*arg == '[') {
EMSG(_(e_invarg));
- else if (!ends_excmd(*arg))
- /* ":let var1 var2" */
- arg = list_arg_vars(eap, arg, &first);
- else if (!eap->skip) {
- /* ":let" */
+ } else if (!ends_excmd(*arg)) {
+ // ":let var1 var2"
+ arg = (char_u *)list_arg_vars(eap, (const char *)arg, &first);
+ } else if (!eap->skip) {
+ // ":let"
list_glob_vars(&first);
list_buf_vars(&first);
list_win_vars(&first);
@@ -1478,13 +1498,13 @@ void ex_let(exarg_T *eap)
++emsg_skip;
i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip);
if (eap->skip) {
- if (i != FAIL)
- clear_tv(&rettv);
- --emsg_skip;
+ if (i != FAIL) {
+ tv_clear(&rettv);
+ }
+ emsg_skip--;
} else if (i != FAIL) {
- (void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count,
- op);
- clear_tv(&rettv);
+ (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, op);
+ tv_clear(&rettv);
}
}
}
@@ -1507,7 +1527,7 @@ ex_let_vars (
char_u *nextchars
)
{
- char_u *arg = arg_start;
+ char_u *arg = arg_start;
list_T *l;
int i;
listitem_T *item;
@@ -1530,7 +1550,7 @@ ex_let_vars (
return FAIL;
}
- i = list_len(l);
+ i = tv_list_len(l);
if (semicolon == 0 && var_count < i) {
EMSG(_("E687: Less targets than List items"));
return FAIL;
@@ -1552,9 +1572,9 @@ ex_let_vars (
if (*arg == ';') {
/* Put the rest of the list (may be empty) in the var after ';'.
* Create a new list for this. */
- l = list_alloc();
+ l = tv_list_alloc();
while (item != NULL) {
- list_append_tv(l, &item->li_tv);
+ tv_list_append_tv(l, &item->li_tv);
item = item->li_next;
}
@@ -1563,11 +1583,12 @@ ex_let_vars (
ltv.vval.v_list = l;
l->lv_refcount = 1;
- arg = ex_let_one(skipwhite(arg + 1), &ltv, FALSE,
- (char_u *)"]", nextchars);
- clear_tv(&ltv);
- if (arg == NULL)
+ arg = ex_let_one(skipwhite(arg + 1), &ltv, false,
+ (char_u *)"]", nextchars);
+ tv_clear(&ltv);
+ if (arg == NULL) {
return FAIL;
+ }
break;
} else if (*arg != ',' && *arg != ']') {
EMSG2(_(e_intern2), "ex_let_vars()");
@@ -1585,9 +1606,11 @@ ex_let_vars (
* for "[var, var; var]" set "semicolon".
* Return NULL for an error.
*/
-static char_u *skip_var_list(char_u *arg, int *var_count, int *semicolon)
+static const char_u *skip_var_list(const char_u *arg, int *var_count,
+ int *semicolon)
{
- char_u *p, *s;
+ const char_u *p;
+ const char_u *s;
if (*arg == '[') {
/* "[var, var]": find the matching ']'. */
@@ -1624,7 +1647,7 @@ static char_u *skip_var_list(char_u *arg, int *var_count, int *semicolon)
* Skip one (assignable) variable name, including @r, $VAR, &option, d.key,
* l[idx].
*/
-static char_u *skip_var_one(char_u *arg)
+static const char_u *skip_var_one(const char_u *arg)
{
if (*arg == '@' && arg[1] != NUL)
return arg + 2;
@@ -1636,7 +1659,8 @@ static char_u *skip_var_one(char_u *arg)
* List variables for hashtab "ht" with prefix "prefix".
* If "empty" is TRUE also list NULL strings as empty strings.
*/
-static void list_hashtable_vars(hashtab_T *ht, char_u *prefix, int empty, int *first)
+static void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty,
+ int *first)
{
hashitem_T *hi;
dictitem_T *di;
@@ -1645,11 +1669,12 @@ static void list_hashtable_vars(hashtab_T *ht, char_u *prefix, int empty, int *f
todo = (int)ht->ht_used;
for (hi = ht->ht_array; todo > 0 && !got_int; ++hi) {
if (!HASHITEM_EMPTY(hi)) {
- --todo;
- di = HI2DI(hi);
+ todo--;
+ di = TV_DICT_HI2DI(hi);
if (empty || di->di_tv.v_type != VAR_STRING
- || di->di_tv.vval.v_string != NULL)
+ || di->di_tv.vval.v_string != NULL) {
list_one_var(di, prefix, first);
+ }
}
}
}
@@ -1659,7 +1684,7 @@ static void list_hashtable_vars(hashtab_T *ht, char_u *prefix, int empty, int *f
*/
static void list_glob_vars(int *first)
{
- list_hashtable_vars(&globvarht, (char_u *)"", TRUE, first);
+ list_hashtable_vars(&globvarht, "", true, first);
}
/*
@@ -1667,14 +1692,7 @@ static void list_glob_vars(int *first)
*/
static void list_buf_vars(int *first)
{
- char_u numbuf[NUMBUFLEN];
-
- list_hashtable_vars(&curbuf->b_vars->dv_hashtab, (char_u *)"b:",
- TRUE, first);
-
- sprintf((char *)numbuf, "%" PRId64, (int64_t)curbuf->b_changedtick);
- list_one_var_a((char_u *)"b:", (char_u *)"changedtick", VAR_NUMBER,
- numbuf, first);
+ list_hashtable_vars(&curbuf->b_vars->dv_hashtab, "b:", true, first);
}
/*
@@ -1682,8 +1700,7 @@ static void list_buf_vars(int *first)
*/
static void list_win_vars(int *first)
{
- list_hashtable_vars(&curwin->w_vars->dv_hashtab,
- (char_u *)"w:", TRUE, first);
+ list_hashtable_vars(&curwin->w_vars->dv_hashtab, "w:", true, first);
}
/*
@@ -1691,8 +1708,7 @@ static void list_win_vars(int *first)
*/
static void list_tab_vars(int *first)
{
- list_hashtable_vars(&curtab->tp_vars->dv_hashtab,
- (char_u *)"t:", TRUE, first);
+ list_hashtable_vars(&curtab->tp_vars->dv_hashtab, "t:", true, first);
}
/*
@@ -1700,7 +1716,7 @@ static void list_tab_vars(int *first)
*/
static void list_vim_vars(int *first)
{
- list_hashtable_vars(&vimvarht, (char_u *)"v:", FALSE, first);
+ list_hashtable_vars(&vimvarht, "v:", false, first);
}
/*
@@ -1708,9 +1724,9 @@ static void list_vim_vars(int *first)
*/
static void list_script_vars(int *first)
{
- if (current_SID > 0 && current_SID <= ga_scripts.ga_len)
- list_hashtable_vars(&SCRIPT_VARS(current_SID),
- (char_u *)"s:", FALSE, first);
+ if (current_SID > 0 && current_SID <= ga_scripts.ga_len) {
+ list_hashtable_vars(&SCRIPT_VARS(current_SID), "s:", false, first);
+ }
}
/*
@@ -1718,36 +1734,37 @@ static void list_script_vars(int *first)
*/
static void list_func_vars(int *first)
{
- if (current_funccal != NULL)
- list_hashtable_vars(&current_funccal->l_vars.dv_hashtab,
- (char_u *)"l:", FALSE, first);
+ if (current_funccal != NULL) {
+ list_hashtable_vars(&current_funccal->l_vars.dv_hashtab, "l:", false,
+ first);
+ }
}
/*
* List variables in "arg".
*/
-static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first)
+static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
{
int error = FALSE;
int len;
- char_u *name;
- char_u *name_start;
- char_u *arg_subsc;
- char_u *tofree;
+ const char *name;
+ const char *name_start;
typval_T tv;
while (!ends_excmd(*arg) && !got_int) {
if (error || eap->skip) {
- arg = find_name_end(arg, NULL, NULL, FNE_INCL_BR | FNE_CHECK_START);
+ arg = (const char *)find_name_end((char_u *)arg, NULL, NULL,
+ FNE_INCL_BR | FNE_CHECK_START);
if (!ascii_iswhite(*arg) && !ends_excmd(*arg)) {
emsg_severe = TRUE;
EMSG(_(e_trailing));
break;
}
} else {
- /* get_name_len() takes care of expanding curly braces */
+ // get_name_len() takes care of expanding curly braces
name_start = name = arg;
- len = get_name_len(&arg, &tofree, TRUE, TRUE);
+ char *tofree;
+ len = get_name_len(&arg, &tofree, true, true);
if (len <= 0) {
/* This is mainly to keep test 49 working: when expanding
* curly braces fails overrule the exception error message. */
@@ -1761,14 +1778,15 @@ static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first)
if (tofree != NULL) {
name = tofree;
}
- if (get_var_tv(name, len, &tv, NULL, true, false) == FAIL) {
+ if (get_var_tv((const char *)name, len, &tv, NULL, true, false)
+ == FAIL) {
error = true;
} else {
// handle d.key, l[idx], f(expr)
- arg_subsc = arg;
- if (handle_subscript(&arg, &tv, TRUE, TRUE) == FAIL)
- error = TRUE;
- else {
+ const char *const arg_subsc = arg;
+ if (handle_subscript(&arg, &tv, true, true) == FAIL) {
+ error = true;
+ } else {
if (arg == arg_subsc && len == 2 && name[1] == ':') {
switch (*name) {
case 'g': list_glob_vars(first); break;
@@ -1782,20 +1800,18 @@ static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first)
EMSG2(_("E738: Can't list variables for %s"), name);
}
} else {
- int c;
-
- char_u *s = (char_u *) encode_tv2echo(&tv, NULL);
- c = *arg;
- *arg = NUL;
- list_one_var_a((char_u *)"",
- arg == arg_subsc ? name : name_start,
- tv.v_type,
- s == NULL ? (char_u *)"" : s,
- first);
- *arg = c;
+ char *const s = encode_tv2echo(&tv, NULL);
+ const char *const used_name = (arg == arg_subsc
+ ? name
+ : name_start);
+ const ptrdiff_t name_size = (used_name == tofree
+ ? (ptrdiff_t)strlen(used_name)
+ : (arg - used_name));
+ list_one_var_a("", used_name, name_size,
+ tv.v_type, s == NULL ? "" : s, first);
xfree(s);
}
- clear_tv(&tv);
+ tv_clear(&tv);
}
}
}
@@ -1803,152 +1819,153 @@ static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first)
xfree(tofree);
}
- arg = skipwhite(arg);
+ arg = (const char *)skipwhite((const char_u *)arg);
}
return arg;
}
-/*
- * Set one item of ":let var = expr" or ":let [v1, v2] = list" to its value.
- * Returns a pointer to the char just after the var name.
- * Returns NULL if there is an error.
- */
-static char_u *
-ex_let_one (
- char_u *arg, /* points to variable name */
- typval_T *tv, /* value to assign to variable */
- int copy, /* copy value from "tv" */
- char_u *endchars, /* valid chars after variable name or NULL */
- char_u *op /* "+", "-", "." or NULL*/
-)
-{
- int c1;
- char_u *name;
- char_u *p;
- char_u *arg_end = NULL;
+// TODO(ZyX-I): move to eval/ex_cmds
+
+/// Set one item of `:let var = expr` or `:let [v1, v2] = list` to its value
+///
+/// @param[in] arg Start of the variable name.
+/// @param[in] tv Value to assign to the variable.
+/// @param[in] copy If true, copy value from `tv`.
+/// @param[in] endchars Valid characters after variable name or NULL.
+/// @param[in] op Operation performed: *op is `+`, `-`, `.` for `+=`, etc.
+/// NULL for `=`.
+///
+/// @return a pointer to the char just after the var name or NULL in case of
+/// error.
+static char_u *ex_let_one(char_u *arg, typval_T *const tv,
+ const bool copy, const char_u *const endchars,
+ const char_u *const op)
+ FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ char_u *arg_end = NULL;
int len;
int opt_flags;
- char_u *tofree = NULL;
+ char_u *tofree = NULL;
/*
* ":let $VAR = expr": Set environment variable.
*/
if (*arg == '$') {
- /* Find the end of the name. */
- ++arg;
- name = arg;
- len = get_env_len(&arg);
- if (len == 0)
+ // Find the end of the name.
+ arg++;
+ char *name = (char *)arg;
+ len = get_env_len((const char_u **)&arg);
+ if (len == 0) {
EMSG2(_(e_invarg2), name - 1);
- else {
- if (op != NULL && (*op == '+' || *op == '-'))
+ } else {
+ if (op != NULL && (*op == '+' || *op == '-')) {
EMSG2(_(e_letwrong), op);
- else if (endchars != NULL
- && vim_strchr(endchars, *skipwhite(arg)) == NULL)
+ } else if (endchars != NULL
+ && vim_strchr(endchars, *skipwhite(arg)) == NULL) {
EMSG(_(e_letunexp));
- else if (!check_secure()) {
- c1 = name[len];
+ } else if (!check_secure()) {
+ const char c1 = name[len];
name[len] = NUL;
- p = get_tv_string_chk(tv);
+ const char *p = tv_get_string_chk(tv);
if (p != NULL && op != NULL && *op == '.') {
- char *s = vim_getenv((char *)name);
+ char *s = vim_getenv(name);
if (s != NULL) {
- p = tofree = concat_str((char_u *)s, p);
+ tofree = concat_str((const char_u *)s, (const char_u *)p);
+ p = (const char *)tofree;
xfree(s);
}
}
if (p != NULL) {
- vim_setenv((char *)name, (char *)p);
- if (STRICMP(name, "HOME") == 0)
+ vim_setenv(name, p);
+ if (STRICMP(name, "HOME") == 0) {
init_homedir();
- else if (didset_vim && STRICMP(name, "VIM") == 0)
- didset_vim = FALSE;
- else if (didset_vimruntime
- && STRICMP(name, "VIMRUNTIME") == 0)
- didset_vimruntime = FALSE;
+ } else if (didset_vim && STRICMP(name, "VIM") == 0) {
+ didset_vim = false;
+ } else if (didset_vimruntime
+ && STRICMP(name, "VIMRUNTIME") == 0) {
+ didset_vimruntime = false;
+ }
arg_end = arg;
}
name[len] = c1;
xfree(tofree);
}
}
- }
- /*
- * ":let &option = expr": Set option value.
- * ":let &l:option = expr": Set local option value.
- * ":let &g:option = expr": Set global option value.
- */
- else if (*arg == '&') {
- /* Find the end of the name. */
- p = find_option_end(&arg, &opt_flags);
- if (p == NULL || (endchars != NULL
- && vim_strchr(endchars, *skipwhite(p)) == NULL))
+ // ":let &option = expr": Set option value.
+ // ":let &l:option = expr": Set local option value.
+ // ":let &g:option = expr": Set global option value.
+ } else if (*arg == '&') {
+ // Find the end of the name.
+ char *const p = (char *)find_option_end((const char **)&arg, &opt_flags);
+ if (p == NULL
+ || (endchars != NULL
+ && vim_strchr(endchars, *skipwhite((const char_u *)p)) == NULL)) {
EMSG(_(e_letunexp));
- else {
- long n;
+ } else {
int opt_type;
long numval;
- char_u *stringval = NULL;
- char_u *s;
+ char *stringval = NULL;
- c1 = *p;
+ const char c1 = *p;
*p = NUL;
- n = get_tv_number(tv);
- s = get_tv_string_chk(tv); /* != NULL if number or string */
+ varnumber_T n = tv_get_number(tv);
+ const char *s = tv_get_string_chk(tv); // != NULL if number or string.
if (s != NULL && op != NULL && *op != '=') {
- opt_type = get_option_value(arg, &numval,
- &stringval, opt_flags);
+ opt_type = get_option_value(arg, &numval, (char_u **)&stringval,
+ opt_flags);
if ((opt_type == 1 && *op == '.')
- || (opt_type == 0 && *op != '.'))
+ || (opt_type == 0 && *op != '.')) {
EMSG2(_(e_letwrong), op);
- else {
- if (opt_type == 1) { /* number */
- if (*op == '+')
+ } else {
+ if (opt_type == 1) { // number
+ if (*op == '+') {
n = numval + n;
- else
+ } else {
n = numval - n;
- } else if (opt_type == 0 && stringval != NULL) { /* string */
- s = concat_str(stringval, s);
- xfree(stringval);
- stringval = s;
+ }
+ } else if (opt_type == 0 && stringval != NULL) { // string
+ char *const oldstringval = stringval;
+ stringval = (char *)concat_str((const char_u *)stringval,
+ (const char_u *)s);
+ xfree(oldstringval);
+ s = stringval;
}
}
}
if (s != NULL) {
- set_option_value(arg, n, s, opt_flags);
- arg_end = p;
+ set_option_value((const char *)arg, n, s, opt_flags);
+ arg_end = (char_u *)p;
}
*p = c1;
xfree(stringval);
}
- }
- /*
- * ":let @r = expr": Set register contents.
- */
- else if (*arg == '@') {
- ++arg;
- if (op != NULL && (*op == '+' || *op == '-'))
- EMSG2(_(e_letwrong), op);
- else if (endchars != NULL
- && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL)
- EMSG(_(e_letunexp));
- else {
- char_u *ptofree = NULL;
+ // ":let @r = expr": Set register contents.
+ } else if (*arg == '@') {
+ arg++;
+ if (op != NULL && (*op == '+' || *op == '-')) {
+ emsgf(_(e_letwrong), op);
+ } else if (endchars != NULL
+ && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) {
+ emsgf(_(e_letunexp));
+ } else {
char_u *s;
- p = get_tv_string_chk(tv);
+ char_u *ptofree = NULL;
+ const char *p = tv_get_string_chk(tv);
if (p != NULL && op != NULL && *op == '.') {
s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc);
if (s != NULL) {
- p = ptofree = concat_str(s, p);
+ ptofree = concat_str(s, (const char_u *)p);
+ p = (const char *)ptofree;
xfree(s);
}
}
if (p != NULL) {
- write_reg_contents(*arg == '@' ? '"' : *arg, p, STRLEN(p), false);
+ write_reg_contents(*arg == '@' ? '"' : *arg,
+ (const char_u *)p, STRLEN(p), false);
arg_end = arg + 1;
}
xfree(ptofree);
@@ -1961,11 +1978,11 @@ ex_let_one (
else if (eval_isnamec1(*arg) || *arg == '{') {
lval_T lv;
- p = get_lval(arg, tv, &lv, FALSE, FALSE, 0, FNE_CHECK_START);
+ char_u *const p = get_lval(arg, tv, &lv, false, false, 0, FNE_CHECK_START);
if (p != NULL && lv.ll_name != NULL) {
- if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL)
+ if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) {
EMSG(_(e_letunexp));
- else {
+ } else {
set_var_lval(&lv, p, tv, copy, op);
arg_end = p;
}
@@ -1977,57 +1994,41 @@ ex_let_one (
return arg_end;
}
-/*
- * If "arg" is equal to "b:changedtick" give an error and return TRUE.
- */
-static int check_changedtick(char_u *arg)
-{
- if (STRNCMP(arg, "b:changedtick", 13) == 0 && !eval_isnamec(arg[13])) {
- EMSG2(_(e_readonlyvar), arg);
- return TRUE;
- }
- return FALSE;
-}
+// TODO(ZyX-I): move to eval/executor
-/*
- * Get an lval: variable, Dict item or List item that can be assigned a value
- * to: "name", "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]",
- * "name.key", "name.key[expr]" etc.
- * Indexing only works if "name" is an existing List or Dictionary.
- * "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_NO_AUTOLOAD: do not use script autoloading
- *
- * Returns a pointer to just after the name, including indexes.
- * When an evaluation error occurs "lp->ll_name" is NULL;
- * Returns NULL for a parsing error. Still need to free items in "lp"!
- */
-static char_u *
-get_lval (
- char_u *name,
- typval_T *rettv,
- lval_T *lp,
- int unlet,
- int skip,
- int flags, /* GLV_ values */
- int fne_flags /* flags for find_name_end() */
-)
+/// Get an lvalue
+///
+/// Lvalue may be
+/// - variable: "name", "na{me}"
+/// - dictionary item: "dict.key", "dict['key']"
+/// - list item: "list[expr]"
+/// - list slice: "list[expr:expr]"
+///
+/// Indexing only works if trying to use it with an existing List or Dictionary.
+///
+/// @param[in] name Name to parse.
+/// @param rettv Pointer to the value to be assigned or NULL.
+/// @param[out] lp Lvalue definition. When evaluation errors occur `->ll_name`
+/// is NULL.
+/// @param[in] unlet True if using `:unlet`. This results in slightly
+/// different behaviour when something is wrong; must end in
+/// space or cmd separator.
+/// @param[in] skip True when skipping.
+/// @param[in] flags @see GetLvalFlags.
+/// @param[in] fne_flags Flags for find_name_end().
+///
+/// @return A pointer to just after the name, including indexes. Returns NULL
+/// for a parsing error, but it is still needed to free items in lp.
+static char_u *get_lval(char_u *const name, typval_T *const rettv,
+ lval_T *const lp, const bool unlet, const bool skip,
+ const int flags, const int fne_flags)
+ FUNC_ATTR_NONNULL_ARG(1, 3)
{
- char_u *p;
- char_u *expr_start, *expr_end;
- int cc;
dictitem_T *v;
typval_T var1;
typval_T var2;
int empty1 = FALSE;
listitem_T *ni;
- char_u *key = NULL;
- int len;
hashtab_T *ht;
int quiet = flags & GLV_QUIET;
@@ -2035,13 +2036,19 @@ get_lval (
memset(lp, 0, sizeof(lval_T));
if (skip) {
- /* When skipping just find the end of the name. */
- lp->ll_name = name;
- return find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags);
+ // When skipping just find the end of the name.
+ lp->ll_name = (const char *)name;
+ return (char_u *)find_name_end((const char_u *)name, NULL, NULL,
+ FNE_INCL_BR | fne_flags);
}
- /* Find the end of the name. */
- p = find_name_end(name, &expr_start, &expr_end, fne_flags);
+ // Find the end of the name.
+ char_u *expr_start;
+ char_u *expr_end;
+ char_u *p = (char_u *)find_name_end(name,
+ (const char_u **)&expr_start,
+ (const char_u **)&expr_end,
+ fne_flags);
if (expr_start != NULL) {
/* Don't expand the name when we already know there is an error. */
if (unlet && !ascii_iswhite(*p) && !ends_excmd(*p)
@@ -2050,7 +2057,9 @@ get_lval (
return NULL;
}
- lp->ll_exp_name = make_expanded_name(name, expr_start, expr_end, p);
+ lp->ll_exp_name = (char *)make_expanded_name(name, expr_start, expr_end,
+ (char_u *)p);
+ lp->ll_name = lp->ll_exp_name;
if (lp->ll_exp_name == NULL) {
/* Report an invalid expression in braces, unless the
* expression evaluation has been cancelled due to an
@@ -2060,23 +2069,28 @@ get_lval (
EMSG2(_(e_invarg2), name);
return NULL;
}
+ lp->ll_name_len = 0;
+ } else {
+ lp->ll_name_len = strlen(lp->ll_name);
}
- lp->ll_name = lp->ll_exp_name;
- } else
- lp->ll_name = name;
+ } else {
+ lp->ll_name = (const char *)name;
+ lp->ll_name_len = (size_t)((const char *)p - lp->ll_name);
+ }
- /* Without [idx] or .key we are done. */
- if ((*p != '[' && *p != '.') || lp->ll_name == NULL)
+ // Without [idx] or .key we are done.
+ if ((*p != '[' && *p != '.') || lp->ll_name == NULL) {
return p;
+ }
- cc = *p;
- *p = NUL;
- v = find_var(lp->ll_name, &ht, flags & GLV_NO_AUTOLOAD);
- if (v == NULL && !quiet)
- EMSG2(_(e_undefvar), lp->ll_name);
- *p = cc;
- if (v == NULL)
+ v = find_var(lp->ll_name, lp->ll_name_len, &ht, flags & GLV_NO_AUTOLOAD);
+ if (v == NULL && !quiet) {
+ emsgf(_("E121: Undefined variable: %.*s"),
+ (int)lp->ll_name_len, lp->ll_name);
+ }
+ if (v == NULL) {
return NULL;
+ }
/*
* Loop until no more [idx] or .key is following.
@@ -2096,29 +2110,32 @@ get_lval (
return NULL;
}
- len = -1;
+ int len = -1;
+ char_u *key = NULL;
if (*p == '.') {
key = p + 1;
- for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len)
- ;
+ for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) {
+ }
if (len == 0) {
- if (!quiet)
- EMSG(_(e_emptykey));
+ if (!quiet) {
+ EMSG(_("E713: Cannot use empty key after ."));
+ }
return NULL;
}
p = key + len;
} else {
/* Get the index [expr] or the first index [expr: ]. */
p = skipwhite(p + 1);
- if (*p == ':')
- empty1 = TRUE;
- else {
- empty1 = FALSE;
- if (eval1(&p, &var1, TRUE) == FAIL) /* recursive! */
+ if (*p == ':') {
+ empty1 = true;
+ } else {
+ empty1 = false;
+ if (eval1(&p, &var1, true) == FAIL) { // Recursive!
return NULL;
- if (get_tv_string_chk(&var1) == NULL) {
- /* not a number or string */
- clear_tv(&var1);
+ }
+ if (!tv_check_str(&var1)) {
+ // Not a number or string.
+ tv_clear(&var1);
return NULL;
}
}
@@ -2126,35 +2143,41 @@ get_lval (
/* Optionally get the second index [ :expr]. */
if (*p == ':') {
if (lp->ll_tv->v_type == VAR_DICT) {
- if (!quiet)
+ if (!quiet) {
EMSG(_(e_dictrange));
- if (!empty1)
- clear_tv(&var1);
+ }
+ if (!empty1) {
+ tv_clear(&var1);
+ }
return NULL;
}
if (rettv != NULL && (rettv->v_type != VAR_LIST
|| rettv->vval.v_list == NULL)) {
- if (!quiet)
- EMSG(_("E709: [:] requires a List value"));
- if (!empty1)
- clear_tv(&var1);
+ if (!quiet) {
+ emsgf(_("E709: [:] requires a List value"));
+ }
+ if (!empty1) {
+ tv_clear(&var1);
+ }
return NULL;
}
p = skipwhite(p + 1);
- if (*p == ']')
- lp->ll_empty2 = TRUE;
- else {
- lp->ll_empty2 = FALSE;
- if (eval1(&p, &var2, TRUE) == FAIL) { /* recursive! */
- if (!empty1)
- clear_tv(&var1);
+ if (*p == ']') {
+ lp->ll_empty2 = true;
+ } else {
+ lp->ll_empty2 = false;
+ if (eval1(&p, &var2, true) == FAIL) { // Recursive!
+ if (!empty1) {
+ tv_clear(&var1);
+ }
return NULL;
}
- if (get_tv_string_chk(&var2) == NULL) {
- /* not a number or string */
- if (!empty1)
- clear_tv(&var1);
- clear_tv(&var2);
+ if (!tv_check_str(&var2)) {
+ // Not a number or string.
+ if (!empty1) {
+ tv_clear(&var1);
+ }
+ tv_clear(&var2);
return NULL;
}
}
@@ -2163,12 +2186,15 @@ get_lval (
lp->ll_range = FALSE;
if (*p != ']') {
- if (!quiet)
- EMSG(_(e_missbrac));
- if (!empty1)
- clear_tv(&var1);
- if (lp->ll_range && !lp->ll_empty2)
- clear_tv(&var2);
+ if (!quiet) {
+ emsgf(_(e_missbrac));
+ }
+ if (!empty1) {
+ tv_clear(&var1);
+ }
+ if (lp->ll_range && !lp->ll_empty2) {
+ tv_clear(&var2);
+ }
return NULL;
}
@@ -2179,15 +2205,15 @@ get_lval (
if (lp->ll_tv->v_type == VAR_DICT) {
if (len == -1) {
// "[key]": get key from "var1"
- key = get_tv_string_chk(&var1); // is number or string
+ key = (char_u *)tv_get_string(&var1); // is number or string
if (key == NULL) {
- clear_tv(&var1);
+ tv_clear(&var1);
return NULL;
}
}
lp->ll_list = NULL;
lp->ll_dict = lp->ll_tv->vval.v_dict;
- lp->ll_di = dict_find(lp->ll_dict, key, len);
+ lp->ll_di = tv_dict_find(lp->ll_dict, (const char *)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
@@ -2199,16 +2225,19 @@ get_lval (
if (len != -1) {
prevval = key[len];
key[len] = NUL;
- } else
- prevval = 0; /* avoid compiler warning */
- wrong = (lp->ll_dict->dv_scope == VAR_DEF_SCOPE
- && rettv->v_type == VAR_FUNC
- && var_check_func_name(key, lp->ll_di == NULL))
- || !valid_varname(key);
- if (len != -1)
+ } else {
+ prevval = 0; // Avoid compiler warning.
+ }
+ wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE
+ && tv_is_func(*rettv)
+ && !var_check_func_name((const char *)key, lp->ll_di == NULL))
+ || !valid_varname((const char *)key));
+ if (len != -1) {
key[len] = prevval;
- if (wrong)
+ }
+ if (wrong) {
return NULL;
+ }
}
if (lp->ll_di == NULL) {
@@ -2220,51 +2249,61 @@ get_lval (
/* Key does not exist in dict: may need to add it. */
if (*p == '[' || *p == '.' || unlet) {
- if (!quiet)
- EMSG2(_(e_dictkey), key);
- if (len == -1)
- clear_tv(&var1);
+ if (!quiet) {
+ emsgf(_(e_dictkey), key);
+ }
+ if (len == -1) {
+ tv_clear(&var1);
+ }
return NULL;
}
- if (len == -1)
+ if (len == -1) {
lp->ll_newkey = vim_strsave(key);
- else
+ } else {
lp->ll_newkey = vim_strnsave(key, len);
- if (len == -1)
- clear_tv(&var1);
+ }
+ if (len == -1) {
+ tv_clear(&var1);
+ }
break;
- } else if (var_check_ro(lp->ll_di->di_flags, name, false)) {
- // existing variable, need to check if it can be changed
+ // existing variable, need to check if it can be changed
+ } else if (!(flags & GLV_READ_ONLY) && var_check_ro(lp->ll_di->di_flags,
+ (const char *)name,
+ (size_t)(p - name))) {
+ if (len == -1) {
+ tv_clear(&var1);
+ }
return NULL;
}
- if (len == -1)
- clear_tv(&var1);
+ if (len == -1) {
+ tv_clear(&var1);
+ }
lp->ll_tv = &lp->ll_di->di_tv;
} else {
- /*
- * Get the number and item for the only or first index of the List.
- */
- if (empty1)
+ // Get the number and item for the only or first index of the List.
+ if (empty1) {
lp->ll_n1 = 0;
- else {
- lp->ll_n1 = get_tv_number(&var1); /* is number or string */
- clear_tv(&var1);
+ } else {
+ lp->ll_n1 = (long)tv_get_number(&var1); // Is number or string.
+ tv_clear(&var1);
}
lp->ll_dict = NULL;
lp->ll_list = lp->ll_tv->vval.v_list;
- lp->ll_li = list_find(lp->ll_list, lp->ll_n1);
+ lp->ll_li = tv_list_find(lp->ll_list, lp->ll_n1);
if (lp->ll_li == NULL) {
if (lp->ll_n1 < 0) {
lp->ll_n1 = 0;
- lp->ll_li = list_find(lp->ll_list, lp->ll_n1);
+ lp->ll_li = tv_list_find(lp->ll_list, lp->ll_n1);
}
}
if (lp->ll_li == NULL) {
- if (lp->ll_range && !lp->ll_empty2)
- clear_tv(&var2);
- if (!quiet)
+ if (lp->ll_range && !lp->ll_empty2) {
+ tv_clear(&var2);
+ }
+ if (!quiet) {
EMSGN(_(e_listidx), lp->ll_n1);
+ }
return NULL;
}
@@ -2275,24 +2314,26 @@ get_lval (
* Otherwise "lp->ll_n2" is set to the second index.
*/
if (lp->ll_range && !lp->ll_empty2) {
- lp->ll_n2 = get_tv_number(&var2); /* is number or string */
- clear_tv(&var2);
+ lp->ll_n2 = (long)tv_get_number(&var2); // Is number or string.
+ tv_clear(&var2);
if (lp->ll_n2 < 0) {
- ni = list_find(lp->ll_list, lp->ll_n2);
+ ni = tv_list_find(lp->ll_list, lp->ll_n2);
if (ni == NULL) {
if (!quiet)
EMSGN(_(e_listidx), lp->ll_n2);
return NULL;
}
- lp->ll_n2 = list_idx_of_item(lp->ll_list, ni);
+ 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 = list_idx_of_item(lp->ll_list, lp->ll_li);
+ // 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)
+ if (!quiet) {
EMSGN(_(e_listidx), lp->ll_n2);
+ }
return NULL;
}
}
@@ -2304,6 +2345,8 @@ get_lval (
return p;
}
+// TODO(ZyX-I): move to eval/executor
+
/*
* Clear lval "lp" that was filled by get_lval().
*/
@@ -2313,44 +2356,48 @@ static void clear_lval(lval_T *lp)
xfree(lp->ll_newkey);
}
+// TODO(ZyX-I): move to eval/executor
+
/*
* Set a variable that was parsed by get_lval() to "rettv".
* "endp" points to just after the parsed name.
* "op" is NULL, "+" for "+=", "-" for "-=", "." for ".=" or "=" for "=".
*/
-static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, char_u *op)
+static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
+ int copy, const char_u *op)
{
int cc;
listitem_T *ri;
dictitem_T *di;
if (lp->ll_tv == NULL) {
- if (!check_changedtick(lp->ll_name)) {
- cc = *endp;
- *endp = NUL;
- if (op != NULL && *op != '=') {
- typval_T tv;
-
- // handle +=, -= and .=
- di = NULL;
- if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name),
- &tv, &di, true, false) == OK) {
- if ((di == NULL
- || (!var_check_ro(di->di_flags, lp->ll_name, false)
- && !tv_check_lock(di->di_tv.v_lock, lp->ll_name, false)))
- && tv_op(&tv, rettv, op) == OK) {
- set_var(lp->ll_name, &tv, false);
- }
- clear_tv(&tv);
+ cc = *endp;
+ *endp = NUL;
+ if (op != NULL && *op != '=') {
+ typval_T tv;
+
+ // handle +=, -= and .=
+ di = NULL;
+ if (get_var_tv((const char *)lp->ll_name, (int)STRLEN(lp->ll_name),
+ &tv, &di, true, false) == OK) {
+ if ((di == NULL
+ || (!var_check_ro(di->di_flags, (const char *)lp->ll_name,
+ TV_CSTRING)
+ && !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name,
+ TV_CSTRING)))
+ && eexe_mod_op(&tv, rettv, (const char *)op) == OK) {
+ set_var(lp->ll_name, lp->ll_name_len, &tv, false);
}
- } else
- set_var(lp->ll_name, rettv, copy);
- *endp = cc;
+ tv_clear(&tv);
+ }
+ } else {
+ set_var(lp->ll_name, lp->ll_name_len, rettv, copy);
}
+ *endp = cc;
} else if (tv_check_lock(lp->ll_newkey == NULL
? lp->ll_tv->v_lock
: lp->ll_tv->vval.v_dict->dv_lock,
- lp->ll_name, false)) {
+ (const char *)lp->ll_name, TV_CSTRING)) {
} else if (lp->ll_range) {
listitem_T *ll_li = lp->ll_li;
int ll_n1 = lp->ll_n1;
@@ -2358,7 +2405,8 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch
// Check whether any of the list items is locked
for (listitem_T *ri = rettv->vval.v_list->lv_first;
ri != NULL && ll_li != NULL; ) {
- if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name, false)) {
+ if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name,
+ TV_CSTRING)) {
return;
}
ri = ri->li_next;
@@ -2373,18 +2421,18 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch
* Assign the List values to the list items.
*/
for (ri = rettv->vval.v_list->lv_first; ri != NULL; ) {
- if (op != NULL && *op != '=')
- tv_op(&lp->ll_li->li_tv, &ri->li_tv, op);
- else {
- clear_tv(&lp->ll_li->li_tv);
- copy_tv(&ri->li_tv, &lp->ll_li->li_tv);
+ if (op != NULL && *op != '=') {
+ eexe_mod_op(&lp->ll_li->li_tv, &ri->li_tv, (const char *)op);
+ } else {
+ tv_clear(&lp->ll_li->li_tv);
+ tv_copy(&ri->li_tv, &lp->ll_li->li_tv);
}
ri = ri->li_next;
if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1))
break;
if (lp->ll_li->li_next == NULL) {
- /* Need to add an empty item. */
- list_append_number(lp->ll_list, 0);
+ // Need to add an empty item.
+ tv_list_append_number(lp->ll_list, 0);
assert(lp->ll_li->li_next);
}
lp->ll_li = lp->ll_li->li_next;
@@ -2397,198 +2445,60 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch
: lp->ll_n1 != lp->ll_n2)
EMSG(_("E711: List value has not enough items"));
} else {
- typval_T oldtv;
+ typval_T oldtv = TV_INITIAL_VALUE;
dict_T *dict = lp->ll_dict;
- bool watched = is_watched(dict);
+ bool watched = tv_dict_is_watched(dict);
- if (watched) {
- init_tv(&oldtv);
- }
-
- /*
- * Assign to a List or Dictionary item.
- */
+ // Assign to a List or Dictionary item.
if (lp->ll_newkey != NULL) {
if (op != NULL && *op != '=') {
EMSG2(_(e_letwrong), op);
return;
}
- /* Need to add an item to the Dictionary. */
- di = dictitem_alloc(lp->ll_newkey);
- if (dict_add(lp->ll_tv->vval.v_dict, di) == FAIL) {
+ // Need to add an item to the Dictionary.
+ di = tv_dict_item_alloc((const char *)lp->ll_newkey);
+ if (tv_dict_add(lp->ll_tv->vval.v_dict, di) == FAIL) {
xfree(di);
return;
}
lp->ll_tv = &di->di_tv;
} else {
if (watched) {
- copy_tv(lp->ll_tv, &oldtv);
+ tv_copy(lp->ll_tv, &oldtv);
}
if (op != NULL && *op != '=') {
- tv_op(lp->ll_tv, rettv, op);
+ eexe_mod_op(lp->ll_tv, rettv, (const char *)op);
goto notify;
} else {
- clear_tv(lp->ll_tv);
+ tv_clear(lp->ll_tv);
}
}
// Assign the value to the variable or list item.
if (copy) {
- copy_tv(rettv, lp->ll_tv);
+ tv_copy(rettv, lp->ll_tv);
} else {
*lp->ll_tv = *rettv;
lp->ll_tv->v_lock = 0;
- init_tv(rettv);
+ tv_init(rettv);
}
notify:
if (watched) {
if (oldtv.v_type == VAR_UNKNOWN) {
- dictwatcher_notify(dict, (char *)lp->ll_newkey, lp->ll_tv, NULL);
+ tv_dict_watcher_notify(dict, (char *)lp->ll_newkey, lp->ll_tv, NULL);
} else {
dictitem_T *di = lp->ll_di;
- dictwatcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv);
- clear_tv(&oldtv);
- }
- }
- }
-}
-
-/*
- * Handle "tv1 += tv2", "tv1 -= tv2" and "tv1 .= tv2"
- * Returns OK or FAIL.
- */
-static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
-{
- long n;
- char_u numbuf[NUMBUFLEN];
- char_u *s;
-
- // Can't do anything with a Funcref, a Dict or special value on the right.
- if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) {
- switch (tv1->v_type) {
- case VAR_DICT:
- case VAR_FUNC:
- case VAR_PARTIAL:
- case VAR_SPECIAL:
- break;
-
- case VAR_LIST:
- if (*op != '+' || tv2->v_type != VAR_LIST)
- break;
- /* List += List */
- if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL)
- list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL);
- return OK;
-
- case VAR_NUMBER:
- case VAR_STRING:
- if (tv2->v_type == VAR_LIST)
- break;
- if (*op == '+' || *op == '-') {
- /* nr += nr or nr -= nr*/
- n = get_tv_number(tv1);
- if (tv2->v_type == VAR_FLOAT) {
- float_T f = n;
-
- if (*op == '+')
- f += tv2->vval.v_float;
- else
- f -= tv2->vval.v_float;
- clear_tv(tv1);
- tv1->v_type = VAR_FLOAT;
- tv1->vval.v_float = f;
- } else {
- if (*op == '+')
- n += get_tv_number(tv2);
- else
- n -= get_tv_number(tv2);
- clear_tv(tv1);
- tv1->v_type = VAR_NUMBER;
- tv1->vval.v_number = n;
- }
- } else {
- if (tv2->v_type == VAR_FLOAT)
- break;
-
- /* str .= str */
- s = get_tv_string(tv1);
- s = concat_str(s, get_tv_string_buf(tv2, numbuf));
- clear_tv(tv1);
- tv1->v_type = VAR_STRING;
- tv1->vval.v_string = s;
+ tv_dict_watcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv);
+ tv_clear(&oldtv);
}
- return OK;
-
- case VAR_FLOAT:
- {
- float_T f;
-
- if (*op == '.' || (tv2->v_type != VAR_FLOAT
- && tv2->v_type != VAR_NUMBER
- && tv2->v_type != VAR_STRING))
- break;
- if (tv2->v_type == VAR_FLOAT)
- f = tv2->vval.v_float;
- else
- f = get_tv_number(tv2);
- if (*op == '+')
- tv1->vval.v_float += f;
- else
- tv1->vval.v_float -= f;
- }
- return OK;
-
- case VAR_UNKNOWN:
- assert(false);
}
}
-
- EMSG2(_(e_letwrong), op);
- return FAIL;
}
-/*
- * Add a watcher to a list.
- */
-void list_add_watch(list_T *l, listwatch_T *lw)
-{
- lw->lw_next = l->lv_watch;
- l->lv_watch = lw;
-}
-
-/*
- * Remove a watcher from a list.
- * No warning when it isn't found...
- */
-void list_rem_watch(list_T *l, listwatch_T *lwrem)
-{
- listwatch_T *lw, **lwp;
-
- lwp = &l->lv_watch;
- for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next) {
- if (lw == lwrem) {
- *lwp = lw->lw_next;
- break;
- }
- lwp = &lw->lw_next;
- }
-}
-
-/*
- * Just before removing an item from a list: advance watchers to the next
- * item.
- */
-static void list_fix_watch(list_T *l, listitem_T *item)
-{
- listwatch_T *lw;
-
- for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next)
- if (lw->lw_item == item)
- lw->lw_item = item->li_next;
-}
+// TODO(ZyX-I): move to eval/ex_cmds
/*
* Evaluate the expression used in a ":for var in expr" command.
@@ -2596,14 +2506,14 @@ static void list_fix_watch(list_T *l, listitem_T *item)
* Set "*errp" to TRUE for an error, FALSE otherwise;
* Return a pointer that holds the info. Null when there is an error.
*/
-void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip)
+void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip)
{
forinfo_T *fi = xcalloc(1, sizeof(forinfo_T));
- char_u *expr;
+ const char_u *expr;
typval_T tv;
list_T *l;
- *errp = TRUE; /* default: there is an error */
+ *errp = true; // Default: there is an error.
expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon);
if (expr == NULL)
@@ -2618,20 +2528,20 @@ void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip)
if (skip)
++emsg_skip;
if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK) {
- *errp = FALSE;
+ *errp = false;
if (!skip) {
l = tv.vval.v_list;
if (tv.v_type != VAR_LIST) {
EMSG(_(e_listreq));
- clear_tv(&tv);
+ tv_clear(&tv);
} else if (l == NULL) {
// a null list is like an empty list: do nothing
- clear_tv(&tv);
+ tv_clear(&tv);
} else {
/* No need to increment the refcount, it's already set for the
* list being used in "tv". */
fi->fi_list = l;
- list_add_watch(l, &fi->fi_lw);
+ tv_list_watch_add(l, &fi->fi_lw);
fi->fi_lw.lw_item = l->lv_first;
}
}
@@ -2642,6 +2552,8 @@ void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip)
return fi;
}
+// TODO(ZyX-I): move to eval/ex_cmds
+
/*
* Use the first item in a ":for" list. Advance to the next.
* Assign the values to the variable (list). "arg" points to the first one.
@@ -2665,6 +2577,8 @@ int next_for_item(void *fi_void, char_u *arg)
return result;
}
+// TODO(ZyX-I): move to eval/ex_cmds
+
/*
* Free the structure used to store info used by ":for".
*/
@@ -2673,8 +2587,8 @@ void free_for_info(void *fi_void)
forinfo_T *fi = (forinfo_T *)fi_void;
if (fi != NULL && fi->fi_list != NULL) {
- list_rem_watch(fi->fi_list, &fi->fi_lw);
- list_unref(fi->fi_list);
+ tv_list_watch_remove(fi->fi_list, &fi->fi_lw);
+ tv_list_unref(fi->fi_list);
}
xfree(fi);
}
@@ -2762,6 +2676,7 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx)
xp->xp_pattern = arg;
}
+// TODO(ZyX-I): move to eval/ex_cmds
/*
* ":1,25call func(arg1, arg2)" function call.
@@ -2781,13 +2696,14 @@ void ex_call(exarg_T *eap)
partial_T *partial = NULL;
if (eap->skip) {
- /* trans_function_name() doesn't work well when skipping, use eval0()
- * instead to skip to any following command, e.g. for:
- * :if 0 | call dict.foo().bar() | endif */
- ++emsg_skip;
- if (eval0(eap->arg, &rettv, &eap->nextcmd, FALSE) != FAIL)
- clear_tv(&rettv);
- --emsg_skip;
+ // trans_function_name() doesn't work well when skipping, use eval0()
+ // instead to skip to any following command, e.g. for:
+ // :if 0 | call dict.foo().bar() | endif.
+ emsg_skip++;
+ if (eval0(eap->arg, &rettv, &eap->nextcmd, false) != FAIL) {
+ tv_clear(&rettv);
+ }
+ emsg_skip--;
return;
}
@@ -2809,13 +2725,13 @@ void ex_call(exarg_T *eap)
// contents. For VAR_PARTIAL get its partial, unless we already have one
// from trans_function_name().
len = (int)STRLEN(tofree);
- name = deref_func_name(tofree, &len,
+ name = deref_func_name((const char *)tofree, &len,
partial != NULL ? NULL : &partial, false);
/* Skip white space to allow ":call func ()". Not good, but required for
* backward compatibility. */
startarg = skipwhite(arg);
- rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */
+ rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this.
if (*startarg != '(') {
EMSG2(_("E107: Missing parentheses: %s"), eap->arg);
@@ -2829,12 +2745,13 @@ void ex_call(exarg_T *eap)
* call, and the loop is broken.
*/
if (eap->skip) {
- ++emsg_skip;
- lnum = eap->line2; /* do it once, also with an invalid range */
- } else
+ emsg_skip++;
+ lnum = eap->line2; // Do it once, also with an invalid range.
+ } else {
lnum = eap->line1;
- for (; lnum <= eap->line2; ++lnum) {
- if (!eap->skip && eap->addr_count > 0) {
+ }
+ for (; lnum <= eap->line2; lnum++) {
+ if (!eap->skip && eap->addr_count > 0) { // -V560
curwin->w_cursor.lnum = lnum;
curwin->w_cursor.col = 0;
curwin->w_cursor.coladd = 0;
@@ -2847,15 +2764,17 @@ void ex_call(exarg_T *eap)
break;
}
- /* Handle a function returning a Funcref, Dictionary or List. */
- if (handle_subscript(&arg, &rettv, !eap->skip, TRUE) == FAIL) {
- failed = TRUE;
+ // Handle a function returning a Funcref, Dictionary or List.
+ if (handle_subscript((const char **)&arg, &rettv, !eap->skip, true)
+ == FAIL) {
+ failed = true;
break;
}
- clear_tv(&rettv);
- if (doesrange || eap->skip)
+ tv_clear(&rettv);
+ if (doesrange || eap->skip) { // -V560
break;
+ }
/* Stop when immediately aborting on error, or when an interrupt
* occurred or an exception was thrown but not caught.
@@ -2877,10 +2796,12 @@ void ex_call(exarg_T *eap)
}
end:
- dict_unref(fudi.fd_dict);
+ tv_dict_unref(fudi.fd_dict);
xfree(tofree);
}
+// TODO(ZyX-I): move to eval/ex_cmds
+
/*
* ":unlet[!] var1 ... " command.
*/
@@ -2889,6 +2810,8 @@ void ex_unlet(exarg_T *eap)
ex_unletlock(eap, eap->arg, 0);
}
+// TODO(ZyX-I): move to eval/ex_cmds
+
/*
* ":lockvar" and ":unlockvar" commands
*/
@@ -2907,22 +2830,25 @@ void ex_lockvar(exarg_T *eap)
ex_unletlock(eap, arg, deep);
}
+// TODO(ZyX-I): move to eval/ex_cmds
+
/*
* ":unlet", ":lockvar" and ":unlockvar" are quite similar.
*/
static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep)
{
char_u *arg = argstart;
- char_u *name_end;
- int error = FALSE;
+ bool error = false;
lval_T lv;
do {
- /* Parse the name and find the end. */
- name_end = get_lval(arg, NULL, &lv, TRUE, eap->skip || error, 0,
- FNE_CHECK_START);
- if (lv.ll_name == NULL)
- error = TRUE; /* error but continue parsing */
+ // Parse the name and find the end.
+ char_u *const name_end = (char_u *)get_lval(arg, NULL, &lv, true,
+ eap->skip || error,
+ 0, FNE_CHECK_START);
+ if (lv.ll_name == NULL) {
+ error = true; // error, but continue parsing.
+ }
if (name_end == NULL || (!ascii_iswhite(*name_end)
&& !ends_excmd(*name_end))) {
if (name_end != NULL) {
@@ -2940,8 +2866,9 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep)
error = TRUE;
} else {
if (do_lock_var(&lv, name_end, deep,
- eap->cmdidx == CMD_lockvar) == FAIL)
- error = TRUE;
+ eap->cmdidx == CMD_lockvar) == FAIL) {
+ error = true;
+ }
}
}
@@ -2954,7 +2881,9 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep)
eap->nextcmd = check_nextcmd(arg);
}
-static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit)
+// TODO(ZyX-I): move to eval/ex_cmds
+
+static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit)
{
int ret = OK;
int cc;
@@ -2963,16 +2892,18 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit)
cc = *name_end;
*name_end = NUL;
- /* Normal name or expanded name. */
- if (check_changedtick(lp->ll_name))
- ret = FAIL;
- else if (do_unlet(lp->ll_name, forceit) == FAIL)
+ // Normal name or expanded name.
+ if (do_unlet(lp->ll_name, lp->ll_name_len, forceit) == FAIL) {
ret = FAIL;
+ }
*name_end = cc;
} else if ((lp->ll_list != NULL
- && tv_check_lock(lp->ll_list->lv_lock, lp->ll_name, false))
+ && tv_check_lock(lp->ll_list->lv_lock, (const char *)lp->ll_name,
+ lp->ll_name_len))
|| (lp->ll_dict != NULL
- && tv_check_lock(lp->ll_dict->dv_lock, lp->ll_name, false))) {
+ && tv_check_lock(lp->ll_dict->dv_lock,
+ (const char *)lp->ll_name,
+ lp->ll_name_len))) {
return FAIL;
} else if (lp->ll_range) {
listitem_T *li;
@@ -2981,7 +2912,8 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit)
while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) {
li = ll_li->li_next;
- if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name, false)) {
+ if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name,
+ lp->ll_name_len)) {
return false;
}
ll_li = li;
@@ -2991,33 +2923,33 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit)
/* Delete a range of List items. */
while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) {
li = lp->ll_li->li_next;
- listitem_remove(lp->ll_list, lp->ll_li);
+ tv_list_item_remove(lp->ll_list, lp->ll_li);
lp->ll_li = li;
++lp->ll_n1;
}
} else {
if (lp->ll_list != NULL) {
// unlet a List item.
- listitem_remove(lp->ll_list, lp->ll_li);
+ tv_list_item_remove(lp->ll_list, lp->ll_li);
} else {
// unlet a Dictionary item.
dict_T *d = lp->ll_dict;
dictitem_T *di = lp->ll_di;
- bool watched = is_watched(d);
+ bool watched = tv_dict_is_watched(d);
char *key = NULL;
typval_T oldtv;
if (watched) {
- copy_tv(&di->di_tv, &oldtv);
+ tv_copy(&di->di_tv, &oldtv);
// need to save key because dictitem_remove will free it
key = xstrdup((char *)di->di_key);
}
- dictitem_remove(d, di);
+ tv_dict_item_remove(d, di);
if (watched) {
- dictwatcher_notify(d, key, NULL, &oldtv);
- clear_tv(&oldtv);
+ tv_dict_watcher_notify(d, key, NULL, &oldtv);
+ tv_clear(&oldtv);
xfree(key);
}
}
@@ -3026,21 +2958,24 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit)
return ret;
}
-/*
- * "unlet" a variable. Return OK if it existed, FAIL if not.
- * When "forceit" is TRUE don't complain if the variable doesn't exist.
- */
-int do_unlet(char_u *name, int forceit)
+// TODO(ZyX-I): move to eval/ex_cmds
+
+/// unlet a variable
+///
+/// @param[in] name Variable name to unlet.
+/// @param[in] name_len Variable name length.
+/// @param[in] fonceit If true, do not complain if variable doesn’t exist.
+///
+/// @return OK if it existed, FAIL otherwise.
+int do_unlet(const char *const name, const size_t name_len, const int forceit)
+ FUNC_ATTR_NONNULL_ALL
{
- hashtab_T *ht;
- hashitem_T *hi;
- char_u *varname;
- dict_T *d;
- dictitem_T *di;
+ const char *varname;
dict_T *dict;
- ht = find_var_ht_dict(name, &varname, &dict);
+ hashtab_T *ht = find_var_ht_dict(name, name_len, &varname, &dict);
if (ht != NULL && *varname != NUL) {
+ dict_T *d;
if (ht == &globvarht) {
d = &globvardict;
} else if (current_funccal != NULL
@@ -3049,38 +2984,41 @@ int do_unlet(char_u *name, int forceit)
} else if (ht == &compat_hashtab) {
d = &vimvardict;
} else {
- di = find_var_in_ht(ht, *name, (char_u *)"", false);
+ dictitem_T *const di = find_var_in_ht(ht, *name, "", 0, false);
d = di->di_tv.vval.v_dict;
}
if (d == NULL) {
EMSG2(_(e_intern2), "do_unlet()");
return FAIL;
}
- hi = hash_find(ht, varname);
- if (!HASHITEM_EMPTY(hi)) {
- di = HI2DI(hi);
- if (var_check_fixed(di->di_flags, name, false)
- || var_check_ro(di->di_flags, name, false)
- || tv_check_lock(d->dv_lock, name, false)) {
+ hashitem_T *hi = hash_find(ht, (const char_u *)varname);
+ if (HASHITEM_EMPTY(hi)) {
+ hi = find_hi_in_scoped_ht((const char *)name, &ht);
+ }
+ if (hi != NULL && !HASHITEM_EMPTY(hi)) {
+ dictitem_T *const di = TV_DICT_HI2DI(hi);
+ if (var_check_fixed(di->di_flags, (const char *)name, TV_CSTRING)
+ || var_check_ro(di->di_flags, (const char *)name, TV_CSTRING)
+ || tv_check_lock(d->dv_lock, (const char *)name, TV_CSTRING)) {
return FAIL;
}
- if (d == NULL || tv_check_lock(d->dv_lock, name, false)) {
+ if (tv_check_lock(d->dv_lock, (const char *)name, TV_CSTRING)) {
return FAIL;
}
typval_T oldtv;
- bool watched = is_watched(dict);
+ bool watched = tv_dict_is_watched(dict);
if (watched) {
- copy_tv(&di->di_tv, &oldtv);
+ tv_copy(&di->di_tv, &oldtv);
}
delete_var(ht, hi);
if (watched) {
- dictwatcher_notify(dict, (char *)varname, NULL, &oldtv);
- clear_tv(&oldtv);
+ tv_dict_watcher_notify(dict, varname, NULL, &oldtv);
+ tv_clear(&oldtv);
}
return OK;
}
@@ -3091,162 +3029,74 @@ int do_unlet(char_u *name, int forceit)
return FAIL;
}
+// TODO(ZyX-I): move to eval/ex_cmds
+
/*
* Lock or unlock variable indicated by "lp".
* "deep" is the levels to go (-1 for unlimited);
* "lock" is TRUE for ":lockvar", FALSE for ":unlockvar".
*/
-static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock)
+static int do_lock_var(lval_T *lp, char_u *const name_end, const int deep,
+ const bool lock)
{
int ret = OK;
- int cc;
- dictitem_T *di;
- if (deep == 0) /* nothing to do */
+ if (deep == 0) { // Nothing to do.
return OK;
+ }
if (lp->ll_tv == NULL) {
- cc = *name_end;
- *name_end = NUL;
-
- /* Normal name or expanded name. */
- if (check_changedtick(lp->ll_name))
+ // Normal name or expanded name.
+ dictitem_T *const di = find_var(
+ (const char *)lp->ll_name, lp->ll_name_len, NULL,
+ true);
+ if (di == NULL) {
ret = FAIL;
- else {
- di = find_var(lp->ll_name, NULL, TRUE);
- if (di == NULL)
- ret = FAIL;
- else {
- if (lock)
- di->di_flags |= DI_FLAGS_LOCK;
- else
- di->di_flags &= ~DI_FLAGS_LOCK;
- item_lock(&di->di_tv, deep, lock);
+ } else if ((di->di_flags & DI_FLAGS_FIX)
+ && di->di_tv.v_type != VAR_DICT
+ && di->di_tv.v_type != VAR_LIST) {
+ // For historical reasons this error is not given for Lists and
+ // Dictionaries. E.g. b: dictionary may be locked/unlocked.
+ emsgf(_("E940: Cannot lock or unlock variable %s"), lp->ll_name);
+ } else {
+ if (lock) {
+ di->di_flags |= DI_FLAGS_LOCK;
+ } else {
+ di->di_flags &= ~DI_FLAGS_LOCK;
}
+ tv_item_lock(&di->di_tv, deep, lock);
}
- *name_end = cc;
} else if (lp->ll_range) {
listitem_T *li = lp->ll_li;
/* (un)lock a range of List items. */
while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) {
- item_lock(&li->li_tv, deep, lock);
+ tv_item_lock(&li->li_tv, deep, lock);
li = li->li_next;
++lp->ll_n1;
}
} else if (lp->ll_list != NULL) {
// (un)lock a List item.
- item_lock(&lp->ll_li->li_tv, deep, lock);
+ tv_item_lock(&lp->ll_li->li_tv, deep, lock);
} else {
// (un)lock a Dictionary item.
- item_lock(&lp->ll_di->di_tv, deep, lock);
+ tv_item_lock(&lp->ll_di->di_tv, deep, lock);
}
return ret;
}
/*
- * Lock or unlock an item. "deep" is nr of levels to go.
- */
-static void item_lock(typval_T *tv, int deep, int lock)
-{
- static int recurse = 0;
- list_T *l;
- listitem_T *li;
- dict_T *d;
- hashitem_T *hi;
- int todo;
-
- if (recurse >= DICT_MAXNEST) {
- EMSG(_("E743: variable nested too deep for (un)lock"));
- return;
- }
- if (deep == 0)
- return;
- ++recurse;
-
- /* lock/unlock the item itself */
- if (lock)
- tv->v_lock |= VAR_LOCKED;
- else
- tv->v_lock &= ~VAR_LOCKED;
-
- switch (tv->v_type) {
- case VAR_LIST:
- if ((l = tv->vval.v_list) != NULL) {
- if (lock)
- l->lv_lock |= VAR_LOCKED;
- else
- l->lv_lock &= ~VAR_LOCKED;
- if (deep < 0 || deep > 1)
- /* recursive: lock/unlock the items the List contains */
- for (li = l->lv_first; li != NULL; li = li->li_next)
- item_lock(&li->li_tv, deep - 1, lock);
- }
- break;
- case VAR_DICT:
- if ((d = tv->vval.v_dict) != NULL) {
- if (lock)
- d->dv_lock |= VAR_LOCKED;
- else
- d->dv_lock &= ~VAR_LOCKED;
- if (deep < 0 || deep > 1) {
- /* recursive: lock/unlock the items the List contains */
- todo = (int)d->dv_hashtab.ht_used;
- for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) {
- if (!HASHITEM_EMPTY(hi)) {
- --todo;
- item_lock(&HI2DI(hi)->di_tv, deep - 1, lock);
- }
- }
- }
- }
- break;
- case VAR_NUMBER:
- case VAR_FLOAT:
- case VAR_STRING:
- case VAR_FUNC:
- case VAR_PARTIAL:
- case VAR_SPECIAL:
- break;
- case VAR_UNKNOWN:
- assert(false);
- }
- --recurse;
-}
-
-/*
- * Return TRUE if typeval "tv" is locked: Either that value is locked itself
- * or it refers to a List or Dictionary that is locked.
- */
-static int tv_islocked(typval_T *tv)
-{
- return (tv->v_lock & VAR_LOCKED)
- || (tv->v_type == VAR_LIST
- && tv->vval.v_list != NULL
- && (tv->vval.v_list->lv_lock & VAR_LOCKED))
- || (tv->v_type == VAR_DICT
- && tv->vval.v_dict != NULL
- && (tv->vval.v_dict->dv_lock & VAR_LOCKED));
-}
-
-/*
* Delete all "menutrans_" variables.
*/
void del_menutrans_vars(void)
{
- hashitem_T *hi;
- int todo;
-
hash_lock(&globvarht);
- todo = (int)globvarht.ht_used;
- for (hi = globvarht.ht_array; todo > 0 && !got_int; ++hi) {
- if (!HASHITEM_EMPTY(hi)) {
- --todo;
- if (STRNCMP(HI2DI(hi)->di_key, "menutrans_", 10) == 0)
- delete_var(&globvarht, hi);
+ HASHTAB_ITER(&globvarht, hi, {
+ if (STRNCMP(hi->hi_key, "menutrans_", 10) == 0) {
+ delete_var(&globvarht, hi);
}
- }
+ });
hash_unlock(&globvarht);
}
@@ -3322,10 +3172,6 @@ char_u *get_user_var_name(expand_T *xp, int idx)
++hi;
return cat_prefix_varname('b', hi->hi_key);
}
- if (bdone == ht->ht_used) {
- ++bdone;
- return (char_u *)"b:changedtick";
- }
/* w: variables */
ht = &curwin->w_vars->dv_hashtab;
@@ -3362,6 +3208,7 @@ char_u *get_user_var_name(expand_T *xp, int idx)
return NULL;
}
+// TODO(ZyX-I): move to eval/expressions
/// Return TRUE if "pat" matches "text".
/// Does not use 'cpo' and always uses 'magic'.
@@ -3398,6 +3245,8 @@ typedef enum {
, TYPE_NOMATCH /* !~ */
} exptype_T;
+// TODO(ZyX-I): move to eval/expressions
+
/*
* The "evaluate" argument: When FALSE, the argument is only parsed but not
* executed. The function may return OK, but the rettv will be of type
@@ -3411,7 +3260,7 @@ typedef enum {
* Note: "rettv.v_lock" is not set.
* Return OK or FAIL.
*/
-static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate)
+int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate)
{
int ret;
char_u *p;
@@ -3419,15 +3268,15 @@ static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate)
p = skipwhite(arg);
ret = eval1(&p, rettv, evaluate);
if (ret == FAIL || !ends_excmd(*p)) {
- if (ret != FAIL)
- clear_tv(rettv);
- /*
- * Report the invalid expression unless the expression evaluation has
- * been cancelled due to an aborting error, an interrupt, or an
- * exception.
- */
- if (!aborting())
- EMSG2(_(e_invexpr2), arg);
+ if (ret != FAIL) {
+ tv_clear(rettv);
+ }
+ // Report the invalid expression unless the expression evaluation has
+ // been cancelled due to an aborting error, an interrupt, or an
+ // exception.
+ if (!aborting()) {
+ emsgf(_(e_invexpr2), arg);
+ }
ret = FAIL;
}
if (nextcmd != NULL)
@@ -3436,6 +3285,8 @@ static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate)
return ret;
}
+// TODO(ZyX-I): move to eval/expressions
+
/*
* Handle top level expression:
* expr2 ? expr1 : expr1
@@ -3461,13 +3312,15 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate)
if ((*arg)[0] == '?') {
result = FALSE;
if (evaluate) {
- int error = FALSE;
+ bool error = false;
- if (get_tv_number_chk(rettv, &error) != 0)
- result = TRUE;
- clear_tv(rettv);
- if (error)
+ if (tv_get_number_chk(rettv, &error) != 0) {
+ result = true;
+ }
+ tv_clear(rettv);
+ if (error) {
return FAIL;
+ }
}
/*
@@ -3481,9 +3334,10 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate)
* Check for the ":".
*/
if ((*arg)[0] != ':') {
- EMSG(_("E109: Missing ':' after '?'"));
- if (evaluate && result)
- clear_tv(rettv);
+ emsgf(_("E109: Missing ':' after '?'"));
+ if (evaluate && result) {
+ tv_clear(rettv);
+ }
return FAIL;
}
@@ -3491,9 +3345,10 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate)
* Get the third variable.
*/
*arg = skipwhite(*arg + 1);
- if (eval1(arg, &var2, evaluate && !result) == FAIL) { /* recursive! */
- if (evaluate && result)
- clear_tv(rettv);
+ if (eval1(arg, &var2, evaluate && !result) == FAIL) { // Recursive!
+ if (evaluate && result) {
+ tv_clear(rettv);
+ }
return FAIL;
}
if (evaluate && !result)
@@ -3503,6 +3358,8 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate)
return OK;
}
+// TODO(ZyX-I): move to eval/expressions
+
/*
* Handle first level expression:
* expr2 || expr2 || expr2 logical OR
@@ -3517,7 +3374,7 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate)
typval_T var2;
long result;
int first;
- int error = FALSE;
+ bool error = false;
/*
* Get the first variable.
@@ -3532,12 +3389,14 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate)
result = FALSE;
while ((*arg)[0] == '|' && (*arg)[1] == '|') {
if (evaluate && first) {
- if (get_tv_number_chk(rettv, &error) != 0)
- result = TRUE;
- clear_tv(rettv);
- if (error)
+ if (tv_get_number_chk(rettv, &error) != 0) {
+ result = true;
+ }
+ tv_clear(rettv);
+ if (error) {
return FAIL;
- first = FALSE;
+ }
+ first = false;
}
/*
@@ -3551,11 +3410,13 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate)
* Compute the result.
*/
if (evaluate && !result) {
- if (get_tv_number_chk(&var2, &error) != 0)
- result = TRUE;
- clear_tv(&var2);
- if (error)
+ if (tv_get_number_chk(&var2, &error) != 0) {
+ result = true;
+ }
+ tv_clear(&var2);
+ if (error) {
return FAIL;
+ }
}
if (evaluate) {
rettv->v_type = VAR_NUMBER;
@@ -3566,6 +3427,8 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate)
return OK;
}
+// TODO(ZyX-I): move to eval/expressions
+
/*
* Handle second level expression:
* expr3 && expr3 && expr3 logical AND
@@ -3580,7 +3443,7 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate)
typval_T var2;
long result;
int first;
- int error = FALSE;
+ bool error = false;
/*
* Get the first variable.
@@ -3595,12 +3458,14 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate)
result = TRUE;
while ((*arg)[0] == '&' && (*arg)[1] == '&') {
if (evaluate && first) {
- if (get_tv_number_chk(rettv, &error) == 0)
- result = FALSE;
- clear_tv(rettv);
- if (error)
+ if (tv_get_number_chk(rettv, &error) == 0) {
+ result = false;
+ }
+ tv_clear(rettv);
+ if (error) {
return FAIL;
- first = FALSE;
+ }
+ first = false;
}
/*
@@ -3614,11 +3479,13 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate)
* Compute the result.
*/
if (evaluate && result) {
- if (get_tv_number_chk(&var2, &error) == 0)
- result = FALSE;
- clear_tv(&var2);
- if (error)
+ if (tv_get_number_chk(&var2, &error) == 0) {
+ result = false;
+ }
+ tv_clear(&var2);
+ if (error) {
return FAIL;
+ }
}
if (evaluate) {
rettv->v_type = VAR_NUMBER;
@@ -3629,6 +3496,8 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate)
return OK;
}
+// TODO(ZyX-I): move to eval/expressions
+
/*
* Handle third level expression:
* var1 == var2
@@ -3655,9 +3524,7 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate)
exptype_T type = TYPE_UNKNOWN;
int type_is = FALSE; /* TRUE for "is" and "isnot" */
int len = 2;
- long n1, n2;
- char_u *s1, *s2;
- char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
+ varnumber_T n1, n2;
int ic;
/*
@@ -3725,7 +3592,7 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate)
*/
*arg = skipwhite(p + len);
if (eval5(arg, &var2, evaluate) == FAIL) {
- clear_tv(rettv);
+ tv_clear(rettv);
return FAIL;
}
@@ -3747,15 +3614,15 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate)
} else {
EMSG(_("E692: Invalid operation for List"));
}
- clear_tv(rettv);
- clear_tv(&var2);
+ tv_clear(rettv);
+ tv_clear(&var2);
return FAIL;
} else {
- /* Compare two Lists for being equal or unequal. */
- n1 = list_equal(rettv->vval.v_list, var2.vval.v_list,
- ic, FALSE);
- if (type == TYPE_NEQUAL)
+ // Compare two Lists for being equal or unequal.
+ n1 = tv_list_equal(rettv->vval.v_list, var2.vval.v_list, ic, false);
+ if (type == TYPE_NEQUAL) {
n1 = !n1;
+ }
}
} else if (rettv->v_type == VAR_DICT || var2.v_type == VAR_DICT) {
if (type_is) {
@@ -3769,23 +3636,22 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate)
EMSG(_("E735: Can only compare Dictionary with Dictionary"));
else
EMSG(_("E736: Invalid operation for Dictionary"));
- clear_tv(rettv);
- clear_tv(&var2);
+ tv_clear(rettv);
+ tv_clear(&var2);
return FAIL;
} else {
- /* Compare two Dictionaries for being equal or unequal. */
- n1 = dict_equal(rettv->vval.v_dict, var2.vval.v_dict,
- ic, FALSE);
- if (type == TYPE_NEQUAL)
+ // Compare two Dictionaries for being equal or unequal.
+ n1 = tv_dict_equal(rettv->vval.v_dict, var2.vval.v_dict,
+ ic, false);
+ if (type == TYPE_NEQUAL) {
n1 = !n1;
+ }
}
- } else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC
- || rettv->v_type == VAR_PARTIAL
- || var2.v_type == VAR_PARTIAL) {
+ } else if (tv_is_func(*rettv) || tv_is_func(var2)) {
if (type != TYPE_EQUAL && type != TYPE_NEQUAL) {
EMSG(_("E694: Invalid operation for Funcrefs"));
- clear_tv(rettv);
- clear_tv(&var2);
+ tv_clear(rettv);
+ tv_clear(&var2);
return FAIL;
}
if ((rettv->v_type == VAR_PARTIAL
@@ -3820,25 +3686,27 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate)
&& type != TYPE_MATCH && type != TYPE_NOMATCH) {
float_T f1, f2;
- if (rettv->v_type == VAR_FLOAT)
+ if (rettv->v_type == VAR_FLOAT) {
f1 = rettv->vval.v_float;
- else
- f1 = get_tv_number(rettv);
- if (var2.v_type == VAR_FLOAT)
+ } else {
+ f1 = tv_get_number(rettv);
+ }
+ if (var2.v_type == VAR_FLOAT) {
f2 = var2.vval.v_float;
- else
- f2 = get_tv_number(&var2);
- n1 = FALSE;
+ } else {
+ f2 = tv_get_number(&var2);
+ }
+ n1 = false;
switch (type) {
- case TYPE_EQUAL: n1 = (f1 == f2); break;
- case TYPE_NEQUAL: n1 = (f1 != f2); break;
- case TYPE_GREATER: n1 = (f1 > f2); break;
- case TYPE_GEQUAL: n1 = (f1 >= f2); break;
- case TYPE_SMALLER: n1 = (f1 < f2); break;
- case TYPE_SEQUAL: n1 = (f1 <= f2); break;
- case TYPE_UNKNOWN:
- case TYPE_MATCH:
- case TYPE_NOMATCH: break; /* avoid gcc warning */
+ case TYPE_EQUAL: n1 = (f1 == f2); break;
+ case TYPE_NEQUAL: n1 = (f1 != f2); break;
+ case TYPE_GREATER: n1 = (f1 > f2); break;
+ case TYPE_GEQUAL: n1 = (f1 >= f2); break;
+ case TYPE_SMALLER: n1 = (f1 < f2); break;
+ case TYPE_SEQUAL: n1 = (f1 <= f2); break;
+ case TYPE_UNKNOWN:
+ case TYPE_MATCH:
+ case TYPE_NOMATCH: break;
}
}
/*
@@ -3847,48 +3715,51 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate)
*/
else if ((rettv->v_type == VAR_NUMBER || var2.v_type == VAR_NUMBER)
&& type != TYPE_MATCH && type != TYPE_NOMATCH) {
- n1 = get_tv_number(rettv);
- n2 = get_tv_number(&var2);
+ n1 = tv_get_number(rettv);
+ n2 = tv_get_number(&var2);
switch (type) {
- case TYPE_EQUAL: n1 = (n1 == n2); break;
- case TYPE_NEQUAL: n1 = (n1 != n2); break;
- case TYPE_GREATER: n1 = (n1 > n2); break;
- case TYPE_GEQUAL: n1 = (n1 >= n2); break;
- case TYPE_SMALLER: n1 = (n1 < n2); break;
- case TYPE_SEQUAL: n1 = (n1 <= n2); break;
- case TYPE_UNKNOWN:
- case TYPE_MATCH:
- case TYPE_NOMATCH: break; /* avoid gcc warning */
+ case TYPE_EQUAL: n1 = (n1 == n2); break;
+ case TYPE_NEQUAL: n1 = (n1 != n2); break;
+ case TYPE_GREATER: n1 = (n1 > n2); break;
+ case TYPE_GEQUAL: n1 = (n1 >= n2); break;
+ case TYPE_SMALLER: n1 = (n1 < n2); break;
+ case TYPE_SEQUAL: n1 = (n1 <= n2); break;
+ case TYPE_UNKNOWN:
+ case TYPE_MATCH:
+ case TYPE_NOMATCH: break;
}
} else {
- s1 = get_tv_string_buf(rettv, buf1);
- s2 = get_tv_string_buf(&var2, buf2);
- if (type != TYPE_MATCH && type != TYPE_NOMATCH)
- i = ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2);
- else
+ char buf1[NUMBUFLEN];
+ char buf2[NUMBUFLEN];
+ const char *const s1 = tv_get_string_buf(rettv, buf1);
+ const char *const s2 = tv_get_string_buf(&var2, buf2);
+ if (type != TYPE_MATCH && type != TYPE_NOMATCH) {
+ i = mb_strcmp_ic((bool)ic, s1, s2);
+ } else {
i = 0;
- n1 = FALSE;
+ }
+ n1 = false;
switch (type) {
- case TYPE_EQUAL: n1 = (i == 0); break;
- case TYPE_NEQUAL: n1 = (i != 0); break;
- case TYPE_GREATER: n1 = (i > 0); break;
- case TYPE_GEQUAL: n1 = (i >= 0); break;
- case TYPE_SMALLER: n1 = (i < 0); break;
- case TYPE_SEQUAL: n1 = (i <= 0); break;
-
- case TYPE_MATCH:
- case TYPE_NOMATCH:
- n1 = pattern_match(s2, s1, ic);
- if (type == TYPE_NOMATCH) {
- n1 = !n1;
+ case TYPE_EQUAL: n1 = (i == 0); break;
+ case TYPE_NEQUAL: n1 = (i != 0); break;
+ case TYPE_GREATER: n1 = (i > 0); break;
+ case TYPE_GEQUAL: n1 = (i >= 0); break;
+ case TYPE_SMALLER: n1 = (i < 0); break;
+ case TYPE_SEQUAL: n1 = (i <= 0); break;
+
+ case TYPE_MATCH:
+ case TYPE_NOMATCH: {
+ n1 = pattern_match((char_u *)s2, (char_u *)s1, ic);
+ if (type == TYPE_NOMATCH) {
+ n1 = !n1;
+ }
+ break;
}
- break;
-
- case TYPE_UNKNOWN: break; /* avoid gcc warning */
+ case TYPE_UNKNOWN: break; // Avoid gcc warning.
}
}
- clear_tv(rettv);
- clear_tv(&var2);
+ tv_clear(rettv);
+ tv_clear(&var2);
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = n1;
}
@@ -3897,6 +3768,8 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate)
return OK;
}
+// TODO(ZyX-I): move to eval/expressions
+
/*
* Handle fourth level expression:
* + number addition
@@ -3913,10 +3786,8 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate)
typval_T var2;
typval_T var3;
int op;
- long n1, n2;
+ varnumber_T n1, n2;
float_T f1 = 0, f2 = 0;
- char_u *s1, *s2;
- char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
char_u *p;
/*
@@ -3934,17 +3805,16 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate)
break;
if ((op != '+' || rettv->v_type != VAR_LIST)
- && (op == '.' || rettv->v_type != VAR_FLOAT)
- ) {
- /* 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.
- * For "something . ...", "something - ..." or "non-list + ...",
- * 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 && get_tv_string_chk(rettv) == NULL) {
- clear_tv(rettv);
+ && (op == '.' || rettv->v_type != VAR_FLOAT)) {
+ // 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.
+ // For "something . ...", "something - ..." or "non-list + ...",
+ // 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)) {
+ tv_clear(rettv);
return FAIL;
}
}
@@ -3954,7 +3824,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate)
*/
*arg = skipwhite(*arg + 1);
if (eval6(arg, &var2, evaluate, op == '.') == FAIL) {
- clear_tv(rettv);
+ tv_clear(rettv);
return FAIL;
}
@@ -3963,41 +3833,44 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate)
* Compute the result.
*/
if (op == '.') {
- s1 = get_tv_string_buf(rettv, buf1); /* already checked */
- s2 = get_tv_string_buf_chk(&var2, buf2);
- if (s2 == NULL) { /* type error ? */
- clear_tv(rettv);
- clear_tv(&var2);
+ 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);
return FAIL;
}
- p = concat_str(s1, s2);
- clear_tv(rettv);
+ p = concat_str((const char_u *)s1, (const char_u *)s2);
+ tv_clear(rettv);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = p;
} else if (op == '+' && rettv->v_type == VAR_LIST
&& var2.v_type == VAR_LIST) {
- /* concatenate Lists */
- if (list_concat(rettv->vval.v_list, var2.vval.v_list,
- &var3) == FAIL) {
- clear_tv(rettv);
- clear_tv(&var2);
+ // Concatenate Lists.
+ if (tv_list_concat(rettv->vval.v_list, var2.vval.v_list, &var3)
+ == FAIL) {
+ tv_clear(rettv);
+ tv_clear(&var2);
return FAIL;
}
- clear_tv(rettv);
+ tv_clear(rettv);
*rettv = var3;
} else {
- int error = FALSE;
+ bool error = false;
if (rettv->v_type == VAR_FLOAT) {
f1 = rettv->vval.v_float;
n1 = 0;
} else {
- n1 = get_tv_number_chk(rettv, &error);
+ n1 = tv_get_number_chk(rettv, &error);
if (error) {
/* This can only happen for "list + non-list". For
* "non-list + ..." or "something - ...", we returned
* before evaluating the 2nd operand. */
- clear_tv(rettv);
+ tv_clear(rettv);
return FAIL;
}
if (var2.v_type == VAR_FLOAT)
@@ -4007,16 +3880,16 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate)
f2 = var2.vval.v_float;
n2 = 0;
} else {
- n2 = get_tv_number_chk(&var2, &error);
+ n2 = tv_get_number_chk(&var2, &error);
if (error) {
- clear_tv(rettv);
- clear_tv(&var2);
+ tv_clear(rettv);
+ tv_clear(&var2);
return FAIL;
}
if (rettv->v_type == VAR_FLOAT)
f2 = n2;
}
- clear_tv(rettv);
+ 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) {
@@ -4035,37 +3908,36 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate)
rettv->vval.v_number = n1;
}
}
- clear_tv(&var2);
+ tv_clear(&var2);
}
}
return OK;
}
-/*
- * Handle fifth level expression:
- * * number multiplication
- * / number division
- * % number modulo
- *
- * "arg" must point to the first non-white of the expression.
- * "arg" is advanced to the next non-white after the recognized expression.
- *
- * Return OK or FAIL.
- */
-static int
-eval6 (
- char_u **arg,
- typval_T *rettv,
- int evaluate,
- int want_string /* after "." operator */
-)
+// TODO(ZyX-I): move to eval/expressions
+
+/// Handle fifth level expression:
+/// - * number multiplication
+/// - / number division
+/// - % number modulo
+///
+/// @param[in,out] arg Points to the first non-whitespace character of the
+/// 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_u **arg, typval_T *rettv, int evaluate, int want_string)
+ FUNC_ATTR_NO_SANITIZE_UNDEFINED
{
typval_T var2;
int op;
- long n1, n2;
- int use_float = FALSE;
+ varnumber_T n1, n2;
+ bool use_float = false;
float_T f1 = 0, f2;
- int error = FALSE;
+ bool error = false;
/*
* Get the first variable.
@@ -4084,15 +3956,18 @@ eval6 (
if (evaluate) {
if (rettv->v_type == VAR_FLOAT) {
f1 = rettv->vval.v_float;
- use_float = TRUE;
+ use_float = true;
n1 = 0;
- } else
- n1 = get_tv_number_chk(rettv, &error);
- clear_tv(rettv);
- if (error)
+ } else {
+ n1 = tv_get_number_chk(rettv, &error);
+ }
+ tv_clear(rettv);
+ if (error) {
return FAIL;
- } else
+ }
+ } else {
n1 = 0;
+ }
/*
* Get the second variable.
@@ -4105,17 +3980,19 @@ eval6 (
if (var2.v_type == VAR_FLOAT) {
if (!use_float) {
f1 = n1;
- use_float = TRUE;
+ use_float = true;
}
f2 = var2.vval.v_float;
n2 = 0;
} else {
- n2 = get_tv_number_chk(&var2, &error);
- clear_tv(&var2);
- if (error)
+ n2 = tv_get_number_chk(&var2, &error);
+ tv_clear(&var2);
+ if (error) {
return FAIL;
- if (use_float)
+ }
+ if (use_float) {
f2 = n2;
+ }
}
/*
@@ -4146,18 +4023,20 @@ eval6 (
rettv->v_type = VAR_FLOAT;
rettv->vval.v_float = f1;
} else {
- if (op == '*')
+ if (op == '*') {
n1 = n1 * n2;
- else if (op == '/') {
- if (n2 == 0) { /* give an error message? */
- if (n1 == 0)
- n1 = -0x7fffffffL - 1L; /* similar to NaN */
- else if (n1 < 0)
- n1 = -0x7fffffffL;
- else
- n1 = 0x7fffffffL;
- } else
+ } else if (op == '/') {
+ if (n2 == 0) { // give an error message?
+ if (n1 == 0) {
+ n1 = VARNUMBER_MIN; // similar to NaN
+ } else if (n1 < 0) {
+ n1 = -VARNUMBER_MAX;
+ } else {
+ n1 = VARNUMBER_MAX;
+ }
+ } else {
n1 = n1 / n2;
+ }
} else {
if (n2 == 0) /* give an error message? */
n1 = 0;
@@ -4173,6 +4052,8 @@ eval6 (
return OK;
}
+// TODO(ZyX-I): move to eval/expressions
+
// Handle sixth level expression:
// number number constant
// "string" string constant
@@ -4204,18 +4085,18 @@ static int eval7(
int want_string // after "." operator
)
{
- long n;
+ varnumber_T n;
int len;
char_u *s;
char_u *start_leader, *end_leader;
int ret = OK;
char_u *alias;
- // Initialise variable so that clear_tv() can't mistake this for a
+ // Initialise variable so that tv_clear() can't mistake this for a
// string and free a string that isn't there.
rettv->v_type = VAR_UNKNOWN;
- // Skip '!' and '-' characters. They are handled later.
+ // Skip '!', '-' and '+' characters. They are handled later.
start_leader = *arg;
while (**arg == '!' || **arg == '-' || **arg == '+') {
*arg = skipwhite(*arg + 1);
@@ -4292,14 +4173,19 @@ static int eval7(
case '[': ret = get_list_tv(arg, rettv, evaluate);
break;
+ // Lambda: {arg, arg -> expr}
// Dictionary: {key: val, key: val}
- case '{': ret = get_dict_tv(arg, rettv, evaluate);
+ case '{': ret = get_lambda_tv(arg, rettv, evaluate);
+ if (ret == NOTDONE) {
+ ret = get_dict_tv(arg, rettv, evaluate);
+ }
break;
// Option value: &name
- case '&': ret = get_option_tv(arg, rettv, evaluate);
+ case '&': {
+ ret = get_option_tv((const char **)arg, rettv, evaluate);
break;
-
+ }
// Environment variable: $VAR.
case '$': ret = get_env_tv(arg, rettv, evaluate);
break;
@@ -4322,7 +4208,7 @@ static int eval7(
++*arg;
} else if (ret == OK) {
EMSG(_("E110: Missing ')'"));
- clear_tv(rettv);
+ tv_clear(rettv);
ret = FAIL;
}
break;
@@ -4335,7 +4221,7 @@ static int eval7(
// Must be a variable or function name.
// Can also be a curly-braces kind of name: {expr}.
s = *arg;
- len = get_name_len(arg, &alias, evaluate, true);
+ len = get_name_len((const char **)arg, (char **)&alias, evaluate, true);
if (alias != NULL) {
s = alias;
}
@@ -4345,20 +4231,31 @@ static int eval7(
} else {
if (**arg == '(') { // recursive!
partial_T *partial;
+
+ if (!evaluate) {
+ check_vars((const char *)s, len);
+ }
+
// If "s" is the name of a variable of type VAR_FUNC
// use its contents.
- s = deref_func_name(s, &len, &partial, !evaluate);
+ s = deref_func_name((const char *)s, &len, &partial, !evaluate);
+
+ // Need to make a copy, in case evaluating the arguments makes
+ // the name invalid.
+ s = xmemdupz(s, len);
// Invoke the function.
ret = get_func_tv(s, len, rettv, arg,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&len, evaluate, partial, NULL);
+ xfree(s);
+
// If evaluate is false rettv->v_type was not set in
// get_func_tv, but it's needed in handle_subscript() to parse
// what follows. So set it here.
if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') {
- rettv->vval.v_string = empty_string;
+ rettv->vval.v_string = (char_u *)tv_empty_string;
rettv->v_type = VAR_FUNC;
}
@@ -4367,13 +4264,14 @@ static int eval7(
// an exception was thrown but not caught.
if (aborting()) {
if (ret == OK) {
- clear_tv(rettv);
+ tv_clear(rettv);
}
ret = FAIL;
}
} else if (evaluate) {
- ret = get_var_tv(s, len, rettv, NULL, true, false);
+ ret = get_var_tv((const char *)s, len, rettv, NULL, true, false);
} else {
+ check_vars((const char *)s, len);
ret = OK;
}
}
@@ -4385,22 +4283,22 @@ static int eval7(
// Handle following '[', '(' and '.' for expr[expr], expr.name,
// expr(expr).
if (ret == OK) {
- ret = handle_subscript(arg, rettv, evaluate, true);
+ ret = handle_subscript((const char **)arg, rettv, evaluate, true);
}
// Apply logical NOT and unary '-', from right to left, ignore '+'.
if (ret == OK && evaluate && end_leader > start_leader) {
- int error = false;
- int val = 0;
+ bool error = false;
+ varnumber_T val = 0;
float_T f = 0.0;
if (rettv->v_type == VAR_FLOAT) {
f = rettv->vval.v_float;
} else {
- val = get_tv_number_chk(rettv, &error);
+ val = tv_get_number_chk(rettv, &error);
}
if (error) {
- clear_tv(rettv);
+ tv_clear(rettv);
ret = FAIL;
} else {
while (end_leader > start_leader) {
@@ -4420,10 +4318,10 @@ static int eval7(
}
}
if (rettv->v_type == VAR_FLOAT) {
- clear_tv(rettv);
+ tv_clear(rettv);
rettv->vval.v_float = f;
} else {
- clear_tv(rettv);
+ tv_clear(rettv);
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = val;
}
@@ -4433,6 +4331,8 @@ static int eval7(
return ret;
}
+// TODO(ZyX-I): move to eval/expressions
+
/*
* Evaluate an "[expr]" or "[expr:expr]" index. Also "dict.key".
* "*arg" points to the '[' or '.'.
@@ -4446,12 +4346,11 @@ eval_index (
int verbose /* give error messages */
)
{
- int empty1 = FALSE, empty2 = FALSE;
- typval_T var1, var2;
+ bool empty1 = false;
+ bool empty2 = false;
long n1, n2 = 0;
- long len = -1;
- int range = FALSE;
- char_u *s;
+ ptrdiff_t len = -1;
+ int range = false;
char_u *key = NULL;
switch (rettv->v_type) {
@@ -4488,8 +4387,8 @@ eval_index (
}
}
- init_tv(&var1);
- init_tv(&var2);
+ typval_T var1 = TV_INITIAL_VALUE;
+ typval_T var2 = TV_INITIAL_VALUE;
if (**arg == '.') {
/*
* dict.name
@@ -4507,13 +4406,13 @@ eval_index (
* Get the (first) variable from inside the [].
*/
*arg = skipwhite(*arg + 1);
- if (**arg == ':')
- empty1 = TRUE;
- else if (eval1(arg, &var1, evaluate) == FAIL) /* recursive! */
+ if (**arg == ':') {
+ empty1 = true;
+ } else if (eval1(arg, &var1, evaluate) == FAIL) { // Recursive!
return FAIL;
- else if (evaluate && get_tv_string_chk(&var1) == NULL) {
- /* not a number or string */
- clear_tv(&var1);
+ } else if (evaluate && !tv_check_str(&var1)) {
+ // Not a number or string.
+ tv_clear(&var1);
return FAIL;
}
@@ -4523,28 +4422,32 @@ eval_index (
if (**arg == ':') {
range = TRUE;
*arg = skipwhite(*arg + 1);
- if (**arg == ']')
- empty2 = TRUE;
- else if (eval1(arg, &var2, evaluate) == FAIL) { /* recursive! */
- if (!empty1)
- clear_tv(&var1);
+ if (**arg == ']') {
+ empty2 = true;
+ } else if (eval1(arg, &var2, evaluate) == FAIL) { // Recursive!
+ if (!empty1) {
+ tv_clear(&var1);
+ }
return FAIL;
- } else if (evaluate && get_tv_string_chk(&var2) == NULL) {
- /* not a number or string */
- if (!empty1)
- clear_tv(&var1);
- clear_tv(&var2);
+ } else if (evaluate && !tv_check_str(&var2)) {
+ // Not a number or string.
+ if (!empty1) {
+ tv_clear(&var1);
+ }
+ tv_clear(&var2);
return FAIL;
}
}
/* Check for the ']'. */
if (**arg != ']') {
- if (verbose)
- EMSG(_(e_missbrac));
- clear_tv(&var1);
- if (range)
- clear_tv(&var2);
+ if (verbose) {
+ emsgf(_(e_missbrac));
+ }
+ tv_clear(&var1);
+ if (range) {
+ tv_clear(&var2);
+ }
return FAIL;
}
*arg = skipwhite(*arg + 1); /* skip the ']' */
@@ -4553,168 +4456,181 @@ eval_index (
if (evaluate) {
n1 = 0;
if (!empty1 && rettv->v_type != VAR_DICT) {
- n1 = get_tv_number(&var1);
- clear_tv(&var1);
+ n1 = tv_get_number(&var1);
+ tv_clear(&var1);
}
if (range) {
- if (empty2)
+ if (empty2) {
n2 = -1;
- else {
- n2 = get_tv_number(&var2);
- clear_tv(&var2);
+ } else {
+ n2 = tv_get_number(&var2);
+ tv_clear(&var2);
}
}
switch (rettv->v_type) {
- case VAR_NUMBER:
- case VAR_STRING:
- s = get_tv_string(rettv);
- len = (long)STRLEN(s);
- if (range) {
- /* The resulting variable is a substring. If the indexes
- * are out of range the result is empty. */
+ 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);
+ }
+ }
+ tv_clear(rettv);
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = (char_u *)v;
+ break;
+ }
+ case VAR_LIST: {
+ len = tv_list_len(rettv->vval.v_list);
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)
- s = NULL;
- else
- s = vim_strnsave(s + n1, (int)(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)
- s = NULL;
- else
- s = vim_strnsave(s + n1, 1);
- }
- clear_tv(rettv);
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = s;
- break;
-
- case VAR_LIST:
- len = list_len(rettv->vval.v_list);
- 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)
- EMSGN(_(e_listidx), n1);
- return FAIL;
+ 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) {
+ EMSGN(_(e_listidx), n1);
+ }
+ return FAIL;
+ }
+ n1 = len;
}
- 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 = list_alloc();
- item = list_find(rettv->vval.v_list, n1);
- while (n1++ <= n2) {
- list_append_tv(l, &item->li_tv);
- item = item->li_next;
+ 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();
+ item = tv_list_find(rettv->vval.v_list, n1);
+ while (n1++ <= n2) {
+ tv_list_append_tv(l, &item->li_tv);
+ item = item->li_next;
+ }
+ tv_clear(rettv);
+ rettv->v_type = VAR_LIST;
+ rettv->vval.v_list = l;
+ l->lv_refcount++;
+ } else {
+ tv_copy(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1);
+ tv_clear(rettv);
+ *rettv = var1;
}
- clear_tv(rettv);
- rettv->v_type = VAR_LIST;
- rettv->vval.v_list = l;
- ++l->lv_refcount;
- } else {
- copy_tv(&list_find(rettv->vval.v_list, n1)->li_tv, &var1);
- clear_tv(rettv);
- *rettv = var1;
- }
- break;
-
- case VAR_DICT:
- if (range) {
- if (verbose)
- EMSG(_(e_dictrange));
- if (len == -1)
- clear_tv(&var1);
- return FAIL;
+ break;
}
- {
- dictitem_T *item;
+ case VAR_DICT: {
+ if (range) {
+ if (verbose) {
+ emsgf(_(e_dictrange));
+ }
+ if (len == -1) {
+ tv_clear(&var1);
+ }
+ return FAIL;
+ }
if (len == -1) {
- key = get_tv_string_chk(&var1);
+ key = (char_u *)tv_get_string_chk(&var1);
if (key == NULL) {
- clear_tv(&var1);
+ tv_clear(&var1);
return FAIL;
}
}
- item = dict_find(rettv->vval.v_dict, key, (int)len);
+ dictitem_T *const item = tv_dict_find(rettv->vval.v_dict,
+ (const char *)key, len);
- if (item == NULL && verbose)
- EMSG2(_(e_dictkey), key);
- if (len == -1)
- clear_tv(&var1);
- if (item == NULL)
+ if (item == NULL && verbose) {
+ emsgf(_(e_dictkey), key);
+ }
+ if (len == -1) {
+ tv_clear(&var1);
+ }
+ if (item == NULL) {
return FAIL;
+ }
- copy_tv(&item->di_tv, &var1);
- clear_tv(rettv);
+ tv_copy(&item->di_tv, &var1);
+ tv_clear(rettv);
*rettv = var1;
+ break;
+ }
+ case VAR_SPECIAL:
+ case VAR_FUNC:
+ case VAR_FLOAT:
+ case VAR_PARTIAL:
+ case VAR_UNKNOWN: {
+ break; // Not evaluating, skipping over subscript
}
- break;
- case VAR_SPECIAL:
- case VAR_FUNC:
- case VAR_PARTIAL:
- case VAR_FLOAT:
- case VAR_UNKNOWN:
- break; // Not evaluating, skipping over subscript
}
}
return OK;
}
-/*
- * Get an option value.
- * "arg" points to the '&' or '+' before the option name.
- * "arg" is advanced to character after the option name.
- * Return OK or FAIL.
- */
-static int
-get_option_tv (
- char_u **arg,
- typval_T *rettv, /* when NULL, only check if option exists */
- int evaluate
-)
+// TODO(ZyX-I): move to eval/executor
+
+/// Get an option value
+///
+/// @param[in,out] arg Points to the '&' or '+' before the option name. Is
+/// advanced to the character after the option name.
+/// @param[out] rettv Location where result is saved.
+/// @param[in] evaluate If not true, rettv is not populated.
+///
+/// @return OK or FAIL.
+static int get_option_tv(const char **const arg, typval_T *const rettv,
+ const bool evaluate)
+ FUNC_ATTR_NONNULL_ARG(1)
{
- char_u *option_end;
long numval;
char_u *stringval;
int opt_type;
int c;
- int working = (**arg == '+'); /* has("+option") */
+ bool working = (**arg == '+'); // has("+option")
int ret = OK;
int opt_flags;
- /*
- * Isolate the option name and find its value.
- */
- option_end = find_option_end(arg, &opt_flags);
+ // Isolate the option name and find its value.
+ char *option_end = (char *)find_option_end(arg, &opt_flags);
if (option_end == NULL) {
- if (rettv != NULL)
+ if (rettv != NULL) {
EMSG2(_("E112: Option name missing: %s"), *arg);
+ }
return FAIL;
}
@@ -4725,8 +4641,8 @@ get_option_tv (
c = *option_end;
*option_end = NUL;
- opt_type = get_option_value(*arg, &numval,
- rettv == NULL ? NULL : &stringval, opt_flags);
+ opt_type = get_option_value((char_u *)(*arg), &numval,
+ rettv == NULL ? NULL : &stringval, opt_flags);
if (opt_type == -3) { /* invalid name */
if (rettv != NULL)
@@ -4856,7 +4772,7 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
// Special key, e.g.: "\<C-W>"
case '<':
- extra = trans_special((const char_u **) &p, STRLEN(p), name, true);
+ extra = trans_special((const char_u **)&p, STRLEN(p), name, true, true);
if (extra != 0) {
name += extra;
break;
@@ -4871,7 +4787,10 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
}
*name = NUL;
- *arg = p + 1;
+ if (*p != NUL) { // just in case
+ p++;
+ }
+ *arg = p;
return OK;
}
@@ -4930,18 +4849,35 @@ static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate)
return OK;
}
+/// @return the function name of the partial.
+char_u *partial_name(partial_T *pt)
+{
+ if (pt->pt_name != NULL) {
+ return pt->pt_name;
+ }
+ return pt->pt_func->uf_name;
+}
+
+// TODO(ZyX-I): Move to eval/typval.h
+
static void partial_free(partial_T *pt)
{
for (int i = 0; i < pt->pt_argc; i++) {
- clear_tv(&pt->pt_argv[i]);
+ tv_clear(&pt->pt_argv[i]);
}
xfree(pt->pt_argv);
- dict_unref(pt->pt_dict);
- func_unref(pt->pt_name);
- xfree(pt->pt_name);
+ tv_dict_unref(pt->pt_dict);
+ if (pt->pt_name != NULL) {
+ func_unref(pt->pt_name);
+ xfree(pt->pt_name);
+ } else {
+ func_ptr_unref(pt->pt_func);
+ }
xfree(pt);
}
+// TODO(ZyX-I): Move to eval/typval.h
+
/// Unreference a closure: decrement the reference count and free it when it
/// becomes zero.
void partial_unref(partial_T *pt)
@@ -4960,7 +4896,7 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
listitem_T *item;
if (evaluate) {
- l = list_alloc();
+ l = tv_list_alloc();
}
*arg = skipwhite(*arg + 1);
@@ -4968,10 +4904,10 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */
goto failret;
if (evaluate) {
- item = listitem_alloc();
+ item = tv_list_item_alloc();
item->li_tv = tv;
item->li_tv.v_lock = 0;
- list_append(l, item);
+ tv_list_append(l, item);
}
if (**arg == ']')
@@ -4987,7 +4923,7 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
EMSG2(_("E697: Missing end of List ']': %s"), *arg);
failret:
if (evaluate) {
- list_free(l);
+ tv_list_free(l);
}
return FAIL;
}
@@ -5002,196 +4938,7 @@ failret:
return OK;
}
-/*
- * Allocate an empty header for a list.
- * Caller should take care of the reference count.
- */
-list_T *list_alloc(void) FUNC_ATTR_NONNULL_RET
-{
- list_T *list = xcalloc(1, sizeof(list_T));
-
- /* Prepend the list to the list of lists for garbage collection. */
- if (first_list != NULL)
- first_list->lv_used_prev = list;
- list->lv_used_prev = NULL;
- list->lv_used_next = first_list;
- first_list = list;
- return list;
-}
-
-/*
- * Allocate an empty list for a return value.
- */
-static list_T *rettv_list_alloc(typval_T *rettv)
-{
- list_T *l = list_alloc();
- rettv->vval.v_list = l;
- rettv->v_type = VAR_LIST;
- rettv->v_lock = VAR_UNLOCKED;
- l->lv_refcount++;
- return l;
-}
-
-/// Unreference a list: decrement the reference count and free it when it
-/// becomes zero.
-void list_unref(list_T *l) {
- if (l != NULL && --l->lv_refcount <= 0) {
- list_free(l);
- }
-}
-
-/// Free a list, including all items it points to.
-/// Ignores the reference count.
-static void list_free_contents(list_T *l) {
- listitem_T *item;
-
- for (item = l->lv_first; item != NULL; item = l->lv_first) {
- // Remove the item before deleting it.
- l->lv_first = item->li_next;
- clear_tv(&item->li_tv);
- xfree(item);
- }
-}
-
-static void list_free_list(list_T *l) {
- // Remove the list from the list of lists for garbage collection.
- if (l->lv_used_prev == NULL) {
- first_list = l->lv_used_next;
- } else {
- l->lv_used_prev->lv_used_next = l->lv_used_next;
- }
- if (l->lv_used_next != NULL) {
- l->lv_used_next->lv_used_prev = l->lv_used_prev;
- }
-
- xfree(l);
-}
-
-void list_free(list_T *l) {
- if (!in_free_unref_items) {
- list_free_contents(l);
- list_free_list(l);
- }
-}
-
-/*
- * Allocate a list item.
- * It is not initialized, don't forget to set v_lock.
- */
-listitem_T *listitem_alloc(void) FUNC_ATTR_NONNULL_RET
-{
- return xmalloc(sizeof(listitem_T));
-}
-
-/*
- * Free a list item. Also clears the value. Does not notify watchers.
- */
-void listitem_free(listitem_T *item)
-{
- clear_tv(&item->li_tv);
- xfree(item);
-}
-
-/*
- * Remove a list item from a List and free it. Also clears the value.
- */
-void listitem_remove(list_T *l, listitem_T *item)
-{
- vim_list_remove(l, item, item);
- listitem_free(item);
-}
-
-/*
- * Get the number of items in a list.
- */
-static long list_len(list_T *l)
-{
- if (l == NULL)
- return 0L;
- return l->lv_len;
-}
-
-/*
- * Return TRUE when two lists have exactly the same values.
- */
-static int
-list_equal (
- list_T *l1,
- list_T *l2,
- int ic, /* ignore case for strings */
- int recursive /* TRUE when used recursively */
-)
-{
- listitem_T *item1, *item2;
-
- if (l1 == NULL || l2 == NULL)
- return FALSE;
- if (l1 == l2)
- return TRUE;
- if (list_len(l1) != list_len(l2))
- return FALSE;
-
- for (item1 = l1->lv_first, item2 = l2->lv_first;
- item1 != NULL && item2 != NULL;
- item1 = item1->li_next, item2 = item2->li_next)
- if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive))
- return FALSE;
- return item1 == NULL && item2 == NULL;
-}
-
-/*
- * Return the dictitem that an entry in a hashtable points to.
- */
-dictitem_T *dict_lookup(hashitem_T *hi)
-{
- return HI2DI(hi);
-}
-
-/*
- * Return TRUE when two dictionaries have exactly the same key/values.
- */
-static int
-dict_equal (
- dict_T *d1,
- dict_T *d2,
- int ic, /* ignore case for strings */
- int recursive /* TRUE when used recursively */
-)
-{
- hashitem_T *hi;
- dictitem_T *item2;
- int todo;
-
- if (d1 == NULL && d2 == NULL) {
- return true;
- }
- if (d1 == NULL || d2 == NULL) {
- return false;
- }
- if (d1 == d2) {
- return true;
- }
- if (dict_len(d1) != dict_len(d2)) {
- return false;
- }
-
- todo = (int)d1->dv_hashtab.ht_used;
- for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi) {
- if (!HASHITEM_EMPTY(hi)) {
- item2 = dict_find(d2, hi->hi_key, -1);
- if (item2 == NULL)
- return FALSE;
- if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive))
- return FALSE;
- --todo;
- }
- }
- return TRUE;
-}
-
-static int tv_equal_recurse_limit;
-
-static bool func_equal(
+bool func_equal(
typval_T *tv1,
typval_T *tv2,
bool ic // ignore case
@@ -5202,12 +4949,12 @@ static bool func_equal(
// empty and NULL function name considered the same
s1 = tv1->v_type == VAR_FUNC ? tv1->vval.v_string
- : tv1->vval.v_partial->pt_name;
+ : partial_name(tv1->vval.v_partial);
if (s1 != NULL && *s1 == NUL) {
s1 = NULL;
}
s2 = tv2->v_type == VAR_FUNC ? tv2->vval.v_string
- : tv2->vval.v_partial->pt_name;
+ : partial_name(tv2->vval.v_partial);
if (s2 != NULL && *s2 == NUL) {
s2 = NULL;
}
@@ -5226,7 +4973,7 @@ static bool func_equal(
if (d1 != d2) {
return false;
}
- } else if (!dict_equal(d1, d2, ic, true)) {
+ } else if (!tv_dict_equal(d1, d2, ic, true)) {
return false;
}
@@ -5245,550 +4992,6 @@ static bool func_equal(
return true;
}
-/*
- * Return TRUE if "tv1" and "tv2" have the same value.
- * Compares the items just like "==" would compare them, but strings and
- * numbers are different. Floats and numbers are also different.
- */
-static int
-tv_equal (
- typval_T *tv1,
- typval_T *tv2,
- int ic, /* ignore case */
- int recursive /* TRUE when used recursively */
-)
-{
- char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
- char_u *s1, *s2;
- static int recursive_cnt = 0; /* catch recursive loops */
- int r;
-
- /* Catch lists and dicts that have an endless loop by limiting
- * recursiveness to a limit. We guess they are equal then.
- * A fixed limit has the problem of still taking an awful long time.
- * Reduce the limit every time running into it. That should work fine for
- * deeply linked structures that are not recursively linked and catch
- * recursiveness quickly. */
- if (!recursive)
- tv_equal_recurse_limit = 1000;
- if (recursive_cnt >= tv_equal_recurse_limit) {
- --tv_equal_recurse_limit;
- return TRUE;
- }
-
- // For VAR_FUNC and VAR_PARTIAL only compare the function name.
- if ((tv1->v_type == VAR_FUNC
- || (tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial != NULL))
- && (tv2->v_type == VAR_FUNC
- || (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial != NULL))) {
- recursive_cnt++;
- r = func_equal(tv1, tv2, ic);
- recursive_cnt--;
- return r;
- }
- if (tv1->v_type != tv2->v_type) {
- return false;
- }
-
- switch (tv1->v_type) {
- case VAR_LIST:
- ++recursive_cnt;
- r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, TRUE);
- --recursive_cnt;
- return r;
-
- case VAR_DICT:
- ++recursive_cnt;
- r = dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, TRUE);
- --recursive_cnt;
- return r;
-
- case VAR_NUMBER:
- return tv1->vval.v_number == tv2->vval.v_number;
-
- case VAR_FLOAT:
- return tv1->vval.v_float == tv2->vval.v_float;
-
- case VAR_STRING:
- s1 = get_tv_string_buf(tv1, buf1);
- s2 = get_tv_string_buf(tv2, buf2);
- return (ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2)) == 0;
-
- case VAR_SPECIAL:
- return tv1->vval.v_special == tv2->vval.v_special;
-
- case VAR_FUNC:
- case VAR_PARTIAL:
- case VAR_UNKNOWN:
- // VAR_UNKNOWN can be the result of an invalid expression, let’s say it does
- // not equal anything, not even self.
- return false;
- }
-
- assert(false);
- return false;
-}
-
-/*
- * Locate item with index "n" in list "l" and return it.
- * A negative index is counted from the end; -1 is the last item.
- * Returns NULL when "n" is out of range.
- */
-listitem_T *list_find(list_T *l, long n)
-{
- listitem_T *item;
- long idx;
-
- if (l == NULL)
- return NULL;
-
- /* Negative index is relative to the end. */
- if (n < 0)
- n = l->lv_len + n;
-
- /* Check for index out of range. */
- if (n < 0 || n >= l->lv_len)
- return NULL;
-
- /* When there is a cached index may start search from there. */
- if (l->lv_idx_item != NULL) {
- if (n < l->lv_idx / 2) {
- /* closest to the start of the list */
- item = l->lv_first;
- idx = 0;
- } else if (n > (l->lv_idx + l->lv_len) / 2) {
- /* closest to the end of the list */
- item = l->lv_last;
- idx = l->lv_len - 1;
- } else {
- /* closest to the cached index */
- item = l->lv_idx_item;
- idx = l->lv_idx;
- }
- } else {
- if (n < l->lv_len / 2) {
- /* closest to the start of the list */
- item = l->lv_first;
- idx = 0;
- } else {
- /* closest to the end of the list */
- item = l->lv_last;
- idx = l->lv_len - 1;
- }
- }
-
- while (n > idx) {
- /* search forward */
- item = item->li_next;
- ++idx;
- }
- while (n < idx) {
- /* search backward */
- item = item->li_prev;
- --idx;
- }
-
- /* cache the used index */
- l->lv_idx = idx;
- l->lv_idx_item = item;
-
- return item;
-}
-
-/*
- * Get list item "l[idx]" as a number.
- */
-static long
-list_find_nr (
- list_T *l,
- long idx,
- int *errorp /* set to TRUE when something wrong */
-)
-{
- listitem_T *li;
-
- li = list_find(l, idx);
- if (li == NULL) {
- if (errorp != NULL)
- *errorp = TRUE;
- return -1L;
- }
- return get_tv_number_chk(&li->li_tv, errorp);
-}
-
-/*
- * Get list item "l[idx - 1]" as a string. Returns NULL for failure.
- */
-char_u *list_find_str(list_T *l, long idx)
-{
- listitem_T *li;
-
- li = list_find(l, idx - 1);
- if (li == NULL) {
- EMSGN(_(e_listidx), idx);
- return NULL;
- }
- return get_tv_string(&li->li_tv);
-}
-
-/*
- * Locate "item" list "l" and return its index.
- * Returns -1 when "item" is not in the list.
- */
-static long list_idx_of_item(list_T *l, listitem_T *item)
-{
- long idx = 0;
- listitem_T *li;
-
- if (l == NULL)
- return -1;
- idx = 0;
- for (li = l->lv_first; li != NULL && li != item; li = li->li_next)
- ++idx;
- if (li == NULL)
- return -1;
- return idx;
-}
-
-/*
- * Append item "item" to the end of list "l".
- */
-void list_append(list_T *l, listitem_T *item)
-{
- if (l->lv_last == NULL) {
- /* empty list */
- l->lv_first = item;
- l->lv_last = item;
- item->li_prev = NULL;
- } else {
- l->lv_last->li_next = item;
- item->li_prev = l->lv_last;
- l->lv_last = item;
- }
- ++l->lv_len;
- item->li_next = NULL;
-}
-
-/*
- * Append typval_T "tv" to the end of list "l".
- */
-void list_append_tv(list_T *l, typval_T *tv)
-{
- listitem_T *li = listitem_alloc();
- copy_tv(tv, &li->li_tv);
- list_append(l, li);
-}
-
-/*
- * Add a list to a list.
- */
-void list_append_list(list_T *list, list_T *itemlist)
-{
- listitem_T *li = listitem_alloc();
-
- li->li_tv.v_type = VAR_LIST;
- li->li_tv.v_lock = 0;
- li->li_tv.vval.v_list = itemlist;
- list_append(list, li);
- ++itemlist->lv_refcount;
-}
-
-/*
- * Add a dictionary to a list. Used by getqflist().
- */
-void list_append_dict(list_T *list, dict_T *dict)
-{
- listitem_T *li = listitem_alloc();
-
- li->li_tv.v_type = VAR_DICT;
- li->li_tv.v_lock = 0;
- li->li_tv.vval.v_dict = dict;
- list_append(list, li);
- ++dict->dv_refcount;
-}
-
-/// Make a copy of "str" and append it as an item to list "l"
-///
-/// @param[out] l List to append to.
-/// @param[in] str String to append.
-/// @param[in] len Length of the appended string. May be negative, in this
-/// case string is considered to be usual zero-terminated
-/// string.
-void list_append_string(list_T *l, const char_u *str, int len)
- FUNC_ATTR_NONNULL_ARG(1)
-{
- if (str == NULL) {
- list_append_allocated_string(l, NULL);
- } else {
- list_append_allocated_string(l, (len >= 0
- ? xmemdupz((char *) str, len)
- : xstrdup((char *) str)));
- }
-}
-
-/// Append given string to the list
-///
-/// Unlike list_append_string this function does not copy the string.
-///
-/// @param[out] l List to append to.
-/// @param[in] str String to append.
-void list_append_allocated_string(list_T *l, char *const str)
- FUNC_ATTR_NONNULL_ARG(1)
-{
- listitem_T *li = listitem_alloc();
-
- list_append(l, li);
- li->li_tv.v_type = VAR_STRING;
- li->li_tv.v_lock = 0;
- li->li_tv.vval.v_string = (char_u *) str;
-}
-
-/*
- * Append "n" to list "l".
- */
-void list_append_number(list_T *l, varnumber_T n)
-{
- listitem_T *li = listitem_alloc();
- li->li_tv.v_type = VAR_NUMBER;
- li->li_tv.v_lock = 0;
- li->li_tv.vval.v_number = n;
- list_append(l, li);
-}
-
-/*
- * Insert typval_T "tv" in list "l" before "item".
- * If "item" is NULL append at the end.
- */
-void list_insert_tv(list_T *l, typval_T *tv, listitem_T *item)
-{
- listitem_T *ni = listitem_alloc();
-
- copy_tv(tv, &ni->li_tv);
- list_insert(l, ni, item);
-}
-
-void list_insert(list_T *l, listitem_T *ni, listitem_T *item)
-{
- if (item == NULL)
- /* Append new item at end of list. */
- list_append(l, ni);
- else {
- /* Insert new item before existing item. */
- ni->li_prev = item->li_prev;
- ni->li_next = item;
- if (item->li_prev == NULL) {
- l->lv_first = ni;
- ++l->lv_idx;
- } else {
- item->li_prev->li_next = ni;
- l->lv_idx_item = NULL;
- }
- item->li_prev = ni;
- ++l->lv_len;
- }
-}
-
-/*
- * Extend "l1" with "l2".
- * If "bef" is NULL append at the end, otherwise insert before this item.
- */
-static void list_extend(list_T *l1, list_T *l2, listitem_T *bef)
-{
- listitem_T *item;
- int todo = l2->lv_len;
-
- /* We also quit the loop when we have inserted the original item count of
- * the list, avoid a hang when we extend a list with itself. */
- for (item = l2->lv_first; item != NULL && --todo >= 0; item = item->li_next) {
- list_insert_tv(l1, &item->li_tv, bef);
- }
-}
-
-/*
- * Concatenate lists "l1" and "l2" into a new list, stored in "tv".
- * Return FAIL on failure to copy.
- */
-static int list_concat(list_T *l1, list_T *l2, typval_T *tv)
-{
- list_T *l;
-
- if (l1 == NULL || l2 == NULL)
- return FAIL;
-
- /* make a copy of the first list. */
- l = list_copy(NULL, l1, false, 0);
- if (l == NULL)
- return FAIL;
- tv->v_type = VAR_LIST;
- tv->vval.v_list = l;
-
- /* append all items from the second list */
- list_extend(l, l2, NULL);
- return OK;
-}
-
-/// Make a copy of list
-///
-/// @param[in] conv If non-NULL, then all internal strings will be converted.
-/// @param[in] orig Original list to copy.
-/// @param[in] deep If false, then shallow copy will be done.
-/// @param[in] copyID See var_item_copy().
-///
-/// @return Copied list. May be NULL in case original list is NULL or some
-/// failure happens. The refcount of the new list is set to 1.
-static list_T *list_copy(const vimconv_T *const conv,
- list_T *const orig,
- const bool deep,
- const int copyID)
- FUNC_ATTR_WARN_UNUSED_RESULT
-{
- listitem_T *item;
- listitem_T *ni;
-
- if (orig == NULL)
- return NULL;
-
- list_T *copy = list_alloc();
- if (copyID != 0) {
- /* Do this before adding the items, because one of the items may
- * refer back to this list. */
- orig->lv_copyID = copyID;
- orig->lv_copylist = copy;
- }
- for (item = orig->lv_first; item != NULL && !got_int;
- item = item->li_next) {
- ni = listitem_alloc();
- if (deep) {
- if (var_item_copy(conv, &item->li_tv, &ni->li_tv, deep, copyID) == FAIL) {
- xfree(ni);
- break;
- }
- } else
- copy_tv(&item->li_tv, &ni->li_tv);
- list_append(copy, ni);
- }
- ++copy->lv_refcount;
- if (item != NULL) {
- list_unref(copy);
- copy = NULL;
- }
-
- return copy;
-}
-
-/// Remove items "item" to "item2" from list "l".
-/// @warning Does not free the listitem or the value!
-void vim_list_remove(list_T *l, listitem_T *item, listitem_T *item2)
-{
- // notify watchers
- for (listitem_T *ip = item; ip != NULL; ip = ip->li_next) {
- --l->lv_len;
- list_fix_watch(l, ip);
- if (ip == item2) {
- break;
- }
- }
-
- if (item2->li_next == NULL) {
- l->lv_last = item->li_prev;
- } else {
- item2->li_next->li_prev = item->li_prev;
- }
- if (item->li_prev == NULL) {
- l->lv_first = item2->li_next;
- } else {
- item->li_prev->li_next = item2->li_next;
- }
- l->lv_idx_item = NULL;
-}
-
-typedef struct join_S {
- char_u *s;
- char_u *tofree;
-} join_T;
-
-/// Join list into a string, helper function
-///
-/// @param[out] gap Garray where result will be saved.
-/// @param[in] l List to join.
-/// @param[in] sep Used separator.
-/// @param[in] join_gap Garray to keep each list item string.
-///
-/// @return OK in case of success, FAIL otherwise.
-static int list_join_inner(garray_T *const gap, list_T *const l,
- const char *const sep, garray_T *const join_gap)
- FUNC_ATTR_NONNULL_ALL
-{
- int sumlen = 0;
- bool first = true;
- listitem_T *item;
-
- /* Stringify each item in the list. */
- for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) {
- char *s;
- size_t len;
- s = encode_tv2echo(&item->li_tv, &len);
- if (s == NULL) {
- return FAIL;
- }
-
- sumlen += (int) len;
-
- join_T *const p = GA_APPEND_VIA_PTR(join_T, join_gap);
- p->tofree = p->s = (char_u *) s;
-
- line_breakcheck();
- }
-
- /* Allocate result buffer with its total size, avoid re-allocation and
- * multiple copy operations. Add 2 for a tailing ']' and NUL. */
- if (join_gap->ga_len >= 2)
- sumlen += (int)STRLEN(sep) * (join_gap->ga_len - 1);
- ga_grow(gap, sumlen + 2);
-
- for (int i = 0; i < join_gap->ga_len && !got_int; ++i) {
- if (first) {
- first = false;
- } else {
- ga_concat(gap, (const char_u *) sep);
- }
- const join_T *const p = ((const join_T *)join_gap->ga_data) + i;
-
- if (p->s != NULL)
- ga_concat(gap, p->s);
- line_breakcheck();
- }
-
- return OK;
-}
-
-/// Join list into a string using given separator
-///
-/// @param[out] gap Garray where result will be saved.
-/// @param[in] l Joined list.
-/// @param[in] sep Separator.
-///
-/// @return OK in case of success, FAIL otherwise.
-static int list_join(garray_T *const gap, list_T *const l,
- const char *const sep)
- FUNC_ATTR_NONNULL_ALL
-{
- if (l->lv_len < 1) {
- return OK;
- }
-
- garray_T join_ga;
- int retval;
-
- ga_init(&join_ga, (int)sizeof(join_T), l->lv_len);
- retval = list_join_inner(gap, l, sep, &join_ga);
-
-# define FREE_JOIN_TOFREE(join) xfree((join)->tofree)
- GA_DEEP_CLEAR(&join_ga, join_T, FREE_JOIN_TOFREE);
-
- return retval;
-}
-
/// Get next (unique) copy ID
///
/// Used for traversing nested structures e.g. when serializing them or garbage
@@ -5805,6 +5008,9 @@ int get_copyID(void)
return current_copyID;
}
+// Used by get_func_tv()
+static garray_T funcargs = GA_EMPTY_INIT_VALUE;
+
/*
* Garbage collection for lists and dictionaries.
*
@@ -5827,19 +5033,22 @@ int get_copyID(void)
/// Do garbage collection for lists and dicts.
///
+/// @param testing true if called from test_garbagecollect_now().
/// @returns true if some memory was freed.
-bool garbage_collect(void)
+bool garbage_collect(bool testing)
{
bool abort = false;
#define ABORTING(func) abort = abort || func
- // Only do this once.
- want_garbage_collect = false;
- may_garbage_collect = false;
- garbage_collect_at_exit = false;
+ if (!testing) {
+ // Only do this once.
+ want_garbage_collect = false;
+ may_garbage_collect = false;
+ garbage_collect_at_exit = false;
+ }
- // We advance by two because we add one for items referenced through
- // previous_funccal.
+ // We advance by two (COPYID_INC) because we add one for items referenced
+ // through previous_funccal.
const int copyID = get_copyID();
// 1. Go through all accessible variables and mark all lists and dicts
@@ -5849,6 +5058,7 @@ bool garbage_collect(void)
// referenced through previous_funccal. This must be first, because if
// the item is referenced elsewhere the funccal must not be freed.
for (funccall_T *fc = previous_funccal; fc != NULL; fc = fc->caller) {
+ fc->fc_copyID = copyID + 1;
ABORTING(set_ref_in_ht)(&fc->l_vars.dv_hashtab, copyID + 1, NULL);
ABORTING(set_ref_in_ht)(&fc->l_avars.dv_hashtab, copyID + 1, NULL);
}
@@ -5894,7 +5104,8 @@ bool garbage_collect(void)
do {
yankreg_T reg;
char name = NUL;
- reg_iter = op_register_iter(reg_iter, &name, &reg);
+ bool is_unnamed = false;
+ reg_iter = op_register_iter(reg_iter, &name, &reg, &is_unnamed);
if (name != NUL) {
ABORTING(set_ref_dict)(reg.additional_data, copyID);
}
@@ -5924,10 +5135,14 @@ bool garbage_collect(void)
// function-local variables
for (funccall_T *fc = current_funccal; fc != NULL; fc = fc->caller) {
+ fc->fc_copyID = copyID;
ABORTING(set_ref_in_ht)(&fc->l_vars.dv_hashtab, copyID, NULL);
ABORTING(set_ref_in_ht)(&fc->l_avars.dv_hashtab, copyID, NULL);
}
+ // named functions (matters for closures)
+ ABORTING(set_ref_in_functions(copyID));
+
// Jobs
{
TerminalJobData *data;
@@ -5946,6 +5161,12 @@ bool garbage_collect(void)
})
}
+ // function call arguments, if v:testing is set.
+ for (int i = 0; i < funcargs.ga_len; i++) {
+ ABORTING(set_ref_in_item)(((typval_T **)funcargs.ga_data)[i],
+ copyID, NULL, NULL);
+ }
+
// v: vars
ABORTING(set_ref_in_ht)(&vimvarht, copyID, NULL);
@@ -6000,7 +5221,7 @@ bool garbage_collect(void)
if (did_free_funccal) {
// When a funccal was freed some more items might be garbage
// collected, so run again.
- (void)garbage_collect();
+ (void)garbage_collect(testing);
}
} else if (p_verbose > 0) {
verb_msg((char_u *)_(
@@ -6012,7 +5233,7 @@ bool garbage_collect(void)
/// Free lists and dictionaries that are no longer referenced.
///
-/// Note: This function may only be called from garbage_collect().
+/// @note This function may only be called from garbage_collect().
///
/// @param copyID Free lists/dictionaries that don't have this ID.
/// @return true, if something was freed.
@@ -6025,19 +5246,19 @@ static int free_unref_items(int copyID)
// Let all "free" functions know that we are here. This means no
// dictionaries, lists, or jobs are to be freed, because we will
// do that here.
- in_free_unref_items = true;
+ tv_in_free_unref_items = true;
// PASS 1: free the contents of the items. We don't free the items
// themselves yet, so that it is possible to decrement refcount counters.
// Go through the list of dicts and free items without the copyID.
// Don't free dicts that are referenced internally.
- for (dict_T *dd = first_dict; dd != NULL; dd = dd->dv_used_next) {
+ for (dict_T *dd = gc_first_dict; dd != NULL; dd = dd->dv_used_next) {
if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) {
// Free the Dictionary and ordinary items it contains, but don't
// recurse into Lists and Dictionaries, they will be in the list
// of dicts or list of lists.
- dict_free_contents(dd);
+ tv_dict_free_contents(dd);
did_free = true;
}
}
@@ -6045,36 +5266,36 @@ static int free_unref_items(int copyID)
// Go through the list of lists and free items without the copyID.
// But don't free a list that has a watcher (used in a for loop), these
// are not referenced anywhere.
- for (list_T *ll = first_list; ll != NULL; ll = ll->lv_used_next) {
+ for (list_T *ll = gc_first_list; ll != NULL; ll = ll->lv_used_next) {
if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
&& ll->lv_watch == NULL) {
// Free the List and ordinary items it contains, but don't recurse
// into Lists and Dictionaries, they will be in the list of dicts
// or list of lists.
- list_free_contents(ll);
+ tv_list_free_contents(ll);
did_free = true;
}
}
// PASS 2: free the items themselves.
- for (dd = first_dict; dd != NULL; dd = dd_next) {
+ for (dd = gc_first_dict; dd != NULL; dd = dd_next) {
dd_next = dd->dv_used_next;
if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) {
- dict_free_dict(dd);
+ tv_dict_free_dict(dd);
}
}
- for (ll = first_list; ll != NULL; ll = ll_next) {
+ for (ll = gc_first_list; ll != NULL; ll = ll_next) {
ll_next = ll->lv_used_next;
if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
&& ll->lv_watch == NULL) {
// Free the List and ordinary items it contains, but don't recurse
// into Lists and Dictionaries, they will be in the list of dicts
// or list of lists.
- list_free_list(ll);
+ tv_list_free_list(ll);
}
}
- in_free_unref_items = false;
+ tv_in_free_unref_items = false;
return did_free;
}
@@ -6097,14 +5318,10 @@ bool set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack)
// 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
// list_stack.
- int todo = (int)cur_ht->ht_used;
- for (hashitem_T *hi = cur_ht->ht_array; todo > 0; ++hi) {
- if (!HASHITEM_EMPTY(hi)) {
- --todo;
- abort = abort || set_ref_in_item(&HI2DI(hi)->di_tv, copyID, &ht_stack,
- list_stack);
- }
- }
+ HASHTAB_ITER(cur_ht, hi, {
+ abort = abort || set_ref_in_item(
+ &TV_DICT_HI2DI(hi)->di_tv, copyID, &ht_stack, list_stack);
+ });
}
if (ht_stack == NULL) {
@@ -6196,21 +5413,10 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
QUEUE *w = NULL;
DictWatcher *watcher = NULL;
QUEUE_FOREACH(w, &dd->watchers) {
- watcher = dictwatcher_node_data(w);
+ watcher = tv_dict_watcher_node_data(w);
set_ref_in_callback(&watcher->callback, copyID, ht_stack, list_stack);
}
}
- if (tv->v_type == VAR_PARTIAL) {
- partial_T *pt = tv->vval.v_partial;
- int i;
-
- if (pt != NULL) {
- for (i = 0; i < pt->pt_argc; i++) {
- abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID,
- ht_stack, list_stack);
- }
- }
- }
break;
}
@@ -6240,6 +5446,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
// A partial does not have a copyID, because it cannot contain itself.
if (pt != NULL) {
+ abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID);
if (pt->pt_dict != NULL) {
typval_T dtv;
@@ -6256,6 +5463,8 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
break;
}
case VAR_FUNC:
+ abort = set_ref_in_func(tv->vval.v_string, NULL, copyID);
+ break;
case VAR_UNKNOWN:
case VAR_SPECIAL:
case VAR_FLOAT:
@@ -6267,6 +5476,29 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
return abort;
}
+/// Set "copyID" in all functions available by name.
+bool set_ref_in_functions(int copyID)
+{
+ int todo;
+ hashitem_T *hi = NULL;
+ bool abort = false;
+ ufunc_T *fp;
+
+ todo = (int)func_hashtab.ht_used;
+ for (hi = func_hashtab.ht_array; todo > 0 && !got_int; hi++) {
+ if (!HASHITEM_EMPTY(hi)) {
+ todo--;
+ fp = HI2UF(hi);
+ if (!func_name_refcount(fp->uf_name)) {
+ abort = abort || set_ref_in_func(NULL, fp, copyID);
+ }
+ }
+ }
+ return abort;
+}
+
+
+
/// Mark all lists and dicts referenced in given mark
///
/// @returns true if setting references failed somehow.
@@ -6313,447 +5545,17 @@ static inline bool set_ref_dict(dict_T *dict, int copyID)
return false;
}
-/*
- * Allocate an empty header for a dictionary.
- */
-dict_T *dict_alloc(void) FUNC_ATTR_NONNULL_RET
-{
- dict_T *d = xmalloc(sizeof(dict_T));
-
- /* Add the dict to the list of dicts for garbage collection. */
- if (first_dict != NULL)
- first_dict->dv_used_prev = d;
- d->dv_used_next = first_dict;
- d->dv_used_prev = NULL;
- first_dict = d;
-
- hash_init(&d->dv_hashtab);
- d->dv_lock = 0;
- d->dv_scope = 0;
- d->dv_refcount = 0;
- d->dv_copyID = 0;
- QUEUE_INIT(&d->watchers);
-
- return d;
-}
-
-/*
- * Allocate an empty dict for a return value.
- */
-static void rettv_dict_alloc(typval_T *rettv)
-{
- dict_T *d = dict_alloc();
-
- rettv->vval.v_dict = d;
- rettv->v_type = VAR_DICT;
- rettv->v_lock = VAR_UNLOCKED;
- d->dv_refcount++;
-}
-
-/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary.
-///
-/// @param d The Dictionary to clear
-void dict_clear(dict_T *d)
- FUNC_ATTR_NONNULL_ALL
-{
- hash_lock(&d->dv_hashtab);
- assert(d->dv_hashtab.ht_locked > 0);
-
- size_t todo = d->dv_hashtab.ht_used;
- for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) {
- if (!HASHITEM_EMPTY(hi)) {
- dictitem_free(HI2DI(hi));
- hash_remove(&d->dv_hashtab, hi);
- todo--;
- }
- }
-
- hash_unlock(&d->dv_hashtab);
-}
-
-
-/*
- * Unreference a Dictionary: decrement the reference count and free it when it
- * becomes zero.
- */
-void dict_unref(dict_T *d)
-{
- if (d != NULL && --d->dv_refcount <= 0) {
- dict_free(d);
- }
-}
-
-/// Free a Dictionary, including all items it contains.
-/// Ignores the reference count.
-static void dict_free_contents(dict_T *d) {
- int todo;
- hashitem_T *hi;
- dictitem_T *di;
-
-
- /* Lock the hashtab, we don't want it to resize while freeing items. */
- hash_lock(&d->dv_hashtab);
- assert(d->dv_hashtab.ht_locked > 0);
- todo = (int)d->dv_hashtab.ht_used;
- for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) {
- if (!HASHITEM_EMPTY(hi)) {
- /* Remove the item before deleting it, just in case there is
- * something recursive causing trouble. */
- di = HI2DI(hi);
- hash_remove(&d->dv_hashtab, hi);
- clear_tv(&di->di_tv);
- xfree(di);
- --todo;
- }
- }
-
- while (!QUEUE_EMPTY(&d->watchers)) {
- QUEUE *w = QUEUE_HEAD(&d->watchers);
- DictWatcher *watcher = dictwatcher_node_data(w);
- dictwatcher_free(watcher);
- QUEUE_REMOVE(w);
- }
-
- hash_clear(&d->dv_hashtab);
-}
-
-static void dict_free_dict(dict_T *d) {
- // Remove the dict from the list of dicts for garbage collection.
- if (d->dv_used_prev == NULL) {
- first_dict = d->dv_used_next;
- } else {
- d->dv_used_prev->dv_used_next = d->dv_used_next;
- }
- if (d->dv_used_next != NULL) {
- d->dv_used_next->dv_used_prev = d->dv_used_prev;
- }
-
- xfree(d);
-}
-
-void dict_free(dict_T *d) {
- if (!in_free_unref_items) {
- dict_free_contents(d);
- dict_free_dict(d);
- }
-}
-
-/*
- * Allocate a Dictionary item.
- * The "key" is copied to the new item.
- * Note that the value of the item "di_tv" still needs to be initialized!
- */
-dictitem_T *dictitem_alloc(char_u *key) FUNC_ATTR_NONNULL_RET
-{
- dictitem_T *di = xmalloc(offsetof(dictitem_T, di_key) + STRLEN(key) + 1);
-#ifndef __clang_analyzer__
- STRCPY(di->di_key, key);
-#endif
- di->di_flags = DI_FLAGS_ALLOC;
- return di;
-}
-
-/*
- * Make a copy of a Dictionary item.
- */
-static dictitem_T *dictitem_copy(dictitem_T *org) FUNC_ATTR_NONNULL_RET
-{
- dictitem_T *di = xmalloc(sizeof(dictitem_T) + STRLEN(org->di_key));
-
- STRCPY(di->di_key, org->di_key);
- di->di_flags = DI_FLAGS_ALLOC;
- copy_tv(&org->di_tv, &di->di_tv);
-
- return di;
-}
-
-/*
- * Remove item "item" from Dictionary "dict" and free it.
- */
-static void dictitem_remove(dict_T *dict, dictitem_T *item)
+static bool set_ref_in_funccal(funccall_T *fc, int copyID)
{
- hashitem_T *hi;
-
- hi = hash_find(&dict->dv_hashtab, item->di_key);
- if (HASHITEM_EMPTY(hi)) {
- EMSG2(_(e_intern2), "dictitem_remove()");
- } else {
- hash_remove(&dict->dv_hashtab, hi);
- }
- dictitem_free(item);
-}
-
-/*
- * Free a dict item. Also clears the value.
- */
-void dictitem_free(dictitem_T *item)
-{
- clear_tv(&item->di_tv);
- if (item->di_flags & DI_FLAGS_ALLOC) {
- xfree(item);
- }
-}
-
-/// Make a copy of dictionary
-///
-/// @param[in] conv If non-NULL, then all internal strings will be converted.
-/// @param[in] orig Original dictionary to copy.
-/// @param[in] deep If false, then shallow copy will be done.
-/// @param[in] copyID See var_item_copy().
-///
-/// @return Copied dictionary. May be NULL in case original dictionary is NULL
-/// or some failure happens. The refcount of the new dictionary is set
-/// to 1.
-static dict_T *dict_copy(const vimconv_T *const conv,
- dict_T *const orig,
- const bool deep,
- const int copyID)
-{
- dictitem_T *di;
- int todo;
- hashitem_T *hi;
-
- if (orig == NULL)
- return NULL;
-
- dict_T *copy = dict_alloc();
- {
- if (copyID != 0) {
- orig->dv_copyID = copyID;
- orig->dv_copydict = copy;
- }
- todo = (int)orig->dv_hashtab.ht_used;
- for (hi = orig->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi) {
- if (!HASHITEM_EMPTY(hi)) {
- --todo;
-
- if (conv == NULL || conv->vc_type == CONV_NONE) {
- di = dictitem_alloc(hi->hi_key);
- } else {
- char *const key = (char *) string_convert((vimconv_T *) conv,
- hi->hi_key, NULL);
- if (key == NULL) {
- di = dictitem_alloc(hi->hi_key);
- } else {
- di = dictitem_alloc((char_u *) key);
- xfree(key);
- }
- }
- if (deep) {
- if (var_item_copy(conv, &HI2DI(hi)->di_tv, &di->di_tv, deep,
- copyID) == FAIL) {
- xfree(di);
- break;
- }
- } else
- copy_tv(&HI2DI(hi)->di_tv, &di->di_tv);
- if (dict_add(copy, di) == FAIL) {
- dictitem_free(di);
- break;
- }
- }
- }
-
- ++copy->dv_refcount;
- if (todo > 0) {
- dict_unref(copy);
- copy = NULL;
- }
- }
-
- return copy;
-}
-
-/*
- * Add item "item" to Dictionary "d".
- * Returns FAIL when key already exists.
- */
-int dict_add(dict_T *d, dictitem_T *item)
-{
- return hash_add(&d->dv_hashtab, item->di_key);
-}
-
-/*
- * Add a number or string entry to dictionary "d".
- * When "str" is NULL use number "nr", otherwise use "str".
- * Returns FAIL when key already exists.
- */
-int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str)
-{
- dictitem_T *item;
-
- item = dictitem_alloc((char_u *)key);
- item->di_tv.v_lock = 0;
- if (str == NULL) {
- item->di_tv.v_type = VAR_NUMBER;
- item->di_tv.vval.v_number = nr;
- } else {
- item->di_tv.v_type = VAR_STRING;
- item->di_tv.vval.v_string = vim_strsave(str);
- }
- if (dict_add(d, item) == FAIL) {
- dictitem_free(item);
- return FAIL;
- }
- return OK;
-}
-
-/*
- * Add a list entry to dictionary "d".
- * Returns FAIL when key already exists.
- */
-int dict_add_list(dict_T *d, char *key, list_T *list)
-{
- dictitem_T *item = dictitem_alloc((char_u *)key);
-
- item->di_tv.v_lock = 0;
- item->di_tv.v_type = VAR_LIST;
- item->di_tv.vval.v_list = list;
- if (dict_add(d, item) == FAIL) {
- dictitem_free(item);
- return FAIL;
- }
- ++list->lv_refcount;
- return OK;
-}
-
-/// Add a dict entry to dictionary "d".
-/// Returns FAIL when out of memory and when key already exists.
-int dict_add_dict(dict_T *d, char *key, dict_T *dict)
-{
- dictitem_T *item = dictitem_alloc((char_u *)key);
-
- item->di_tv.v_lock = 0;
- item->di_tv.v_type = VAR_DICT;
- item->di_tv.vval.v_dict = dict;
- if (dict_add(d, item) == FAIL) {
- dictitem_free(item);
- return FAIL;
- }
- dict->dv_refcount++;
- return OK;
-}
-
-/// Set all existing keys in "dict" as read-only.
-///
-/// This does not protect against adding new keys to the Dictionary.
-///
-/// @param dict The dict whose keys should be frozen
-void dict_set_keys_readonly(dict_T *dict)
- FUNC_ATTR_NONNULL_ALL
-{
- size_t todo = dict->dv_hashtab.ht_used;
- for (hashitem_T *hi = dict->dv_hashtab.ht_array; todo > 0 ; hi++) {
- if (HASHITEM_EMPTY(hi)) {
- continue;
- }
- todo--;
- HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
- }
-}
-
-/*
- * Get the number of items in a Dictionary.
- */
-static long dict_len(dict_T *d)
-{
- if (d == NULL)
- return 0L;
- return (long)d->dv_hashtab.ht_used;
-}
-
-/*
- * Find item "key[len]" in Dictionary "d".
- * If "len" is negative use strlen(key).
- * Returns NULL when not found.
- */
-dictitem_T *dict_find(dict_T *d, char_u *key, int len)
-{
-#define AKEYLEN 200
- char_u buf[AKEYLEN];
- char_u *akey;
- char_u *tofree = NULL;
- hashitem_T *hi;
-
- if (d == NULL) {
- return NULL;
- }
- if (len < 0) {
- akey = key;
- } else if (len >= AKEYLEN) {
- tofree = akey = vim_strnsave(key, len);
- } else {
- /* Avoid a malloc/free by using buf[]. */
- STRLCPY(buf, key, len + 1);
- akey = buf;
- }
-
- hi = hash_find(&d->dv_hashtab, akey);
- xfree(tofree);
- if (HASHITEM_EMPTY(hi))
- return NULL;
- return HI2DI(hi);
-}
-
-/// Get a function from a dictionary
-/// @param[out] result The address where a pointer to the wanted callback
-/// will be left.
-/// @return true/false on success/failure.
-static bool get_dict_callback(dict_T *d, char *key, Callback *result)
-{
- dictitem_T *di = dict_find(d, (uint8_t *)key, -1);
-
- if (di == NULL) {
- result->type = kCallbackNone;
- return true;
- }
-
- if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING
- && di->di_tv.v_type != VAR_PARTIAL) {
- EMSG(_("Argument is not a function or function name"));
- result->type = kCallbackNone;
- return false;
- }
-
- typval_T tv;
- copy_tv(&di->di_tv, &tv);
- set_selfdict(&tv, d);
- bool res = callback_from_typval(result, &tv);
- clear_tv(&tv);
- return res;
-}
-
-/// Get a string item from a dictionary.
-///
-/// @param save whether memory should be allocated for the return value
-///
-/// @return the entry or NULL if the entry doesn't exist.
-char_u *get_dict_string(dict_T *d, char *key, bool save)
-{
- dictitem_T *di;
- char_u *s;
-
- di = dict_find(d, (char_u *)key, -1);
- if (di == NULL) {
- return NULL;
- }
- s = get_tv_string(&di->di_tv);
- if (save) {
- s = vim_strsave(s);
- }
- return s;
-}
+ bool abort = false;
-/// Get a number item from a dictionary.
-///
-/// @return the entry or 0 if the entry doesn't exist.
-long get_dict_number(dict_T *d, char *key)
-{
- dictitem_T *di = dict_find(d, (char_u *)key, -1);
- if (di == NULL) {
- return 0;
+ if (fc->fc_copyID != copyID) {
+ fc->fc_copyID = copyID;
+ abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL);
+ abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL);
+ abort = abort || set_ref_in_func(NULL, fc->func, copyID);
}
- return get_tv_number(&di->di_tv);
+ return abort;
}
/*
@@ -6768,7 +5570,7 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate)
char_u *key = NULL;
dictitem_T *item;
char_u *start = skipwhite(*arg + 1);
- char_u buf[NUMBUFLEN];
+ char buf[NUMBUFLEN];
/*
* First check if it's not a curly-braces thing: {expr}.
@@ -6785,7 +5587,7 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate)
}
if (evaluate) {
- d = dict_alloc();
+ d = tv_dict_alloc();
}
tvkey.v_type = VAR_UNKNOWN;
tv.v_type = VAR_UNKNOWN;
@@ -6796,38 +5598,39 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate)
goto failret;
if (**arg != ':') {
EMSG2(_("E720: Missing colon in Dictionary: %s"), *arg);
- clear_tv(&tvkey);
+ tv_clear(&tvkey);
goto failret;
}
if (evaluate) {
- key = get_tv_string_buf_chk(&tvkey, buf);
+ key = (char_u *)tv_get_string_buf_chk(&tvkey, buf);
if (key == NULL) {
- // "key" is NULL when get_tv_string_buf_chk() gave an errmsg
- clear_tv(&tvkey);
+ // "key" is NULL when tv_get_string_buf_chk() gave an errmsg
+ tv_clear(&tvkey);
goto failret;
}
}
*arg = skipwhite(*arg + 1);
- if (eval1(arg, &tv, evaluate) == FAIL) { /* recursive! */
- if (evaluate)
- clear_tv(&tvkey);
+ if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive!
+ if (evaluate) {
+ tv_clear(&tvkey);
+ }
goto failret;
}
if (evaluate) {
- item = dict_find(d, key, -1);
+ item = tv_dict_find(d, (const char *)key, -1);
if (item != NULL) {
EMSG2(_("E721: Duplicate key in Dictionary: \"%s\""), key);
- clear_tv(&tvkey);
- clear_tv(&tv);
+ tv_clear(&tvkey);
+ tv_clear(&tv);
goto failret;
}
- item = dictitem_alloc(key);
- clear_tv(&tvkey);
+ item = tv_dict_item_alloc((const char *)key);
+ tv_clear(&tvkey);
item->di_tv = tv;
item->di_tv.v_lock = 0;
- if (dict_add(d, item) == FAIL) {
- dictitem_free(item);
+ if (tv_dict_add(d, item) == FAIL) {
+ tv_dict_item_free(item);
}
}
@@ -6844,7 +5647,7 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate)
EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg);
failret:
if (evaluate) {
- dict_free(d);
+ tv_dict_free(d);
}
return FAIL;
}
@@ -6859,6 +5662,232 @@ failret:
return OK;
}
+/// Get function arguments.
+static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs,
+ int *varargs, bool skip)
+{
+ bool mustend = false;
+ char_u *arg = *argp;
+ char_u *p = arg;
+ int c;
+ int i;
+
+ if (newargs != NULL) {
+ ga_init(newargs, (int)sizeof(char_u *), 3);
+ }
+
+ if (varargs != NULL) {
+ *varargs = false;
+ }
+
+ // Isolate the arguments: "arg1, arg2, ...)"
+ while (*p != endchar) {
+ if (p[0] == '.' && p[1] == '.' && p[2] == '.') {
+ if (varargs != NULL) {
+ *varargs = true;
+ }
+ p += 3;
+ mustend = true;
+ } else {
+ arg = p;
+ while (ASCII_ISALNUM(*p) || *p == '_') {
+ p++;
+ }
+ if (arg == p || isdigit(*arg)
+ || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0)
+ || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0)) {
+ if (!skip) {
+ EMSG2(_("E125: Illegal argument: %s"), arg);
+ }
+ break;
+ }
+ if (newargs != NULL) {
+ ga_grow(newargs, 1);
+ c = *p;
+ *p = NUL;
+ arg = vim_strsave(arg);
+ if (arg == NULL) {
+ *p = c;
+ goto err_ret;
+ }
+
+ // Check for duplicate argument name.
+ for (i = 0; i < newargs->ga_len; i++) {
+ if (STRCMP(((char_u **)(newargs->ga_data))[i], arg) == 0) {
+ EMSG2(_("E853: Duplicate argument name: %s"), arg);
+ xfree(arg);
+ goto err_ret;
+ }
+ }
+ ((char_u **)(newargs->ga_data))[newargs->ga_len] = arg;
+ newargs->ga_len++;
+
+ *p = c;
+ }
+ if (*p == ',') {
+ p++;
+ } else {
+ mustend = true;
+ }
+ }
+ p = skipwhite(p);
+ if (mustend && *p != endchar) {
+ if (!skip) {
+ EMSG2(_(e_invarg2), *argp);
+ }
+ break;
+ }
+ }
+ if (*p != endchar) {
+ goto err_ret;
+ }
+ p++; // skip "endchar"
+
+ *argp = p;
+ return OK;
+
+err_ret:
+ if (newargs != NULL) {
+ ga_clear_strings(newargs);
+ }
+ return FAIL;
+}
+
+/// Register function "fp" as using "current_funccal" as its scope.
+static void register_closure(ufunc_T *fp)
+{
+ if (fp->uf_scoped == current_funccal) {
+ // no change
+ return;
+ }
+ funccal_unref(fp->uf_scoped, fp, false);
+ fp->uf_scoped = current_funccal;
+ current_funccal->fc_refcount++;
+ ga_grow(&current_funccal->fc_funcs, 1);
+ ((ufunc_T **)current_funccal->fc_funcs.ga_data)
+ [current_funccal->fc_funcs.ga_len++] = fp;
+}
+
+/// Parse a lambda expression and get a Funcref from "*arg".
+///
+/// @return OK or FAIL. Returns NOTDONE for dict or {expr}.
+static int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
+{
+ garray_T newargs = GA_EMPTY_INIT_VALUE;
+ garray_T *pnewargs;
+ ufunc_T *fp = NULL;
+ int varargs;
+ int ret;
+ char_u *start = skipwhite(*arg + 1);
+ char_u *s, *e;
+ static int lambda_no = 0;
+ int *old_eval_lavars = eval_lavars_used;
+ int eval_lavars = false;
+
+ // First, check if this is a lambda expression. "->" must exists.
+ ret = get_function_args(&start, '-', NULL, NULL, true);
+ if (ret == FAIL || *start != '>') {
+ return NOTDONE;
+ }
+
+ // Parse the arguments again.
+ if (evaluate) {
+ pnewargs = &newargs;
+ } else {
+ pnewargs = NULL;
+ }
+ *arg = skipwhite(*arg + 1);
+ ret = get_function_args(arg, '-', pnewargs, &varargs, false);
+ if (ret == FAIL || **arg != '>') {
+ goto errret;
+ }
+
+ // Set up a flag for checking local variables and arguments.
+ if (evaluate) {
+ eval_lavars_used = &eval_lavars;
+ }
+
+ // Get the start and the end of the expression.
+ *arg = skipwhite(*arg + 1);
+ s = *arg;
+ ret = skip_expr(arg);
+ if (ret == FAIL) {
+ goto errret;
+ }
+ e = *arg;
+ *arg = skipwhite(*arg);
+ if (**arg != '}') {
+ goto errret;
+ }
+ (*arg)++;
+
+ if (evaluate) {
+ int len, flags = 0;
+ char_u *p;
+ char_u name[20];
+ partial_T *pt;
+ garray_T newlines;
+
+ lambda_no++;
+ snprintf((char *)name, sizeof(name), "<lambda>%d", lambda_no);
+
+ fp = (ufunc_T *)xcalloc(1, sizeof(ufunc_T) + STRLEN(name));
+ pt = (partial_T *)xcalloc(1, sizeof(partial_T));
+ if (pt == NULL) {
+ xfree(fp);
+ goto errret;
+ }
+
+ ga_init(&newlines, (int)sizeof(char_u *), 1);
+ ga_grow(&newlines, 1);
+
+ // Add "return " before the expression.
+ len = 7 + e - s + 1;
+ p = (char_u *)xmalloc(len);
+ ((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
+ STRCPY(p, "return ");
+ STRLCPY(p + 7, s, e - s + 1);
+
+ fp->uf_refcount = 1;
+ STRCPY(fp->uf_name, name);
+ hash_add(&func_hashtab, UF2HIKEY(fp));
+ fp->uf_args = newargs;
+ fp->uf_lines = newlines;
+ if (current_funccal != NULL && eval_lavars) {
+ flags |= FC_CLOSURE;
+ register_closure(fp);
+ } else {
+ fp->uf_scoped = NULL;
+ }
+
+ fp->uf_tml_count = NULL;
+ fp->uf_tml_total = NULL;
+ fp->uf_tml_self = NULL;
+ fp->uf_profiling = false;
+ if (prof_def_func()) {
+ func_do_profile(fp);
+ }
+ fp->uf_varargs = true;
+ fp->uf_flags = flags;
+ fp->uf_calls = 0;
+ fp->uf_script_ID = current_SID;
+
+ pt->pt_func = fp;
+ pt->pt_refcount = 1;
+ rettv->vval.v_partial = pt;
+ rettv->v_type = VAR_PARTIAL;
+ }
+
+ eval_lavars_used = old_eval_lavars;
+ return OK;
+
+errret:
+ ga_clear_strings(&newargs);
+ xfree(fp);
+ eval_lavars_used = old_eval_lavars;
+ return FAIL;
+}
+
/// Convert the string to a floating point number
///
/// This uses strtod(). setlocale(LC_NUMERIC, "C") has been used earlier to
@@ -6873,6 +5902,19 @@ size_t string2float(const char *const text, float_T *const ret_value)
{
char *s = NULL;
+ // MS-Windows does not deal with "inf" and "nan" properly
+ if (STRNICMP(text, "inf", 3) == 0) {
+ *ret_value = INFINITY;
+ return 3;
+ }
+ if (STRNICMP(text, "-inf", 3) == 0) {
+ *ret_value = -INFINITY;
+ return 4;
+ }
+ if (STRNICMP(text, "nan", 3) == 0) {
+ *ret_value = NAN;
+ return 3;
+ }
*ret_value = strtod(text, &s);
return (size_t) (s - text);
}
@@ -6893,7 +5935,7 @@ static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate)
++*arg;
name = *arg;
- len = get_env_len(arg);
+ len = get_env_len((const char_u **)arg);
if (evaluate) {
if (len == 0) {
@@ -6981,64 +6023,67 @@ char_u *get_expr_name(expand_T *xp, int idx)
return get_user_var_name(xp, ++intidx);
}
-
-
-
/// Find internal function in hash functions
///
/// @param[in] name Name of the function.
///
/// Returns pointer to the function definition or NULL if not found.
-static VimLFuncDef *find_internal_func(const char *const name)
+static const VimLFuncDef *find_internal_func(const char *const name)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL
{
size_t len = strlen(name);
return find_internal_func_gperf(name, len);
}
-/// Check if "name" is a variable of type VAR_FUNC. If so, return the function
-/// name it contains, otherwise return "name".
-/// If "partialp" is not NULL, and "name" is of type VAR_PARTIAL also set
-/// "partialp".
-static char_u *deref_func_name(
- char_u *name, int *lenp,
- partial_T **partialp, bool no_autoload
-)
+/// Return name of the function corresponding to `name`
+///
+/// If `name` points to variable that is either a function or partial then
+/// corresponding function name is returned. Otherwise it returns `name` itself.
+///
+/// @param[in] name Function name to check.
+/// @param[in,out] lenp Location where length of the returned name is stored.
+/// Must be set to the length of the `name` argument.
+/// @param[out] partialp Location where partial will be stored if found
+/// function appears to be a partial. May be NULL if this
+/// is not needed.
+/// @param[in] no_autoload If true, do not source autoload scripts if function
+/// was not found.
+///
+/// @return name of the function.
+static char_u *deref_func_name(const char *name, int *lenp,
+ partial_T **const partialp, bool no_autoload)
+ FUNC_ATTR_NONNULL_ARG(1, 2)
{
- dictitem_T *v;
- int cc;
if (partialp != NULL) {
*partialp = NULL;
}
- cc = name[*lenp];
- name[*lenp] = NUL;
- v = find_var(name, NULL, no_autoload);
- name[*lenp] = cc;
+ dictitem_T *const v = find_var(name, (size_t)(*lenp), NULL, no_autoload);
if (v != NULL && v->di_tv.v_type == VAR_FUNC) {
- if (v->di_tv.vval.v_string == NULL) {
+ if (v->di_tv.vval.v_string == NULL) { // just in case
*lenp = 0;
- return (char_u *)""; /* just in case */
+ return (char_u *)"";
}
*lenp = (int)STRLEN(v->di_tv.vval.v_string);
return v->di_tv.vval.v_string;
}
if (v != NULL && v->di_tv.v_type == VAR_PARTIAL) {
- partial_T *pt = v->di_tv.vval.v_partial;
+ partial_T *const pt = v->di_tv.vval.v_partial;
- if (pt == NULL) {
+ if (pt == NULL) { // just in case
*lenp = 0;
- return (char_u *)""; // just in case
+ return (char_u *)"";
}
if (partialp != NULL) {
*partialp = pt;
}
- *lenp = (int)STRLEN(pt->pt_name);
- return pt->pt_name;
+ char_u *s = partial_name(pt);
+ *lenp = (int)STRLEN(s);
+ return s;
}
- return name;
+ return (char_u *)name;
}
/*
@@ -7087,9 +6132,24 @@ get_func_tv (
ret = FAIL;
if (ret == OK) {
- ret = call_func(name, len, rettv, argcount, argvars,
+ int i = 0;
+
+ if (get_vim_var_nr(VV_TESTING)) {
+ // Prepare for calling garbagecollect_for_testing(), need to know
+ // what variables are used on the call stack.
+ if (funcargs.ga_itemsize == 0) {
+ ga_init(&funcargs, (int)sizeof(typval_T *), 50);
+ }
+ for (i = 0; i < argcount; i++) {
+ ga_grow(&funcargs, 1);
+ ((typval_T **)funcargs.ga_data)[funcargs.ga_len++] = &argvars[i];
+ }
+ }
+ ret = call_func(name, len, rettv, argcount, argvars, NULL,
firstline, lastline, doesrange, evaluate,
partial, selfdict);
+
+ funcargs.ga_len -= i;
} else if (!aborting()) {
if (argcount == MAX_FUNC_ARGS) {
emsg_funcname(N_("E740: Too many arguments for function %s"), name);
@@ -7098,40 +6158,57 @@ get_func_tv (
}
}
- while (--argcount >= 0)
- clear_tv(&argvars[argcount]);
+ while (--argcount >= 0) {
+ tv_clear(&argvars[argcount]);
+ }
*arg = skipwhite(argp);
return ret;
}
-#define ERROR_UNKNOWN 0
-#define ERROR_TOOMANY 1
-#define ERROR_TOOFEW 2
-#define ERROR_SCRIPT 3
-#define ERROR_DICT 4
-#define ERROR_NONE 5
-#define ERROR_OTHER 6
-#define ERROR_BOTH 7
+typedef enum {
+ ERROR_UNKNOWN = 0,
+ ERROR_TOOMANY,
+ ERROR_TOOFEW,
+ ERROR_SCRIPT,
+ ERROR_DICT,
+ ERROR_NONE,
+ ERROR_OTHER,
+ ERROR_BOTH,
+ ERROR_DELETED,
+} FnameTransError;
+
#define FLEN_FIXED 40
-/// In a script change <SID>name() and s:name() to K_SNR 123_name().
-/// Change <SNR>123_name() to K_SNR 123_name().
-/// Use "fname_buf[FLEN_FIXED + 1]" when it fits, otherwise allocate memory
-/// (slow).
-static char_u *
-fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error) {
- int llen;
+/// In a script transform script-local names into actually used names
+///
+/// Transforms "<SID>" and "s:" prefixes to `K_SNR {N}` (e.g. K_SNR "123") and
+/// "<SNR>" prefix to `K_SNR`. Uses `fname_buf` buffer that is supposed to have
+/// #FLEN_FIXED + 1 length when it fits, otherwise it allocates memory.
+///
+/// @param[in] name Name to transform.
+/// @param fname_buf Buffer to save resulting function name to, if it fits.
+/// Must have at least #FLEN_FIXED + 1 length.
+/// @param[out] tofree Location where pointer to an allocated memory is saved
+/// in case result does not fit into fname_buf.
+/// @param[out] error Location where error type is saved, @see
+/// FnameTransError.
+///
+/// @return transformed name: either `fname_buf` or a pointer to an allocated
+/// memory.
+static char_u *fname_trans_sid(const char_u *const name,
+ char_u *const fname_buf,
+ char_u **const tofree, int *const error)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
char_u *fname;
- int i;
-
- llen = eval_fname_script(name);
+ const int llen = eval_fname_script((const char *)name);
if (llen > 0) {
fname_buf[0] = K_SPECIAL;
fname_buf[1] = KS_EXTRA;
fname_buf[2] = (int)KE_SNR;
- i = 3;
- if (eval_fname_sid(name)) { // "<SID>" or "s:"
+ int i = 3;
+ if (eval_fname_sid((const char *)name)) { // "<SID>" or "s:"
if (current_SID <= 0) {
*error = ERROR_SCRIPT;
} else {
@@ -7154,23 +6231,60 @@ fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error) {
}
}
} else {
- fname = name;
+ fname = (char_u *)name;
}
return fname;
}
+/// Mark all lists and dicts referenced through function "name" with "copyID".
+/// "list_stack" is used to add lists to be marked. Can be NULL.
+/// "ht_stack" is used to add hashtabs to be marked. Can be NULL.
+///
+/// @return true if setting references failed somehow.
+bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID)
+{
+ ufunc_T *fp = fp_in;
+ funccall_T *fc;
+ int error = ERROR_NONE;
+ char_u fname_buf[FLEN_FIXED + 1];
+ char_u *tofree = NULL;
+ char_u *fname;
+ bool abort = false;
+ if (name == NULL && fp_in == NULL) {
+ return false;
+ }
+
+ if (fp_in == NULL) {
+ fname = fname_trans_sid(name, fname_buf, &tofree, &error);
+ fp = find_func(fname);
+ }
+ if (fp != NULL) {
+ for (fc = fp->uf_scoped; fc != NULL; fc = fc->func->uf_scoped) {
+ abort = abort || set_ref_in_funccal(fc, copyID);
+ }
+ }
+ xfree(tofree);
+ return abort;
+}
+
/// Call a function with its resolved parameters
+///
+/// "argv_func", when not NULL, can be used to fill in arguments only when the
+/// invoked function uses them. It is called like this:
+/// new_argcount = argv_func(current_argcount, argv, called_func_argcount)
+///
/// Return FAIL when the function can't be called, OK otherwise.
/// Also returns OK when an error was encountered while executing the function.
int
call_func(
- char_u *funcname, // name of the function
+ const char_u *funcname, // name of the function
int len, // length of "name"
typval_T *rettv, // return value goes here
int argcount_in, // number of "argvars"
typval_T *argvars_in, // vars for arguments, must have "argcount"
// PLUS ONE elements!
+ ArgvFunc argv_func, // function to fill in argvars
linenr_T firstline, // first line of range
linenr_T lastline, // last line of range
int *doesrange, // return: function handled range
@@ -7213,7 +6327,7 @@ call_func(
}
if (error == ERROR_NONE && partial->pt_argc > 0) {
for (argv_clear = 0; argv_clear < partial->pt_argc; argv_clear++) {
- copy_tv(&partial->pt_argv[argv_clear], &argv[argv_clear]);
+ tv_copy(&partial->pt_argv[argv_clear], &argv[argv_clear]);
}
for (int i = 0; i < argcount_in; i++) {
argv[i + argv_clear] = argvars_in[i];
@@ -7237,44 +6351,53 @@ call_func(
rettv->vval.v_number = 0;
error = ERROR_UNKNOWN;
- if (!builtin_function(rfname, -1)) {
- /*
- * User defined function.
- */
- fp = find_func(rfname);
+ if (!builtin_function((const char *)rfname, -1)) {
+ // User defined function.
+ if (partial != NULL && partial->pt_func != NULL) {
+ fp = partial->pt_func;
+ } else {
+ fp = find_func(rfname);
+ }
- /* Trigger FuncUndefined event, may load the function. */
+ // Trigger FuncUndefined event, may load the function.
if (fp == NULL
&& apply_autocmds(EVENT_FUNCUNDEFINED, rfname, rfname, TRUE, NULL)
&& !aborting()) {
/* executed an autocommand, search for the function again */
fp = find_func(rfname);
}
- /* Try loading a package. */
- if (fp == NULL && script_autoload(rfname, TRUE) && !aborting()) {
- /* loaded a package, search for the function again */
+ // Try loading a package.
+ if (fp == NULL && script_autoload((const char *)rfname, STRLEN(rfname),
+ true) && !aborting()) {
+ // Loaded a package, search for the function again.
fp = find_func(rfname);
}
- if (fp != NULL) {
- if (fp->uf_flags & FC_RANGE)
- *doesrange = TRUE;
- if (argcount < fp->uf_args.ga_len)
+ if (fp != NULL && (fp->uf_flags & FC_DELETED)) {
+ error = ERROR_DELETED;
+ } else if (fp != NULL) {
+ if (argv_func != NULL) {
+ argcount = argv_func(argcount, argvars, fp->uf_args.ga_len);
+ }
+ if (fp->uf_flags & FC_RANGE) {
+ *doesrange = true;
+ }
+ if (argcount < fp->uf_args.ga_len) {
error = ERROR_TOOFEW;
- else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len)
+ } else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len) {
error = ERROR_TOOMANY;
- else if ((fp->uf_flags & FC_DICT) && selfdict == NULL)
+ } else if ((fp->uf_flags & FC_DICT) && selfdict == NULL) {
error = ERROR_DICT;
- else {
+ } else {
// Call the user function.
call_user_func(fp, argcount, argvars, rettv, firstline, lastline,
- (fp->uf_flags & FC_DICT) ? selfdict : NULL);
+ (fp->uf_flags & FC_DICT) ? selfdict : NULL);
error = ERROR_NONE;
}
}
} else {
// Find the function name in the table, call its implementation.
- VimLFuncDef *const fdef = find_internal_func((char *)fname);
+ const VimLFuncDef *const fdef = find_internal_func((const char *)fname);
if (fdef != NULL) {
if (argcount < fdef->min_argc) {
error = ERROR_TOOFEW;
@@ -7311,6 +6434,9 @@ call_func(
case ERROR_UNKNOWN:
emsg_funcname(N_("E117: Unknown function: %s"), name);
break;
+ case ERROR_DELETED:
+ emsg_funcname(N_("E933: Function was deleted: %s"), name);
+ break;
case ERROR_TOOMANY:
emsg_funcname(e_toomanyarg, name);
break;
@@ -7330,7 +6456,7 @@ call_func(
}
while (argv_clear > 0) {
- clear_tv(&argv[--argv_clear]);
+ tv_clear(&argv[--argv_clear]);
}
xfree(tofree);
xfree(name);
@@ -7360,11 +6486,13 @@ static void emsg_funcname(char *ermsg, char_u *name)
*/
static int non_zero_arg(typval_T *argvars)
{
- return (argvars[0].v_type == VAR_NUMBER
- && argvars[0].vval.v_number != 0)
- || (argvars[0].v_type == VAR_STRING
- && argvars[0].vval.v_string != NULL
- && *argvars[0].vval.v_string != NUL);
+ return ((argvars[0].v_type == VAR_NUMBER
+ && argvars[0].vval.v_number != 0)
+ || (argvars[0].v_type == VAR_SPECIAL
+ && argvars[0].vval.v_special == kSpecialVarTrue)
+ || (argvars[0].v_type == VAR_STRING
+ && argvars[0].vval.v_string != NULL
+ && *argvars[0].vval.v_string != NUL));
}
/*********************************************
@@ -7372,24 +6500,6 @@ static int non_zero_arg(typval_T *argvars)
*/
-/*
- * Get the float value of "argvars[0]" into "f".
- * Returns FAIL when the argument is not a Number or Float.
- */
-static inline int get_float_arg(typval_T *argvars, float_T *f)
-{
- if (argvars[0].v_type == VAR_FLOAT) {
- *f = argvars[0].vval.v_float;
- return OK;
- }
- if (argvars[0].v_type == VAR_NUMBER) {
- *f = (float_T)argvars[0].vval.v_number;
- return OK;
- }
- EMSG(_("E808: Number or Float required"));
- return FAIL;
-}
-
// Apply a floating point C function on a typval with one float_T.
//
// Some versions of glibc on i386 have an optimization that makes it harder to
@@ -7401,7 +6511,7 @@ static void float_op_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
float_T (*function)(float_T) = (float_T (*)(float_T))fptr;
rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &f) == OK) {
+ if (tv_get_float_chk(argvars, &f)) {
rettv->vval.v_float = function(f);
} else {
rettv->vval.v_float = 0.0;
@@ -7419,9 +6529,9 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
Error err = ERROR_INIT;
- Object result = fn(INTERNAL_CALL, args, &err);
+ Object result = fn(VIML_INTERNAL_CALL, args, &err);
- if (err.set) {
+ if (ERROR_SET(&err)) {
nvim_err_writeln(cstr_as_string(err.msg));
goto end;
}
@@ -7433,6 +6543,7 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
end:
api_free_array(args);
api_free_object(result);
+ api_clear_error(&err);
}
/*
@@ -7444,15 +6555,16 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr)
float_op_wrapper(argvars, rettv, (FunPtr)&fabs);
} else {
varnumber_T n;
- int error = FALSE;
+ bool error = false;
- n = get_tv_number_chk(&argvars[0], &error);
- if (error)
+ n = tv_get_number_chk(&argvars[0], &error);
+ if (error) {
rettv->vval.v_number = -1;
- else if (n > 0)
+ } else if (n > 0) {
rettv->vval.v_number = n;
- else
+ } else {
rettv->vval.v_number = -n;
+ }
}
}
@@ -7466,13 +6578,13 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = 1; /* Default: Failed */
if (argvars[0].v_type == VAR_LIST) {
if ((l = argvars[0].vval.v_list) != NULL
- && !tv_check_lock(l->lv_lock,
- (char_u *)N_("add() argument"), true)) {
- list_append_tv(l, &argvars[1]);
- copy_tv(&argvars[0], rettv);
+ && !tv_check_lock(l->lv_lock, "add() argument", TV_TRANSLATE)) {
+ tv_list_append_tv(l, &argvars[1]);
+ tv_copy(&argvars[0], rettv);
}
- } else
+ } else {
EMSG(_(e_listreq));
+ }
}
/*
@@ -7480,8 +6592,8 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_and(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL)
- & get_tv_number_chk(&argvars[1], NULL);
+ rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL)
+ & tv_get_number_chk(&argvars[1], NULL);
}
@@ -7499,7 +6611,6 @@ static void f_api_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
long lnum;
- char_u *line;
list_T *l = NULL;
listitem_T *li = NULL;
typval_T *tv;
@@ -7512,7 +6623,7 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr)
u_sync(TRUE);
}
- lnum = get_tv_lnum(argvars);
+ lnum = tv_get_lnum(argvars);
if (lnum >= 0
&& lnum <= curbuf->b_ml.ml_line_count
&& u_save(lnum, lnum + 1) == OK) {
@@ -7523,21 +6634,23 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr)
li = l->lv_first;
}
for (;; ) {
- if (l == NULL)
- tv = &argvars[1]; /* append a string */
- else if (li == NULL)
- break; /* end of list */
- else
- tv = &li->li_tv; /* append item from list */
- line = get_tv_string_chk(tv);
- if (line == NULL) { /* type error */
- rettv->vval.v_number = 1; /* Failed */
+ if (l == NULL) {
+ tv = &argvars[1]; // Append a string.
+ } else if (li == NULL) {
+ break; // End of list.
+ } else {
+ tv = &li->li_tv; // Append item from list.
+ }
+ const char *const line = tv_get_string_chk(tv);
+ if (line == NULL) { // Type error.
+ rettv->vval.v_number = 1; // Failed.
break;
}
- ml_append(lnum + added, line, (colnr_T)0, FALSE);
- ++added;
- if (l == NULL)
+ ml_append(lnum + added, (char_u *)line, (colnr_T)0, false);
+ added++;
+ if (l == NULL) {
break;
+ }
li = li->li_next;
}
@@ -7582,16 +6695,19 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int idx;
if (argvars[0].v_type != VAR_UNKNOWN) {
- idx = get_tv_number_chk(&argvars[0], NULL);
- if (idx >= 0 && idx < ARGCOUNT)
- rettv->vval.v_string = vim_strsave(alist_name(&ARGLIST[idx]));
- else
+ idx = (int)tv_get_number_chk(&argvars[0], NULL);
+ if (idx >= 0 && idx < ARGCOUNT) {
+ rettv->vval.v_string = (char_u *)xstrdup(
+ (const char *)alist_name(&ARGLIST[idx]));
+ } else {
rettv->vval.v_string = NULL;
+ }
rettv->v_type = VAR_STRING;
} else {
- rettv_list_alloc(rettv);
- for (idx = 0; idx < ARGCOUNT; ++idx) {
- list_append_string(rettv->vval.v_list, alist_name(&ARGLIST[idx]), -1);
+ tv_list_alloc_ret(rettv);
+ for (idx = 0; idx < ARGCOUNT; idx++) {
+ tv_list_append_string(rettv->vval.v_list,
+ (const char *)alist_name(&ARGLIST[idx]), -1);
}
}
}
@@ -7663,10 +6779,10 @@ static void assert_error(garray_T *gap)
if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL) {
// Make sure v:errors is a list.
- set_vim_var_list(VV_ERRORS, list_alloc());
+ set_vim_var_list(VV_ERRORS, tv_list_alloc());
}
- list_append_string(vimvars[VV_ERRORS].vv_list,
- gap->ga_data, gap->ga_len);
+ tv_list_append_string(vimvars[VV_ERRORS].vv_list,
+ (const char *)gap->ga_data, (ptrdiff_t)gap->ga_len);
}
static void assert_equal_common(typval_T *argvars, assert_type_T atype)
@@ -7695,12 +6811,23 @@ static void f_assert_notequal(typval_T *argvars, typval_T *rettv, FunPtr fptr)
assert_equal_common(argvars, ASSERT_NOTEQUAL);
}
+/// "assert_report(msg)
+static void f_assert_report(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ garray_T ga;
+
+ prepare_assert_error(&ga);
+ ga_concat(&ga, (const char_u *)tv_get_string(&argvars[0]));
+ assert_error(&ga);
+ ga_clear(&ga);
+}
+
/// "assert_exception(string[, msg])" function
static void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
garray_T ga;
- char *error = (char *)get_tv_string_chk(&argvars[0]);
+ const char *const error = tv_get_string_chk(&argvars[0]);
if (vimvars[VV_EXCEPTION].vv_str == NULL) {
prepare_assert_error(&ga);
ga_concat(&ga, (char_u *)"v:exception is not set");
@@ -7719,22 +6846,22 @@ static void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "assert_fails(cmd [, error])" function
static void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *cmd = get_tv_string_chk(&argvars[0]);
+ const char *const cmd = tv_get_string_chk(&argvars[0]);
garray_T ga;
called_emsg = false;
suppress_errthrow = true;
emsg_silent = true;
- do_cmdline_cmd((char *)cmd);
+ do_cmdline_cmd(cmd);
if (!called_emsg) {
prepare_assert_error(&ga);
- ga_concat(&ga, (char_u *)"command did not fail: ");
- ga_concat(&ga, cmd);
+ ga_concat(&ga, (const char_u *)"command did not fail: ");
+ ga_concat(&ga, (const char_u *)cmd);
assert_error(&ga);
ga_clear(&ga);
} else if (argvars[1].v_type != VAR_UNKNOWN) {
- char_u buf[NUMBUFLEN];
- char *error = (char *)get_tv_string_buf_chk(&argvars[1], buf);
+ char buf[NUMBUFLEN];
+ const char *const error = tv_get_string_buf_chk(&argvars[1], buf);
if (error == NULL
|| strstr((char *)vimvars[VV_ERRMSG].vv_str, error) == NULL) {
@@ -7753,14 +6880,39 @@ static void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr)
set_vim_var_string(VV_ERRMSG, NULL, 0);
}
+void assert_inrange(typval_T *argvars)
+{
+ bool error = false;
+ const varnumber_T lower = tv_get_number_chk(&argvars[0], &error);
+ const varnumber_T upper = tv_get_number_chk(&argvars[1], &error);
+ const varnumber_T actual = tv_get_number_chk(&argvars[2], &error);
+
+ if (error) {
+ return;
+ }
+ if (actual < lower || actual > upper) {
+ garray_T ga;
+ prepare_assert_error(&ga);
+
+ char msg[55];
+ vim_snprintf(msg, sizeof(msg),
+ "range %" PRIdVARNUMBER " - %" PRIdVARNUMBER ",",
+ lower, upper);
+ fill_assert_error(&ga, &argvars[3], (char_u *)msg, NULL, &argvars[2],
+ ASSERT_INRANGE);
+ assert_error(&ga);
+ ga_clear(&ga);
+ }
+}
+
// Common for assert_true() and assert_false().
static void assert_bool(typval_T *argvars, bool is_true)
{
- int error = (int)false;
+ bool error = false;
garray_T ga;
if ((argvars[0].v_type != VAR_NUMBER
- || (get_tv_number_chk(&argvars[0], &error) == 0) == is_true
+ || (tv_get_number_chk(&argvars[0], &error) == 0) == is_true
|| error)
&& (argvars[0].v_type != VAR_SPECIAL
|| (argvars[0].vval.v_special
@@ -7784,14 +6936,15 @@ static void f_assert_false(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void assert_match_common(typval_T *argvars, assert_type_T atype)
{
- char_u buf1[NUMBUFLEN];
- char_u buf2[NUMBUFLEN];
- char_u *pat = get_tv_string_buf_chk(&argvars[0], buf1);
- char_u *text = get_tv_string_buf_chk(&argvars[1], buf2);
+ char buf1[NUMBUFLEN];
+ char buf2[NUMBUFLEN];
+ const char *const pat = tv_get_string_buf_chk(&argvars[0], buf1);
+ const char *const text = tv_get_string_buf_chk(&argvars[1], buf2);
if (pat == NULL || text == NULL) {
EMSG(_(e_invarg));
- } else if (pattern_match(pat, text, false) != (atype == ASSERT_MATCH)) {
+ } else if (pattern_match((char_u *)pat, (char_u *)text, false)
+ != (atype == ASSERT_MATCH)) {
garray_T ga;
prepare_assert_error(&ga);
fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], atype);
@@ -7800,6 +6953,12 @@ static void assert_match_common(typval_T *argvars, assert_type_T atype)
}
}
+/// "assert_inrange(lower, upper[, msg])" function
+static void f_assert_inrange(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ assert_inrange(argvars);
+}
+
/// "assert_match(pattern, actual[, msg])" function
static void f_assert_match(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -7823,14 +6982,15 @@ static void f_assert_true(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- float_T fx, fy;
+ float_T fx;
+ float_T fy;
rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &fx) == OK
- && get_float_arg(&argvars[1], &fy) == OK)
+ if (tv_get_float_chk(argvars, &fx) && tv_get_float_chk(&argvars[1], &fy)) {
rettv->vval.v_float = atan2(fx, fy);
- else
+ } else {
rettv->vval.v_float = 0.0;
+ }
}
/*
@@ -7954,17 +7114,17 @@ static buf_T *get_buf_tv(typval_T *tv, int curtab_only)
*/
static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- buf_T *buf;
-
- (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */
- ++emsg_off;
- buf = get_buf_tv(&argvars[0], FALSE);
rettv->v_type = VAR_STRING;
- if (buf != NULL && buf->b_fname != NULL)
- rettv->vval.v_string = vim_strsave(buf->b_fname);
- else
- rettv->vval.v_string = NULL;
- --emsg_off;
+ rettv->vval.v_string = NULL;
+ if (!tv_check_str_or_nr(&argvars[0])) {
+ return;
+ }
+ emsg_off++;
+ const buf_T *const buf = get_buf_tv(&argvars[0], false);
+ emsg_off--;
+ if (buf != NULL && buf->b_fname != NULL) {
+ rettv->vval.v_string = (char_u *)xstrdup((char *)buf->b_fname);
+ }
}
/*
@@ -7972,36 +7132,36 @@ static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- buf_T *buf;
- int error = FALSE;
- char_u *name;
+ bool error = false;
- (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */
- ++emsg_off;
- buf = get_buf_tv(&argvars[0], FALSE);
- --emsg_off;
+ rettv->vval.v_number = -1;
+ if (!tv_check_str_or_nr(&argvars[0])) {
+ return;
+ }
+ emsg_off++;
+ const buf_T *buf = get_buf_tv(&argvars[0], false);
+ emsg_off--;
- /* If the buffer isn't found and the second argument is not zero create a
- * new buffer. */
+ // If the buffer isn't found and the second argument is not zero create a
+ // new buffer.
+ const char *name;
if (buf == NULL
&& argvars[1].v_type != VAR_UNKNOWN
- && get_tv_number_chk(&argvars[1], &error) != 0
+ && tv_get_number_chk(&argvars[1], &error) != 0
&& !error
- && (name = get_tv_string_chk(&argvars[0])) != NULL
- && !error)
- buf = buflist_new(name, NULL, (linenr_T)1, 0);
+ && (name = tv_get_string_chk(&argvars[0])) != NULL
+ && !error) {
+ buf = buflist_new((char_u *)name, NULL, 1, 0);
+ }
- if (buf != NULL)
+ if (buf != NULL) {
rettv->vval.v_number = buf->b_fnum;
- else
- rettv->vval.v_number = -1;
+ }
}
static void buf_win_common(typval_T *argvars, typval_T *rettv, bool get_nr)
{
- int error = false;
- (void)get_tv_number_chk(&argvars[0], &error); // issue errmsg if type error
- if (error) { // the argument has an invalid type
+ if (!tv_check_str_or_nr(&argvars[0])) {
rettv->vval.v_number = -1;
return;
}
@@ -8045,36 +7205,34 @@ static void f_bufwinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_byte2line(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- long boff = 0;
-
- boff = get_tv_number(&argvars[0]) - 1; /* boff gets -1 on type error */
- if (boff < 0)
+ long boff = tv_get_number(&argvars[0]) - 1;
+ if (boff < 0) {
rettv->vval.v_number = -1;
- else
- rettv->vval.v_number = ml_find_line_or_offset(curbuf,
- (linenr_T)0, &boff);
+ } else {
+ rettv->vval.v_number = (varnumber_T)ml_find_line_or_offset(curbuf, 0,
+ &boff);
+ }
}
static void byteidx(typval_T *argvars, typval_T *rettv, int comp)
{
- char_u *t;
- char_u *str;
- long idx;
-
- str = get_tv_string_chk(&argvars[0]);
- idx = get_tv_number_chk(&argvars[1], NULL);
+ const char *const str = tv_get_string_chk(&argvars[0]);
+ varnumber_T idx = tv_get_number_chk(&argvars[1], NULL);
rettv->vval.v_number = -1;
- if (str == NULL || idx < 0)
+ if (str == NULL || idx < 0) {
return;
+ }
- t = str;
+ const char *t = str;
for (; idx > 0; idx--) {
- if (*t == NUL) /* EOL reached */
+ if (*t == NUL) { // EOL reached.
return;
- if (enc_utf8 && comp)
- t += utf_ptr2len(t);
- else
- t += (*mb_ptr2len)(t);
+ }
+ if (enc_utf8 && comp) {
+ t += utf_ptr2len((const char_u *)t);
+ } else {
+ t += (*mb_ptr2len)((const char_u *)t);
+ }
}
rettv->vval.v_number = (varnumber_T)(t - str);
}
@@ -8113,18 +7271,19 @@ int func_call(char_u *name, typval_T *args, partial_T *partial,
/* Make a copy of each argument. This is needed to be able to set
* v_lock to VAR_FIXED in the copy without changing the original list.
*/
- copy_tv(&item->li_tv, &argv[argc++]);
+ tv_copy(&item->li_tv, &argv[argc++]);
}
if (item == NULL) {
- r = call_func(name, (int)STRLEN(name), rettv, argc, argv,
+ r = call_func(name, (int)STRLEN(name), rettv, argc, argv, NULL,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&dummy, true, partial, selfdict);
}
- /* Free the arguments. */
- while (argc > 0)
- clear_tv(&argv[--argc]);
+ // Free the arguments.
+ while (argc > 0) {
+ tv_clear(&argv[--argc]);
+ }
return r;
}
@@ -8132,24 +7291,24 @@ int func_call(char_u *name, typval_T *args, partial_T *partial,
/// "call(func, arglist [, dict])" function
static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *func;
- partial_T *partial = NULL;
- dict_T *selfdict = NULL;
-
if (argvars[1].v_type != VAR_LIST) {
EMSG(_(e_listreq));
return;
}
- if (argvars[1].vval.v_list == NULL)
+ if (argvars[1].vval.v_list == NULL) {
return;
+ }
+ char_u *func;
+ partial_T *partial = NULL;
+ dict_T *selfdict = NULL;
if (argvars[0].v_type == VAR_FUNC) {
func = argvars[0].vval.v_string;
} else if (argvars[0].v_type == VAR_PARTIAL) {
partial = argvars[0].vval.v_partial;
- func = partial->pt_name;
+ func = partial_name(partial);
} else {
- func = get_tv_string(&argvars[0]);
+ func = (char_u *)tv_get_string(&argvars[0]);
}
if (*func == NUL) {
return; // type error or empty name
@@ -8179,18 +7338,14 @@ static void f_changenr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- if (has_mbyte) {
- int utf8 = 0;
-
- if (argvars[1].v_type != VAR_UNKNOWN)
- utf8 = get_tv_number_chk(&argvars[1], NULL);
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ if (!tv_check_num(&argvars[1])) {
+ return;
+ }
+ }
- if (utf8)
- rettv->vval.v_number = (*utf_ptr2char)(get_tv_string(&argvars[0]));
- else
- rettv->vval.v_number = (*mb_ptr2char)(get_tv_string(&argvars[0]));
- } else
- rettv->vval.v_number = get_tv_string(&argvars[0])[0];
+ rettv->vval.v_number = utf_ptr2char(
+ (const char_u *)tv_get_string(&argvars[0]));
}
/*
@@ -8202,7 +7357,7 @@ static void f_cindent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
linenr_T lnum;
pos = curwin->w_cursor;
- lnum = get_tv_lnum(argvars);
+ lnum = tv_get_lnum(argvars);
if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
curwin->w_cursor.lnum = lnum;
rettv->vval.v_number = get_c_indent();
@@ -8261,8 +7416,6 @@ static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- int startcol;
-
if ((State & INSERT) == 0) {
EMSG(_("E785: complete() can only be used in Insert mode"));
return;
@@ -8278,9 +7431,10 @@ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- startcol = get_tv_number_chk(&argvars[0], NULL);
- if (startcol <= 0)
+ const colnr_T startcol = tv_get_number_chk(&argvars[0], NULL);
+ if (startcol <= 0) {
return;
+ }
set_completion(startcol - 1, argvars[1].vval.v_list);
}
@@ -8311,47 +7465,51 @@ static void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *message;
- char_u *buttons = NULL;
- char_u buf[NUMBUFLEN];
- char_u buf2[NUMBUFLEN];
+ char buf[NUMBUFLEN];
+ char buf2[NUMBUFLEN];
+ const char *message;
+ const char *buttons = NULL;
int def = 1;
int type = VIM_GENERIC;
- char_u *typestr;
- int error = FALSE;
+ const char *typestr;
+ bool error = false;
- message = get_tv_string_chk(&argvars[0]);
- if (message == NULL)
- error = TRUE;
+ message = tv_get_string_chk(&argvars[0]);
+ if (message == NULL) {
+ error = true;
+ }
if (argvars[1].v_type != VAR_UNKNOWN) {
- buttons = get_tv_string_buf_chk(&argvars[1], buf);
- if (buttons == NULL)
- error = TRUE;
+ buttons = tv_get_string_buf_chk(&argvars[1], buf);
+ if (buttons == NULL) {
+ error = true;
+ }
if (argvars[2].v_type != VAR_UNKNOWN) {
- def = get_tv_number_chk(&argvars[2], &error);
+ def = tv_get_number_chk(&argvars[2], &error);
if (argvars[3].v_type != VAR_UNKNOWN) {
- typestr = get_tv_string_buf_chk(&argvars[3], buf2);
- if (typestr == NULL)
- error = TRUE;
- else {
+ typestr = tv_get_string_buf_chk(&argvars[3], buf2);
+ if (typestr == NULL) {
+ error = true;
+ } else {
switch (TOUPPER_ASC(*typestr)) {
- case 'E': type = VIM_ERROR; break;
- case 'Q': type = VIM_QUESTION; break;
- case 'I': type = VIM_INFO; break;
- case 'W': type = VIM_WARNING; break;
- case 'G': type = VIM_GENERIC; break;
+ case 'E': type = VIM_ERROR; break;
+ case 'Q': type = VIM_QUESTION; break;
+ case 'I': type = VIM_INFO; break;
+ case 'W': type = VIM_WARNING; break;
+ case 'G': type = VIM_GENERIC; break;
}
}
}
}
}
- if (buttons == NULL || *buttons == NUL)
- buttons = (char_u *)_("&Ok");
+ if (buttons == NULL || *buttons == NUL) {
+ buttons = _("&Ok");
+ }
- if (!error)
- rettv->vval.v_number = do_dialog(type, NULL, message, buttons,
- def, NULL, FALSE);
+ if (!error) {
+ rettv->vval.v_number = do_dialog(
+ type, NULL, (char_u *)message, (char_u *)buttons, def, NULL, false);
+ }
}
/*
@@ -8378,15 +7536,16 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if ((l = argvars[0].vval.v_list) != NULL) {
li = l->lv_first;
if (argvars[2].v_type != VAR_UNKNOWN) {
- int error = FALSE;
+ bool error = false;
- ic = get_tv_number_chk(&argvars[2], &error);
+ ic = tv_get_number_chk(&argvars[2], &error);
if (argvars[3].v_type != VAR_UNKNOWN) {
- idx = get_tv_number_chk(&argvars[3], &error);
+ idx = tv_get_number_chk(&argvars[3], &error);
if (!error) {
- li = list_find(l, idx);
- if (li == NULL)
+ li = tv_list_find(l, idx);
+ if (li == NULL) {
EMSGN(_(e_listidx), idx);
+ }
}
}
if (error)
@@ -8403,20 +7562,22 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
hashitem_T *hi;
if ((d = argvars[0].vval.v_dict) != NULL) {
- int error = FALSE;
+ bool error = false;
if (argvars[2].v_type != VAR_UNKNOWN) {
- ic = get_tv_number_chk(&argvars[2], &error);
- if (argvars[3].v_type != VAR_UNKNOWN)
+ ic = tv_get_number_chk(&argvars[2], &error);
+ if (argvars[3].v_type != VAR_UNKNOWN) {
EMSG(_(e_invarg));
+ }
}
todo = error ? 0 : (int)d->dv_hashtab.ht_used;
for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) {
if (!HASHITEM_EMPTY(hi)) {
- --todo;
- if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE))
- ++n;
+ todo--;
+ if (tv_equal(&TV_DICT_HI2DI(hi)->di_tv, &argvars[1], ic, false)) {
+ n++;
+ }
}
}
}
@@ -8433,19 +7594,21 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_cscope_connection(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int num = 0;
- char_u *dbpath = NULL;
- char_u *prepend = NULL;
- char_u buf[NUMBUFLEN];
+ const char *dbpath = NULL;
+ const char *prepend = NULL;
+ char buf[NUMBUFLEN];
if (argvars[0].v_type != VAR_UNKNOWN
&& argvars[1].v_type != VAR_UNKNOWN) {
- num = (int)get_tv_number(&argvars[0]);
- dbpath = get_tv_string(&argvars[1]);
- if (argvars[2].v_type != VAR_UNKNOWN)
- prepend = get_tv_string_buf(&argvars[2], buf);
+ num = (int)tv_get_number(&argvars[0]);
+ dbpath = tv_get_string(&argvars[1]);
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ prepend = tv_get_string_buf(&argvars[2], buf);
+ }
}
- rettv->vval.v_number = cs_connection(num, dbpath, prepend);
+ rettv->vval.v_number = cs_connection(num, (char_u *)dbpath,
+ (char_u *)prepend);
}
/// "cursor(lnum, col)" function, or
@@ -8478,10 +7641,10 @@ static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr)
set_curswant = false;
}
} else {
- line = get_tv_lnum(argvars);
- col = get_tv_number_chk(&argvars[1], NULL);
+ line = tv_get_lnum(argvars);
+ col = (long)tv_get_number_chk(&argvars[1], NULL);
if (argvars[2].v_type != VAR_UNKNOWN) {
- coladd = get_tv_number_chk(&argvars[2], NULL);
+ coladd = (long)tv_get_number_chk(&argvars[2], NULL);
}
}
if (line < 0 || col < 0
@@ -8514,10 +7677,11 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int noref = 0;
- if (argvars[1].v_type != VAR_UNKNOWN)
- noref = get_tv_number_chk(&argvars[1], NULL);
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ noref = tv_get_number_chk(&argvars[1], NULL);
+ }
if (noref < 0 || noref > 1) {
- EMSG(_(e_invarg));
+ emsgf(_(e_invarg));
} else {
var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0
? get_copyID()
@@ -8528,34 +7692,32 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// "delete()" function
static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u nbuf[NUMBUFLEN];
- char_u *name;
- char_u *flags;
-
rettv->vval.v_number = -1;
if (check_restricted() || check_secure()) {
return;
}
- name = get_tv_string(&argvars[0]);
+ const char *const name = tv_get_string(&argvars[0]);
if (name == NULL || *name == NUL) {
EMSG(_(e_invarg));
return;
}
+ char nbuf[NUMBUFLEN];
+ const char *flags;
if (argvars[1].v_type != VAR_UNKNOWN) {
- flags = get_tv_string_buf(&argvars[1], nbuf);
+ flags = tv_get_string_buf(&argvars[1], nbuf);
} else {
- flags = (char_u *)"";
+ flags = "";
}
if (*flags == NUL) {
// delete a file
- rettv->vval.v_number = os_remove((char *)name) == 0 ? 0 : -1;
- } else if (STRCMP(flags, "d") == 0) {
+ rettv->vval.v_number = os_remove(name) == 0 ? 0 : -1;
+ } else if (strcmp(flags, "d") == 0) {
// delete an empty directory
- rettv->vval.v_number = os_rmdir((char *)name) == 0 ? 0 : -1;
- } else if (STRCMP(flags, "rf") == 0) {
+ rettv->vval.v_number = os_rmdir(name) == 0 ? 0 : -1;
+ } else if (strcmp(flags, "rf") == 0) {
// delete a directory recursively
rettv->vval.v_number = delete_recursive(name);
} else {
@@ -8571,39 +7733,34 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (argvars[0].v_type != VAR_DICT) {
- EMSG2(e_invarg2, "dict");
+ emsgf(_(e_invarg2), "dict");
return;
- }
-
- if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_NUMBER) {
- EMSG2(e_invarg2, "key");
+ } else if (argvars[0].vval.v_dict == NULL) {
+ const char *const arg_errmsg = _("dictwatcheradd() argument");
+ const size_t arg_errmsg_len = strlen(arg_errmsg);
+ emsgf(_(e_readonlyvar), (int)arg_errmsg_len, arg_errmsg);
return;
}
- if (argvars[2].v_type != VAR_FUNC && argvars[2].v_type != VAR_STRING) {
- EMSG2(e_invarg2, "funcref");
+ if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_NUMBER) {
+ emsgf(_(e_invarg2), "key");
return;
}
- char *key_pattern = (char *)get_tv_string_chk(argvars + 1);
- assert(key_pattern);
- const size_t key_len = STRLEN(argvars[1].vval.v_string);
-
- if (key_len == 0) {
- EMSG(_(e_emptykey));
+ const char *const key_pattern = tv_get_string_chk(argvars + 1);
+ if (key_pattern == NULL) {
return;
}
+ const size_t key_pattern_len = strlen(key_pattern);
Callback callback;
if (!callback_from_typval(&callback, &argvars[2])) {
+ emsgf(_(e_invarg2), "funcref");
return;
}
- DictWatcher *watcher = xmalloc(sizeof(DictWatcher));
- watcher->key_pattern = xmemdupz(key_pattern, key_len);
- watcher->callback = callback;
- watcher->busy = false;
- QUEUE_INSERT_TAIL(&argvars[0].vval.v_dict->watchers, &watcher->node);
+ tv_dict_watcher_add(argvars[0].vval.v_dict, key_pattern, key_pattern_len,
+ callback);
}
// dictwatcherdel(dict, key, funcref) function
@@ -8614,26 +7771,17 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (argvars[0].v_type != VAR_DICT) {
- EMSG2(e_invarg2, "dict");
- return;
- }
-
- if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_NUMBER) {
- EMSG2(e_invarg2, "key");
+ emsgf(_(e_invarg2), "dict");
return;
}
if (argvars[2].v_type != VAR_FUNC && argvars[2].v_type != VAR_STRING) {
- EMSG2(e_invarg2, "funcref");
+ emsgf(_(e_invarg2), "funcref");
return;
}
- char *key_pattern = (char *)get_tv_string_chk(argvars + 1);
- assert(key_pattern);
- const size_t key_len = STRLEN(argvars[1].vval.v_string);
-
- if (key_len == 0) {
- EMSG(_(e_emptykey));
+ const char *const key_pattern = tv_get_string_chk(argvars + 1);
+ if (key_pattern == NULL) {
return;
}
@@ -8642,28 +7790,12 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- dict_T *dict = argvars[0].vval.v_dict;
- QUEUE *w = NULL;
- DictWatcher *watcher = NULL;
- bool matched = false;
- QUEUE_FOREACH(w, &dict->watchers) {
- watcher = dictwatcher_node_data(w);
- if (callback_equal(&watcher->callback, &callback)
- && !strcmp(watcher->key_pattern, key_pattern)) {
- matched = true;
- break;
- }
- }
-
- callback_free(&callback);
-
- if (!matched) {
+ if (!tv_dict_watcher_remove(argvars[0].vval.v_dict, key_pattern,
+ strlen(key_pattern), callback)) {
EMSG("Couldn't find a watcher matching key and callback");
- return;
}
- QUEUE_REMOVE(w);
- dictwatcher_free(watcher);
+ callback_free(&callback);
}
/*
@@ -8679,7 +7811,7 @@ static void f_did_filetype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_diff_filler(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv->vval.v_number = diff_check_fill(curwin, get_tv_lnum(argvars));
+ rettv->vval.v_number = diff_check_fill(curwin, tv_get_lnum(argvars));
}
/*
@@ -8687,7 +7819,7 @@ static void f_diff_filler(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- linenr_T lnum = get_tv_lnum(argvars);
+ linenr_T lnum = tv_get_lnum(argvars);
static linenr_T prev_lnum = 0;
static int changedtick = 0;
static int fnum = 0;
@@ -8722,11 +7854,12 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (hlID == HLF_CHD || hlID == HLF_TXD) {
- col = get_tv_number(&argvars[1]) - 1; /* ignore type error in {col} */
- if (col >= change_start && col <= change_end)
- hlID = HLF_TXD; /* changed text */
- else
- hlID = HLF_CHD; /* changed line */
+ col = tv_get_number(&argvars[1]) - 1; // Ignore type error in {col}.
+ if (col >= change_start && col <= change_end) {
+ hlID = HLF_TXD; // Changed text.
+ } else {
+ hlID = HLF_CHD; // Changed line.
+ }
}
rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)hlID;
}
@@ -8777,10 +7910,11 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u buf[NUMBUFLEN];
+ char buf[NUMBUFLEN];
- rettv->vval.v_string = vim_strsave_escaped(get_tv_string(&argvars[0]),
- get_tv_string_buf(&argvars[1], buf));
+ rettv->vval.v_string = vim_strsave_escaped(
+ (const char_u *)tv_get_string(&argvars[0]),
+ (const char_u *)tv_get_string_buf(&argvars[1], buf));
rettv->v_type = VAR_STRING;
}
@@ -8789,16 +7923,15 @@ static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_eval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *s;
-
- s = get_tv_string_chk(&argvars[0]);
- if (s != NULL)
- s = skipwhite(s);
+ const char *s = tv_get_string_chk(&argvars[0]);
+ if (s != NULL) {
+ s = (const char *)skipwhite((const char_u *)s);
+ }
- char_u *p = s;
- if (s == NULL || eval1(&s, rettv, TRUE) == FAIL) {
- if (p != NULL && !aborting()) {
- EMSG2(_(e_invexpr2), p);
+ const char *const expr_start = s;
+ if (s == NULL || eval1((char_u **)&s, rettv, true) == FAIL) {
+ if (expr_start != NULL && !aborting()) {
+ EMSG2(_(e_invexpr2), expr_start);
}
need_clr_eos = FALSE;
rettv->v_type = VAR_NUMBER;
@@ -8821,91 +7954,92 @@ static void f_eventhandler(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *name = get_tv_string(&argvars[0]);
+ const char *name = tv_get_string(&argvars[0]);
// Check in $PATH and also check directly if there is a directory name
- rettv->vval.v_number = os_can_exe(name, NULL, true)
- || (gettail_dir(name) != name && os_can_exe(name, NULL, false));
+ rettv->vval.v_number = (
+ os_can_exe((const char_u *)name, NULL, true)
+ || (gettail_dir(name) != name
+ && os_can_exe((const char_u *)name, NULL, false)));
}
-static char_u * get_list_line(int c, void *cookie, int indent)
+static char_u *get_list_line(int c, void *cookie, int indent)
{
- listitem_T **p = (listitem_T **)cookie;
- listitem_T *item = *p;
- char_u buf[NUMBUFLEN];
- char_u *s;
+ const listitem_T **const p = (const listitem_T **)cookie;
+ const listitem_T *item = *p;
if (item == NULL) {
return NULL;
}
- s = get_tv_string_buf_chk(&item->li_tv, buf);
+ char buf[NUMBUFLEN];
+ const char *const s = tv_get_string_buf_chk(&item->li_tv, buf);
*p = item->li_next;
- return s == NULL ? NULL : vim_strsave(s);
+ return (char_u *)(s == NULL ? NULL : xstrdup(s));
}
// "execute(command)" function
static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- int save_msg_silent = msg_silent;
- int save_emsg_silent = emsg_silent;
- bool save_emsg_noredir = emsg_noredir;
- garray_T *save_capture_ga = capture_ga;
+ const int save_msg_silent = msg_silent;
+ const int save_emsg_silent = emsg_silent;
+ const bool save_emsg_noredir = emsg_noredir;
+ garray_T *const save_capture_ga = capture_ga;
- if (check_secure()) {
- return;
- }
+ if (check_secure()) {
+ return;
+ }
- if (argvars[1].v_type != VAR_UNKNOWN) {
- char_u buf[NUMBUFLEN];
- char_u *s = get_tv_string_buf_chk(&argvars[1], buf);
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ char buf[NUMBUFLEN];
+ const char *const s = tv_get_string_buf_chk(&argvars[1], buf);
- if (s == NULL) {
- return;
- }
- if (STRNCMP(s, "silent", 6) == 0) {
- msg_silent++;
- }
- if (STRCMP(s, "silent!") == 0) {
- emsg_silent = true;
- emsg_noredir = true;
- }
- } else {
+ if (s == NULL) {
+ return;
+ }
+ if (strncmp(s, "silent", 6) == 0) {
msg_silent++;
}
-
- garray_T capture_local;
- ga_init(&capture_local, (int)sizeof(char), 80);
- capture_ga = &capture_local;
-
- if (argvars[0].v_type != VAR_LIST) {
- do_cmdline_cmd((char *)get_tv_string(&argvars[0]));
- } else if (argvars[0].vval.v_list != NULL) {
- list_T *list = argvars[0].vval.v_list;
- list->lv_refcount++;
- listitem_T *item = list->lv_first;
- do_cmdline(NULL, get_list_line, (void *)&item,
- DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED);
- list->lv_refcount--;
+ if (strcmp(s, "silent!") == 0) {
+ emsg_silent = true;
+ emsg_noredir = true;
}
- msg_silent = save_msg_silent;
- emsg_silent = save_emsg_silent;
- emsg_noredir = save_emsg_noredir;
+ } else {
+ msg_silent++;
+ }
- ga_append(capture_ga, NUL);
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = vim_strsave(capture_ga->ga_data);
- ga_clear(capture_ga);
+ garray_T capture_local;
+ ga_init(&capture_local, (int)sizeof(char), 80);
+ capture_ga = &capture_local;
- capture_ga = save_capture_ga;
+ if (argvars[0].v_type != VAR_LIST) {
+ do_cmdline_cmd(tv_get_string(&argvars[0]));
+ } else if (argvars[0].vval.v_list != NULL) {
+ list_T *const list = argvars[0].vval.v_list;
+ list->lv_refcount++;
+ listitem_T *const item = list->lv_first;
+ do_cmdline(NULL, get_list_line, (void *)&item,
+ DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED);
+ list->lv_refcount--;
+ }
+ msg_silent = save_msg_silent;
+ emsg_silent = save_emsg_silent;
+ emsg_noredir = save_emsg_noredir;
+
+ ga_append(capture_ga, NUL);
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = vim_strsave(capture_ga->ga_data);
+ ga_clear(capture_ga);
+
+ capture_ga = save_capture_ga;
}
/// "exepath()" function
static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *arg = get_tv_string(&argvars[0]);
+ const char *arg = tv_get_string(&argvars[0]);
char_u *path = NULL;
- (void)os_can_exe(arg, &path, true);
+ (void)os_can_exe((const char_u *)arg, &path, true);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = path;
@@ -8916,53 +8050,55 @@ static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *p;
- char_u *name;
- int n = FALSE;
+ int n = false;
int len = 0;
- p = get_tv_string(&argvars[0]);
- if (*p == '$') { /* environment variable */
- /* first try "normal" environment variables (fast) */
- if (os_getenv((char *)(p + 1)) != NULL)
- n = TRUE;
- else {
- /* try expanding things like $VIM and ${HOME} */
- p = expand_env_save(p);
- if (p != NULL && *p != '$')
- n = TRUE;
- xfree(p);
+ const char *p = tv_get_string(&argvars[0]);
+ if (*p == '$') { // Environment variable.
+ // First try "normal" environment variables (fast).
+ if (os_getenv(p + 1) != NULL) {
+ n = true;
+ } else {
+ // Try expanding things like $VIM and ${HOME}.
+ char_u *const exp = expand_env_save((char_u *)p);
+ if (exp != NULL && *exp != '$') {
+ n = true;
+ }
+ xfree(exp);
+ }
+ } else if (*p == '&' || *p == '+') { // Option.
+ n = (get_option_tv(&p, NULL, true) == OK);
+ if (*skipwhite((const char_u *)p) != NUL) {
+ n = false; // Trailing garbage.
}
- } else if (*p == '&' || *p == '+') { /* option */
- n = (get_option_tv(&p, NULL, TRUE) == OK);
- if (*skipwhite(p) != NUL)
- n = FALSE; /* trailing garbage */
- } else if (*p == '*') { /* internal or user defined function */
- n = function_exists(p + 1);
+ } else if (*p == '*') { // Internal or user defined function.
+ n = function_exists(p + 1, false);
} else if (*p == ':') {
n = cmd_exists(p + 1);
} else if (*p == '#') {
- if (p[1] == '#')
+ if (p[1] == '#') {
n = autocmd_supported(p + 2);
- else
+ } else {
n = au_exists(p + 1);
- } else { /* internal variable */
- char_u *tofree;
+ }
+ } else { // Internal variable.
typval_T tv;
- /* get_name_len() takes care of expanding curly braces */
- name = p;
- len = get_name_len(&p, &tofree, TRUE, FALSE);
+ // get_name_len() takes care of expanding curly braces
+ const char *name = p;
+ char *tofree;
+ len = get_name_len((const char **)&p, &tofree, true, false);
if (len > 0) {
if (tofree != NULL) {
name = tofree;
}
n = (get_var_tv(name, len, &tv, NULL, false, true) == OK);
if (n) {
- /* handle d.key, l[idx], f(expr) */
- n = (handle_subscript(&p, &tv, TRUE, FALSE) == OK);
- if (n)
- clear_tv(&tv);
+ // Handle d.key, l[idx], f(expr).
+ n = (handle_subscript(&p, &tv, true, false) == OK);
+ if (n) {
+ tv_clear(&tv);
+ }
}
}
if (*p != NUL)
@@ -8979,32 +8115,31 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *s;
size_t len;
char_u *errormsg;
int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND;
expand_T xpc;
- int error = FALSE;
- char_u *result;
+ bool error = false;
+ char_u *result;
rettv->v_type = VAR_STRING;
if (argvars[1].v_type != VAR_UNKNOWN
&& argvars[2].v_type != VAR_UNKNOWN
- && get_tv_number_chk(&argvars[2], &error)
+ && tv_get_number_chk(&argvars[2], &error)
&& !error) {
rettv->v_type = VAR_LIST;
rettv->vval.v_list = NULL;
}
- s = get_tv_string(&argvars[0]);
+ const char *s = tv_get_string(&argvars[0]);
if (*s == '%' || *s == '#' || *s == '<') {
- ++emsg_off;
- result = eval_vars(s, s, &len, NULL, &errormsg, NULL);
- --emsg_off;
+ emsg_off++;
+ result = eval_vars((char_u *)s, (char_u *)s, &len, NULL, &errormsg, NULL);
+ emsg_off--;
if (rettv->v_type == VAR_LIST) {
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
if (result != NULL) {
- list_append_string(rettv->vval.v_list, result, -1);
+ tv_list_append_string(rettv->vval.v_list, (const char *)result, -1);
}
} else
rettv->vval.v_string = result;
@@ -9012,94 +8147,44 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/* When the optional second argument is non-zero, don't remove matches
* for 'wildignore' and don't put matches for 'suffixes' at the end. */
if (argvars[1].v_type != VAR_UNKNOWN
- && get_tv_number_chk(&argvars[1], &error))
+ && tv_get_number_chk(&argvars[1], &error)) {
options |= WILD_KEEP_ALL;
+ }
if (!error) {
ExpandInit(&xpc);
xpc.xp_context = EXPAND_FILES;
- if (p_wic)
+ if (p_wic) {
options += WILD_ICASE;
- if (rettv->v_type == VAR_STRING)
- rettv->vval.v_string = ExpandOne(&xpc, s, NULL,
- options, WILD_ALL);
- else {
- rettv_list_alloc(rettv);
- ExpandOne(&xpc, s, NULL, options, WILD_ALL_KEEP);
+ }
+ if (rettv->v_type == VAR_STRING) {
+ rettv->vval.v_string = ExpandOne(&xpc, (char_u *)s, NULL, options,
+ WILD_ALL);
+ } else {
+ tv_list_alloc_ret(rettv);
+ ExpandOne(&xpc, (char_u *)s, NULL, options, WILD_ALL_KEEP);
for (int i = 0; i < xpc.xp_numfiles; i++) {
- list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1);
+ tv_list_append_string(rettv->vval.v_list,
+ (const char *)xpc.xp_files[i], -1);
}
ExpandCleanup(&xpc);
}
- } else
+ } else {
rettv->vval.v_string = NULL;
+ }
}
}
-/*
- * Go over all entries in "d2" and add them to "d1".
- * When "action" is "error" then a duplicate key is an error.
- * When "action" is "force" then a duplicate key is overwritten.
- * Otherwise duplicate keys are ignored ("action" is "keep").
- */
-void dict_extend(dict_T *d1, dict_T *d2, char_u *action)
-{
- dictitem_T *di1;
- hashitem_T *hi2;
- int todo;
- bool watched = is_watched(d1);
- char_u *arg_errmsg = (char_u *)N_("extend() argument");
-
- todo = (int)d2->dv_hashtab.ht_used;
- for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) {
- if (!HASHITEM_EMPTY(hi2)) {
- --todo;
- di1 = dict_find(d1, hi2->hi_key, -1);
- if (d1->dv_scope != 0) {
- /* Disallow replacing a builtin function in l: and g:.
- * Check the key to be valid when adding to any
- * scope. */
- if (d1->dv_scope == VAR_DEF_SCOPE
- && HI2DI(hi2)->di_tv.v_type == VAR_FUNC
- && var_check_func_name(hi2->hi_key,
- di1 == NULL))
- break;
- if (!valid_varname(hi2->hi_key))
- break;
- }
- if (di1 == NULL) {
- di1 = dictitem_copy(HI2DI(hi2));
- if (dict_add(d1, di1) == FAIL) {
- dictitem_free(di1);
- }
-
- if (watched) {
- dictwatcher_notify(d1, (char *)di1->di_key, &di1->di_tv, NULL);
- }
- } else if (*action == 'e') {
- EMSG2(_("E737: Key already exists: %s"), hi2->hi_key);
- break;
- } else if (*action == 'f' && HI2DI(hi2) != di1) {
- typval_T oldtv;
-
- if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, true)
- || var_check_ro(di1->di_flags, arg_errmsg, true)) {
- break;
- }
- if (watched) {
- copy_tv(&di1->di_tv, &oldtv);
- }
-
- clear_tv(&di1->di_tv);
- copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv);
-
- if (watched) {
- dictwatcher_notify(d1, (char *)di1->di_key, &di1->di_tv, &oldtv);
- clear_tv(&oldtv);
- }
- }
- }
+/// "menu_get(path [, modes])" function
+static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ tv_list_alloc_ret(rettv);
+ int modes = MENU_ALL_MODES;
+ if (argvars[1].v_type == VAR_STRING) {
+ const char_u *const strmodes = (char_u *)tv_get_string(&argvars[1]);
+ modes = get_menu_cmd_modes(strmodes, false, NULL, NULL);
}
+ menu_get((char_u *)tv_get_string(&argvars[0]), modes, rettv->vval.v_list);
}
/*
@@ -9108,27 +8193,33 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action)
*/
static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *arg_errmsg = (char_u *)N_("extend() argument");
+ const char *const arg_errmsg = N_("extend() argument");
if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) {
- list_T *l1, *l2;
- listitem_T *item;
long before;
- int error = FALSE;
-
- l1 = argvars[0].vval.v_list;
- l2 = argvars[1].vval.v_list;
- if (l1 != NULL && !tv_check_lock(l1->lv_lock, arg_errmsg, true)
- && l2 != NULL) {
+ bool error = false;
+
+ list_T *const l1 = argvars[0].vval.v_list;
+ list_T *const l2 = argvars[1].vval.v_list;
+ if (l1 == NULL) {
+ const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE);
+ (void)locked;
+ assert(locked == true);
+ } else if (l2 == NULL) {
+ // Do nothing
+ tv_copy(&argvars[0], rettv);
+ } else if (!tv_check_lock(l1->lv_lock, arg_errmsg, TV_TRANSLATE)) {
+ listitem_T *item;
if (argvars[2].v_type != VAR_UNKNOWN) {
- before = get_tv_number_chk(&argvars[2], &error);
- if (error)
- return; /* type error; errmsg already given */
+ before = (long)tv_get_number_chk(&argvars[2], &error);
+ if (error) {
+ return; // Type error; errmsg already given.
+ }
- if (before == l1->lv_len)
+ if (before == l1->lv_len) {
item = NULL;
- else {
- item = list_find(l1, before);
+ } else {
+ item = tv_list_find(l1, before);
if (item == NULL) {
EMSGN(_(e_listidx), before);
return;
@@ -9136,43 +8227,50 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
} else
item = NULL;
- list_extend(l1, l2, item);
+ tv_list_extend(l1, l2, item);
- copy_tv(&argvars[0], rettv);
+ tv_copy(&argvars[0], rettv);
}
} else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type ==
VAR_DICT) {
- dict_T *d1, *d2;
- char_u *action;
- int i;
-
- d1 = argvars[0].vval.v_dict;
- d2 = argvars[1].vval.v_dict;
- if (d1 != NULL && !tv_check_lock(d1->dv_lock, arg_errmsg, true)
- && d2 != NULL) {
- /* Check the third argument. */
+ dict_T *const d1 = argvars[0].vval.v_dict;
+ dict_T *const d2 = argvars[1].vval.v_dict;
+ if (d1 == NULL) {
+ const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE);
+ (void)locked;
+ assert(locked == true);
+ } else if (d2 == NULL) {
+ // Do nothing
+ tv_copy(&argvars[0], rettv);
+ } else if (!tv_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) {
+ const char *action = "force";
+ // Check the third argument.
if (argvars[2].v_type != VAR_UNKNOWN) {
- static char *(av[]) = {"keep", "force", "error"};
+ const char *const av[] = { "keep", "force", "error" };
- action = get_tv_string_chk(&argvars[2]);
- if (action == NULL)
- return; /* type error; errmsg already given */
- for (i = 0; i < 3; ++i)
- if (STRCMP(action, av[i]) == 0)
+ action = tv_get_string_chk(&argvars[2]);
+ if (action == NULL) {
+ return; // Type error; error message already given.
+ }
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(av); i++) {
+ if (strcmp(action, av[i]) == 0) {
break;
+ }
+ }
if (i == 3) {
EMSG2(_(e_invarg2), action);
return;
}
- } else
- action = (char_u *)"force";
+ }
- dict_extend(d1, d2, action);
+ tv_dict_extend(d1, d2, action);
- copy_tv(&argvars[0], rettv);
+ tv_copy(&argvars[0], rettv);
}
- } else
+ } else {
EMSG2(_(e_listdictarg), "extend()");
+ }
}
/*
@@ -9180,19 +8278,18 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *keys, *flags = NULL;
- char_u nbuf[NUMBUFLEN];
-
- /* This is not allowed in the sandbox. If the commands would still be
- * executed in the sandbox it would be OK, but it probably happens later,
- * when "sandbox" is no longer set. */
- if (check_secure())
+ // This is not allowed in the sandbox. If the commands would still be
+ // executed in the sandbox it would be OK, but it probably happens later,
+ // when "sandbox" is no longer set.
+ if (check_secure()) {
return;
+ }
- keys = get_tv_string(&argvars[0]);
-
+ const char *const keys = tv_get_string(&argvars[0]);
+ char nbuf[NUMBUFLEN];
+ const char *flags = NULL;
if (argvars[1].v_type != VAR_UNKNOWN) {
- flags = get_tv_string_buf(&argvars[1], nbuf);
+ flags = tv_get_string_buf(&argvars[1], nbuf);
}
nvim_feedkeys(cstr_as_string((char *)keys),
@@ -9202,9 +8299,9 @@ static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "filereadable()" function
static void f_filereadable(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *p = get_tv_string(&argvars[0]);
+ const char *const p = tv_get_string(&argvars[0]);
rettv->vval.v_number =
- (*p && !os_isdir(p) && os_file_is_readable((char*)p));
+ (*p && !os_isdir((const char_u *)p) && os_file_is_readable(p));
}
/*
@@ -9213,60 +8310,60 @@ static void f_filereadable(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_filewritable(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char *filename = (char *)get_tv_string(&argvars[0]);
+ const char *filename = tv_get_string(&argvars[0]);
rettv->vval.v_number = os_file_is_writable(filename);
}
static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
{
- char_u *fname;
- char_u *fresult = NULL;
- char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path;
- char_u *p;
- char_u pathbuf[NUMBUFLEN];
+ char_u *fresult = NULL;
+ char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path;
int count = 1;
- int first = TRUE;
- int error = FALSE;
+ bool first = true;
+ bool error = false;
rettv->vval.v_string = NULL;
rettv->v_type = VAR_STRING;
- fname = get_tv_string(&argvars[0]);
+ const char *fname = tv_get_string(&argvars[0]);
+ char pathbuf[NUMBUFLEN];
if (argvars[1].v_type != VAR_UNKNOWN) {
- p = get_tv_string_buf_chk(&argvars[1], pathbuf);
- if (p == NULL)
- error = TRUE;
- else {
- if (*p != NUL)
- path = p;
+ const char *p = tv_get_string_buf_chk(&argvars[1], pathbuf);
+ if (p == NULL) {
+ error = true;
+ } else {
+ if (*p != NUL) {
+ path = (char_u *)p;
+ }
- if (argvars[2].v_type != VAR_UNKNOWN)
- count = get_tv_number_chk(&argvars[2], &error);
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ count = tv_get_number_chk(&argvars[2], &error);
+ }
}
}
if (count < 0) {
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
}
if (*fname != NUL && !error) {
do {
if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST)
xfree(fresult);
- fresult = find_file_in_path_option(first ? fname : NULL,
- first ? STRLEN(fname) : 0,
+ fresult = find_file_in_path_option(first ? (char_u *)fname : NULL,
+ first ? strlen(fname) : 0,
0, first, path,
find_what, curbuf->b_ffname,
(find_what == FINDFILE_DIR
? (char_u *)""
: curbuf->b_p_sua));
- first = FALSE;
-
- if (fresult != NULL && rettv->v_type == VAR_LIST)
- list_append_string(rettv->vval.v_list, fresult, -1);
+ first = false;
+ if (fresult != NULL && rettv->v_type == VAR_LIST) {
+ tv_list_append_string(rettv->vval.v_list, (const char *)fresult, -1);
+ }
} while ((rettv->v_type == VAR_LIST || --count > 0) && fresult != NULL);
}
@@ -9280,8 +8377,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
*/
static void filter_map(typval_T *argvars, typval_T *rettv, int map)
{
- char_u buf[NUMBUFLEN];
- char_u *expr;
+ typval_T *expr;
listitem_T *li, *nli;
list_T *l = NULL;
dictitem_T *di;
@@ -9292,20 +8388,21 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
typval_T save_key;
int rem = false;
int todo;
- char_u *ermsg = (char_u *)(map ? "map()" : "filter()");
- char_u *arg_errmsg = (char_u *)(map ? N_("map() argument")
- : N_("filter() argument"));
+ char_u *ermsg = (char_u *)(map ? "map()" : "filter()");
+ const char *const arg_errmsg = (map
+ ? N_("map() argument")
+ : N_("filter() argument"));
int save_did_emsg;
int idx = 0;
if (argvars[0].v_type == VAR_LIST) {
if ((l = argvars[0].vval.v_list) == NULL
- || (!map && tv_check_lock(l->lv_lock, arg_errmsg, true))) {
+ || (!map && tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE))) {
return;
}
} else if (argvars[0].v_type == VAR_DICT) {
if ((d = argvars[0].vval.v_dict) == NULL
- || (!map && tv_check_lock(d->dv_lock, arg_errmsg, true))) {
+ || (!map && tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) {
return;
}
} else {
@@ -9313,16 +8410,15 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
return;
}
- expr = get_tv_string_buf_chk(&argvars[1], buf);
- /* On type errors, the preceding call has already displayed an error
- * message. Avoid a misleading error message for an empty string that
- * was not passed as argument. */
- if (expr != NULL) {
+ expr = &argvars[1];
+ // On type errors, the preceding call has already displayed an error
+ // message. Avoid a misleading error message for an empty string that
+ // was not passed as argument.
+ if (expr->v_type != VAR_UNKNOWN) {
prepare_vimvar(VV_VAL, &save_val);
- expr = skipwhite(expr);
- /* We reset "did_emsg" to be able to detect whether an error
- * occurred during evaluation of the expression. */
+ // We reset "did_emsg" to be able to detect whether an error
+ // occurred during evaluation of the expression.
save_did_emsg = did_emsg;
did_emsg = FALSE;
@@ -9337,24 +8433,25 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
if (!HASHITEM_EMPTY(hi)) {
--todo;
- di = HI2DI(hi);
+ di = TV_DICT_HI2DI(hi);
if (map
- && (tv_check_lock(di->di_tv.v_lock, arg_errmsg, true)
- || var_check_ro(di->di_flags, arg_errmsg, true))) {
+ && (tv_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 = vim_strsave(di->di_key);
int r = filter_map_one(&di->di_tv, expr, map, &rem);
- clear_tv(&vimvars[VV_KEY].vv_tv);
- if (r == FAIL || did_emsg)
+ 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, true)
- || var_check_ro(di->di_flags, arg_errmsg, true)) {
+ if (var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE)
+ || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) {
break;
}
- dictitem_remove(d, di);
+ tv_dict_item_remove(d, di);
}
}
}
@@ -9363,7 +8460,8 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
vimvars[VV_KEY].vv_type = VAR_NUMBER;
for (li = l->lv_first; li != NULL; li = nli) {
- if (map && tv_check_lock(li->li_tv.v_lock, arg_errmsg, true)) {
+ if (map
+ && tv_check_lock(li->li_tv.v_lock, arg_errmsg, TV_TRANSLATE)) {
break;
}
nli = li->li_next;
@@ -9371,9 +8469,10 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL
|| did_emsg)
break;
- if (!map && rem)
- listitem_remove(l, li);
- ++idx;
+ if (!map && rem) {
+ tv_list_item_remove(l, li);
+ }
+ idx++;
}
}
@@ -9383,43 +8482,69 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
did_emsg |= save_did_emsg;
}
- copy_tv(&argvars[0], rettv);
+ tv_copy(&argvars[0], rettv);
}
-static int filter_map_one(typval_T *tv, char_u *expr, int map, int *remp)
+static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
{
typval_T rettv;
- char_u *s;
+ typval_T argv[3];
int retval = FAIL;
+ int dummy;
- copy_tv(tv, &vimvars[VV_VAL].vv_tv);
- s = expr;
- if (eval1(&s, &rettv, TRUE) == FAIL)
- goto theend;
- if (*s != NUL) { /* check for trailing chars after expr */
- EMSG2(_(e_invexpr2), s);
- clear_tv(&rettv);
- goto theend;
+ tv_copy(tv, &vimvars[VV_VAL].vv_tv);
+ argv[0] = vimvars[VV_KEY].vv_tv;
+ argv[1] = vimvars[VV_VAL].vv_tv;
+ if (expr->v_type == VAR_FUNC) {
+ const char_u *const s = expr->vval.v_string;
+ if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL,
+ 0L, 0L, &dummy, true, NULL, NULL) == FAIL) {
+ goto theend;
+ }
+ } else if (expr->v_type == VAR_PARTIAL) {
+ partial_T *partial = expr->vval.v_partial;
+
+ const char_u *const s = partial_name(partial);
+ if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL,
+ 0L, 0L, &dummy, true, partial, NULL) == FAIL) {
+ goto theend;
+ }
+ } else {
+ char buf[NUMBUFLEN];
+ const char *s = tv_get_string_buf_chk(expr, buf);
+ if (s == NULL) {
+ goto theend;
+ }
+ s = (const char *)skipwhite((const char_u *)s);
+ if (eval1((char_u **)&s, &rettv, true) == FAIL) {
+ goto theend;
+ }
+
+ if (*s != NUL) { // check for trailing chars after expr
+ emsgf(_(e_invexpr2), s);
+ goto theend;
+ }
}
if (map) {
- /* map(): replace the list item value */
- clear_tv(tv);
+ // map(): replace the list item value.
+ tv_clear(tv);
rettv.v_lock = 0;
*tv = rettv;
} else {
- int error = FALSE;
-
- /* filter(): when expr is zero remove the item */
- *remp = (get_tv_number_chk(&rettv, &error) == 0);
- clear_tv(&rettv);
- /* On type error, nothing has been removed; return FAIL to stop the
- * loop. The error message was given by get_tv_number_chk(). */
- if (error)
+ bool error = false;
+
+ // filter(): when expr is zero remove the item
+ *remp = (tv_get_number_chk(&rettv, &error) == 0);
+ tv_clear(&rettv);
+ // 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) {
goto theend;
+ }
}
retval = OK;
theend:
- clear_tv(&vimvars[VV_VAL].vv_tv);
+ tv_clear(&vimvars[VV_VAL].vv_tv);
return retval;
}
@@ -9454,13 +8579,14 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
float_T f;
- if (get_float_arg(argvars, &f) == OK) {
- if (f < -0x7fffffff)
- rettv->vval.v_number = -0x7fffffff;
- else if (f > 0x7fffffff)
- rettv->vval.v_number = 0x7fffffff;
- else
+ if (tv_get_float_chk(argvars, &f)) {
+ if (f <= -VARNUMBER_MAX + DBL_EPSILON) {
+ rettv->vval.v_number = -VARNUMBER_MAX;
+ } else if (f >= VARNUMBER_MAX - DBL_EPSILON) {
+ rettv->vval.v_number = VARNUMBER_MAX;
+ } else {
rettv->vval.v_number = (varnumber_T)f;
+ }
}
}
@@ -9469,14 +8595,15 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- float_T fx, fy;
+ float_T fx;
+ float_T fy;
rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &fx) == OK
- && get_float_arg(&argvars[1], &fy) == OK)
+ if (tv_get_float_chk(argvars, &fx) && tv_get_float_chk(&argvars[1], &fy)) {
rettv->vval.v_float = fmod(fx, fy);
- else
+ } else {
rettv->vval.v_float = 0.0;
+ }
}
/*
@@ -9484,8 +8611,8 @@ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv->vval.v_string = vim_strsave_fnameescape(
- get_tv_string(&argvars[0]), FALSE);
+ rettv->vval.v_string = (char_u *)vim_strsave_fnameescape(
+ tv_get_string(&argvars[0]), false);
rettv->v_type = VAR_STRING;
}
@@ -9494,27 +8621,26 @@ static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *fname;
- char_u *mods;
- size_t usedlen = 0;
+ char_u *fbuf = NULL;
size_t len;
- char_u *fbuf = NULL;
- char_u buf[NUMBUFLEN];
-
- fname = get_tv_string_chk(&argvars[0]);
- mods = get_tv_string_buf_chk(&argvars[1], buf);
- if (fname == NULL || mods == NULL)
+ char buf[NUMBUFLEN];
+ const char *fname = tv_get_string_chk(&argvars[0]);
+ const char *const mods = tv_get_string_buf_chk(&argvars[1], buf);
+ if (fname == NULL || mods == NULL) {
fname = NULL;
- else {
- len = STRLEN(fname);
- (void)modify_fname(mods, &usedlen, &fname, &fbuf, &len);
+ } else {
+ len = strlen(fname);
+ size_t usedlen = 0;
+ (void)modify_fname((char_u *)mods, &usedlen, (char_u **)&fname, &fbuf,
+ &len);
}
rettv->v_type = VAR_STRING;
- if (fname == NULL)
+ if (fname == NULL) {
rettv->vval.v_string = NULL;
- else
- rettv->vval.v_string = vim_strnsave(fname, len);
+ } else {
+ rettv->vval.v_string = (char_u *)xmemdupz(fname, len);
+ }
xfree(fbuf);
}
@@ -9524,16 +8650,16 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void foldclosed_both(typval_T *argvars, typval_T *rettv, int end)
{
- linenr_T lnum;
- linenr_T first, last;
-
- lnum = get_tv_lnum(argvars);
+ const linenr_T lnum = tv_get_lnum(argvars);
if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
- if (hasFoldingWin(curwin, lnum, &first, &last, FALSE, NULL)) {
- if (end)
+ linenr_T first;
+ linenr_T last;
+ if (hasFoldingWin(curwin, lnum, &first, &last, false, NULL)) {
+ if (end) {
rettv->vval.v_number = (varnumber_T)last;
- else
+ } else {
rettv->vval.v_number = (varnumber_T)first;
+ }
return;
}
}
@@ -9561,11 +8687,10 @@ static void f_foldclosedend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_foldlevel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- linenr_T lnum;
-
- lnum = get_tv_lnum(argvars);
- if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count)
+ const linenr_T lnum = tv_get_lnum(argvars);
+ if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
rettv->vval.v_number = foldLevel(lnum);
+ }
}
/*
@@ -9573,24 +8698,28 @@ static void f_foldlevel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- linenr_T lnum;
+ linenr_T foldstart;
+ linenr_T foldend;
+ char_u *dashes;
+ linenr_T lnum;
char_u *s;
char_u *r;
- int len;
+ int len;
char *txt;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
- if ((linenr_T)vimvars[VV_FOLDSTART].vv_nr > 0
- && (linenr_T)vimvars[VV_FOLDEND].vv_nr
- <= curbuf->b_ml.ml_line_count
- && vimvars[VV_FOLDDASHES].vv_str != NULL) {
+
+ foldstart = (linenr_T)get_vim_var_nr(VV_FOLDSTART);
+ foldend = (linenr_T)get_vim_var_nr(VV_FOLDEND);
+ dashes = get_vim_var_str(VV_FOLDDASHES);
+ if (foldstart > 0 && foldend <= curbuf->b_ml.ml_line_count
+ && dashes != NULL) {
/* Find first non-empty line in the fold. */
- lnum = (linenr_T)vimvars[VV_FOLDSTART].vv_nr;
- while (lnum < (linenr_T)vimvars[VV_FOLDEND].vv_nr) {
- if (!linewhite(lnum))
+ for (lnum = foldstart; lnum < foldend; ++lnum) {
+ if (!linewhite(lnum)) {
break;
- ++lnum;
+ }
}
/* Find interesting text in this line. */
@@ -9598,21 +8727,19 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/* skip C comment-start */
if (s[0] == '/' && (s[1] == '*' || s[1] == '/')) {
s = skipwhite(s + 2);
- if (*skipwhite(s) == NUL
- && lnum + 1 < (linenr_T)vimvars[VV_FOLDEND].vv_nr) {
+ if (*skipwhite(s) == NUL && lnum + 1 < foldend) {
s = skipwhite(ml_get(lnum + 1));
if (*s == '*')
s = skipwhite(s + 1);
}
}
- txt = _("+-%s%3ld lines: ");
+ unsigned long count = (unsigned long)(foldend - foldstart + 1);
+ txt = ngettext("+-%s%3ld line: ", "+-%s%3ld lines: ", count);
r = xmalloc(STRLEN(txt)
- + STRLEN(vimvars[VV_FOLDDASHES].vv_str) // for %s
- + 20 // for %3ld
- + STRLEN(s)); // concatenated
- sprintf((char *)r, txt, vimvars[VV_FOLDDASHES].vv_str,
- (long)((linenr_T)vimvars[VV_FOLDEND].vv_nr
- - (linenr_T)vimvars[VV_FOLDSTART].vv_nr + 1));
+ + STRLEN(dashes) // for %s
+ + 20 // for %3ld
+ + STRLEN(s)); // concatenated
+ sprintf((char *)r, txt, dashes, count);
len = (int)STRLEN(r);
STRCAT(r, s);
/* remove 'foldmarker' and 'commentstring' */
@@ -9626,24 +8753,24 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- linenr_T lnum;
char_u *text;
- char_u buf[51];
+ char_u buf[FOLD_TEXT_LEN];
foldinfo_T foldinfo;
int fold_count;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
- lnum = get_tv_lnum(argvars);
- /* treat illegal types and illegal string values for {lnum} the same */
- if (lnum < 0)
+ linenr_T lnum = tv_get_lnum(argvars);
+ // Treat illegal types and illegal string values for {lnum} the same.
+ if (lnum < 0) {
lnum = 0;
+ }
fold_count = foldedCount(curwin, lnum, &foldinfo);
if (fold_count > 0) {
- text = get_foldtext(curwin, lnum, lnum + fold_count - 1,
- &foldinfo, buf);
- if (text == buf)
+ text = get_foldtext(curwin, lnum, lnum + fold_count - 1, &foldinfo, buf);
+ if (text == buf) {
text = vim_strsave(text);
+ }
rettv->vval.v_string = text;
}
}
@@ -9655,15 +8782,14 @@ static void f_foreground(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
}
-/*
- * "function()" function
- */
-static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void common_function(typval_T *argvars, typval_T *rettv,
+ bool is_funcref, FunPtr fptr)
{
char_u *s;
char_u *name;
bool use_string = false;
partial_T *arg_pt = NULL;
+ char_u *trans_name = NULL;
if (argvars[0].v_type == VAR_FUNC) {
// function(MyFunc, [arg], dict)
@@ -9672,18 +8798,31 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
&& argvars[0].vval.v_partial != NULL) {
// function(dict.MyFunc, [arg])
arg_pt = argvars[0].vval.v_partial;
- s = arg_pt->pt_name;
+ s = partial_name(arg_pt);
} else {
// function('MyFunc', [arg], dict)
- s = get_tv_string(&argvars[0]);
+ s = (char_u *)tv_get_string(&argvars[0]);
use_string = true;
}
- if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s))) {
- EMSG2(_(e_invarg2), s);
- } else if (use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL
- && !function_exists(s)) {
+ if ((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) || is_funcref) {
+ name = s;
+ trans_name = trans_function_name(&name, false,
+ TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD
+ | TFN_NO_DEREF, NULL, NULL);
+ if (*name != NUL) {
+ s = NULL;
+ }
+ }
+ if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s))
+ || (is_funcref && trans_name == NULL)) {
+ emsgf(_(e_invarg2), (use_string
+ ? tv_get_string(&argvars[0])
+ : (const char *)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))) {
EMSG2(_("E700: Unknown function: %s"), s);
} else {
int dict_idx = 0;
@@ -9724,7 +8863,7 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[dict_idx].v_type != VAR_DICT) {
EMSG(_("E922: expected a dict"));
xfree(name);
- return;
+ goto theend;
}
if (argvars[dict_idx].vval.v_dict == NULL) {
dict_idx = 0;
@@ -9735,7 +8874,7 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
EMSG(_("E923: Second argument of function() must be "
"a list or a dict"));
xfree(name);
- return;
+ goto theend;
}
list = argvars[arg_idx].vval.v_list;
if (list == NULL || list->lv_len == 0) {
@@ -9743,61 +8882,64 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
}
- if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL) {
- partial_T *pt = (partial_T *)xcalloc(1, sizeof(partial_T));
+ 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 (pt != NULL) {
- if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0)) {
- listitem_T *li;
- int i = 0;
- int arg_len = 0;
- int lv_len = 0;
-
- if (arg_pt != NULL) {
- arg_len = arg_pt->pt_argc;
- }
- if (list != NULL) {
- lv_len = list->lv_len;
- }
- pt->pt_argc = arg_len + lv_len;
- pt->pt_argv = (typval_T *)xmalloc(sizeof(typval_T) * pt->pt_argc);
- if (pt->pt_argv == NULL) {
- xfree(pt);
- xfree(name);
- return;
- } else {
- for (i = 0; i < arg_len; i++) {
- copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]);
- }
- if (lv_len > 0) {
- for (li = list->lv_first; li != NULL; li = li->li_next) {
- copy_tv(&li->li_tv, &pt->pt_argv[i++]);
- }
- }
+ 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 = (list == NULL ? 0 : list->lv_len);
+
+ pt->pt_argc = arg_len + lv_len;
+ pt->pt_argv = xmalloc(sizeof(pt->pt_argv[0]) * pt->pt_argc);
+ if (pt->pt_argv == NULL) {
+ xfree(pt);
+ xfree(name);
+ goto theend;
+ }
+ int i = 0;
+ for (; i < arg_len; i++) {
+ tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]);
+ }
+ if (lv_len > 0) {
+ for (listitem_T *li = list->lv_first;
+ li != NULL;
+ li = li->li_next) {
+ tv_copy(&li->li_tv, &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;
+ // 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)++;
- } 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;
+ 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(pt->pt_name);
+ func_ref(name);
}
+
rettv->v_type = VAR_PARTIAL;
rettv->vval.v_partial = pt;
} else {
@@ -9807,17 +8949,30 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
func_ref(name);
}
}
+theend:
+ xfree(trans_name);
+}
+
+static void f_funcref(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ common_function(argvars, rettv, true, fptr);
+}
+
+static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ common_function(argvars, rettv, false, fptr);
}
/// "garbagecollect()" function
static void f_garbagecollect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- /* This is postponed until we are back at the toplevel, because we may be
- * using Lists and Dicts internally. E.g.: ":echo [garbagecollect()]". */
- want_garbage_collect = TRUE;
+ // This is postponed until we are back at the toplevel, because we may be
+ // using Lists and Dicts internally. E.g.: ":echo [garbagecollect()]".
+ want_garbage_collect = true;
- if (argvars[0].v_type != VAR_UNKNOWN && get_tv_number(&argvars[0]) == 1)
- garbage_collect_at_exit = TRUE;
+ if (argvars[0].v_type != VAR_UNKNOWN && tv_get_number(&argvars[0]) == 1) {
+ garbage_collect_at_exit = true;
+ }
}
/*
@@ -9833,20 +8988,21 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[0].v_type == VAR_LIST) {
if ((l = argvars[0].vval.v_list) != NULL) {
- int error = FALSE;
+ bool error = false;
- li = list_find(l, get_tv_number_chk(&argvars[1], &error));
- if (!error && li != NULL)
+ li = tv_list_find(l, tv_get_number_chk(&argvars[1], &error));
+ if (!error && li != NULL) {
tv = &li->li_tv;
+ }
}
} else if (argvars[0].v_type == VAR_DICT) {
if ((d = argvars[0].vval.v_dict) != NULL) {
- di = dict_find(d, get_tv_string(&argvars[1]), -1);
- if (di != NULL)
+ di = tv_dict_find(d, tv_get_string(&argvars[1]), -1);
+ if (di != NULL) {
tv = &di->di_tv;
+ }
}
- } else if (argvars[0].v_type == VAR_PARTIAL
- || argvars[0].v_type == VAR_FUNC) {
+ } else if (tv_is_func(argvars[0])) {
partial_T *pt;
partial_T fref_pt;
@@ -9859,26 +9015,27 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (pt != NULL) {
- char_u *what = get_tv_string(&argvars[1]);
+ const char *const what = tv_get_string(&argvars[1]);
- if (STRCMP(what, "func") == 0 || STRCMP(what, "name") == 0) {
+ if (strcmp(what, "func") == 0 || strcmp(what, "name") == 0) {
rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING);
- if (pt->pt_name != NULL) {
- rettv->vval.v_string = vim_strsave(pt->pt_name);
+ const char *const n = (const char *)partial_name(pt);
+ assert(n != NULL);
+ rettv->vval.v_string = (char_u *)xstrdup(n);
+ if (rettv->v_type == VAR_FUNC) {
+ func_ref(rettv->vval.v_string);
}
- } else if (STRCMP(what, "dict") == 0) {
+ } else if (strcmp(what, "dict") == 0) {
rettv->v_type = VAR_DICT;
rettv->vval.v_dict = pt->pt_dict;
if (pt->pt_dict != NULL) {
(pt->pt_dict->dv_refcount)++;
}
- } else if (STRCMP(what, "args") == 0) {
+ } else if (strcmp(what, "args") == 0) {
rettv->v_type = VAR_LIST;
- if (rettv_list_alloc(rettv) != NULL) {
- int i;
-
- for (i = 0; i < pt->pt_argc; i++) {
- list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]);
+ if (tv_list_alloc_ret(rettv) != NULL) {
+ for (int i = 0; i < pt->pt_argc; i++) {
+ tv_list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]);
}
}
} else {
@@ -9891,60 +9048,63 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (tv == NULL) {
- if (argvars[2].v_type != VAR_UNKNOWN)
- copy_tv(&argvars[2], rettv);
- } else
- copy_tv(tv, rettv);
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ tv_copy(&argvars[2], rettv);
+ }
+ } else {
+ tv_copy(tv, rettv);
+ }
}
/// Returns information about signs placed in a buffer as list of dicts.
static void get_buffer_signs(buf_T *buf, list_T *l)
{
for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) {
- dict_T *d = dict_alloc();
+ dict_T *const d = tv_dict_alloc();
- dict_add_nr_str(d, "id", sign->id, NULL);
- dict_add_nr_str(d, "lnum", sign->lnum, NULL);
- dict_add_nr_str(d, "name", 0L, sign_typenr2name(sign->typenr));
+ tv_dict_add_nr(d, S_LEN("id"), sign->id);
+ tv_dict_add_nr(d, S_LEN("lnum"), sign->lnum);
+ tv_dict_add_str(d, S_LEN("name"),
+ (const char *)sign_typenr2name(sign->typenr));
- list_append_dict(l, d);
+ tv_list_append_dict(l, d);
}
}
/// Returns buffer options, variables and other attributes in a dictionary.
static dict_T *get_buffer_info(buf_T *buf)
{
- dict_T *dict = dict_alloc();
-
- dict_add_nr_str(dict, "bufnr", buf->b_fnum, NULL);
- dict_add_nr_str(dict, "name", 0L,
- buf->b_ffname != NULL ? buf->b_ffname : (char_u *)"");
- dict_add_nr_str(dict, "lnum", buflist_findlnum(buf), NULL);
- dict_add_nr_str(dict, "loaded", buf->b_ml.ml_mfp != NULL, NULL);
- dict_add_nr_str(dict, "listed", buf->b_p_bl, NULL);
- dict_add_nr_str(dict, "changed", bufIsChanged(buf), NULL);
- dict_add_nr_str(dict, "changedtick", buf->b_changedtick, NULL);
- dict_add_nr_str(dict, "hidden",
- buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0,
- NULL);
+ dict_T *const dict = tv_dict_alloc();
+
+ tv_dict_add_nr(dict, S_LEN("bufnr"), buf->b_fnum);
+ tv_dict_add_str(dict, S_LEN("name"),
+ buf->b_ffname != NULL ? (const char *)buf->b_ffname : "");
+ tv_dict_add_nr(dict, S_LEN("lnum"),
+ buf == curbuf ? curwin->w_cursor.lnum : buflist_findlnum(buf));
+ tv_dict_add_nr(dict, S_LEN("loaded"), buf->b_ml.ml_mfp != NULL);
+ tv_dict_add_nr(dict, S_LEN("listed"), buf->b_p_bl);
+ tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf));
+ tv_dict_add_nr(dict, S_LEN("changedtick"), buf->b_changedtick);
+ tv_dict_add_nr(dict, S_LEN("hidden"),
+ buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0);
// Get a reference to buffer variables
- dict_add_dict(dict, "variables", buf->b_vars);
+ tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars);
// List of windows displaying this buffer
- list_T *windows = list_alloc();
+ list_T *const windows = tv_list_alloc();
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (wp->w_buffer == buf) {
- list_append_number(windows, (varnumber_T)wp->handle);
+ tv_list_append_number(windows, (varnumber_T)wp->handle);
}
}
- dict_add_list(dict, "windows", windows);
+ tv_dict_add_list(dict, S_LEN("windows"), windows);
if (buf->b_signlist != NULL) {
// List of signs placed in this buffer
- list_T *signs = list_alloc();
+ list_T *const signs = tv_list_alloc();
get_buffer_signs(buf, signs);
- dict_add_list(dict, "signs", signs);
+ tv_dict_add_list(dict, S_LEN("signs"), signs);
}
return dict;
@@ -9958,7 +9118,7 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
bool sel_buflisted = false;
bool sel_bufloaded = false;
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
// List of all the buffers or selected buffers
if (argvars[0].v_type == VAR_DICT) {
@@ -9969,24 +9129,25 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
filtered = true;
- di = dict_find(sel_d, (char_u *)"buflisted", -1);
- if (di != NULL && get_tv_number(&di->di_tv)) {
+ di = tv_dict_find(sel_d, S_LEN("buflisted"));
+ if (di != NULL && tv_get_number(&di->di_tv)) {
sel_buflisted = true;
}
- di = dict_find(sel_d, (char_u *)"bufloaded", -1);
- if (di != NULL && get_tv_number(&di->di_tv)) {
+ di = tv_dict_find(sel_d, S_LEN("bufloaded"));
+ if (di != NULL && tv_get_number(&di->di_tv)) {
sel_bufloaded = true;
}
}
} else if (argvars[0].v_type != VAR_UNKNOWN) {
// Information about one buffer. Argument specifies the buffer
- (void)get_tv_number(&argvars[0]); // issue errmsg if type error
- emsg_off++;
- argbuf = get_buf_tv(&argvars[0], false);
- emsg_off--;
- if (argbuf == NULL) {
- return;
+ if (tv_check_num(&argvars[0])) { // issue errmsg if type error
+ emsg_off++;
+ argbuf = get_buf_tv(&argvars[0], false);
+ emsg_off--;
+ if (argbuf == NULL) {
+ return;
+ }
}
}
@@ -10000,9 +9161,9 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
continue;
}
- dict_T *d = get_buffer_info(buf);
+ dict_T *const d = get_buffer_info(buf);
if (d != NULL) {
- list_append_dict(rettv->vval.v_list, d);
+ tv_list_append_dict(rettv->vval.v_list, d);
}
if (argbuf != NULL) {
return;
@@ -10023,7 +9184,7 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
if (retlist) {
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
}
if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0)
@@ -10044,33 +9205,54 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli
if (end > buf->b_ml.ml_line_count)
end = buf->b_ml.ml_line_count;
while (start <= end) {
- list_append_string(
- rettv->vval.v_list, ml_get_buf(buf, start++, FALSE), -1);
+ tv_list_append_string(rettv->vval.v_list,
+ (const char *)ml_get_buf(buf, start++, false), -1);
}
}
}
+/// Get the line number from VimL 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.
+static 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] == '$'
+ && buf != NULL) {
+ return buf->b_ml.ml_line_count;
+ }
+ return tv_get_number_chk(tv, NULL);
+}
+
/*
* "getbufline()" function
*/
static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- linenr_T lnum;
- linenr_T end;
- buf_T *buf;
+ buf_T *buf = NULL;
- (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */
- ++emsg_off;
- buf = get_buf_tv(&argvars[0], FALSE);
- --emsg_off;
+ if (tv_check_str_or_nr(&argvars[0])) {
+ emsg_off++;
+ buf = get_buf_tv(&argvars[0], false);
+ emsg_off--;
+ }
- lnum = get_tv_lnum_buf(&argvars[1], buf);
- if (argvars[2].v_type == VAR_UNKNOWN)
- end = lnum;
- else
- end = get_tv_lnum_buf(&argvars[2], buf);
+ const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf);
+ const linenr_T end = (argvars[2].v_type == VAR_UNKNOWN
+ ? lnum
+ : tv_get_lnum_buf(&argvars[2], buf));
- get_buffer_lines(buf, lnum, end, TRUE, rettv);
+ get_buffer_lines(buf, lnum, end, true, rettv);
}
/*
@@ -10078,26 +9260,25 @@ static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- buf_T *buf;
- buf_T *save_curbuf;
- char_u *varname;
- dictitem_T *v;
- int done = FALSE;
-
- (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */
- varname = get_tv_string_chk(&argvars[1]);
- ++emsg_off;
- buf = get_buf_tv(&argvars[0], FALSE);
+ bool done = false;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
+ if (!tv_check_str_or_nr(&argvars[0])) {
+ goto f_getbufvar_end;
+ }
+
+ const char *varname = tv_get_string_chk(&argvars[1]);
+ emsg_off++;
+ buf_T *const buf = get_buf_tv(&argvars[0], false);
+
if (buf != NULL && varname != NULL) {
- /* set curbuf to be our buf, temporarily */
- save_curbuf = curbuf;
+ // set curbuf to be our buf, temporarily
+ buf_T *const save_curbuf = curbuf;
curbuf = buf;
- if (*varname == '&') { // buffer-local-option
+ if (*varname == '&') { // buffer-local-option
if (varname[1] == NUL) {
// get all buffer-local options in a dict
dict_T *opts = get_winbuf_options(true);
@@ -10112,30 +9293,27 @@ static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// buffer-local-option
done = true;
}
- } else if (STRCMP(varname, "changedtick") == 0) {
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = curbuf->b_changedtick;
- done = true;
} else {
- /* Look up the variable. */
- /* Let getbufvar({nr}, "") return the "b:" dictionary. */
- v = find_var_in_ht(&curbuf->b_vars->dv_hashtab,
- 'b', varname, FALSE);
+ // Look up the variable.
+ // Let getbufvar({nr}, "") return the "b:" dictionary.
+ dictitem_T *const v = find_var_in_ht(&curbuf->b_vars->dv_hashtab, 'b',
+ varname, strlen(varname), false);
if (v != NULL) {
- copy_tv(&v->di_tv, rettv);
- done = TRUE;
+ tv_copy(&v->di_tv, rettv);
+ done = true;
}
}
- /* restore previous notion of curbuf */
+ // restore previous notion of curbuf
curbuf = save_curbuf;
}
+ emsg_off--;
- if (!done && argvars[2].v_type != VAR_UNKNOWN)
- /* use the default value */
- copy_tv(&argvars[2], rettv);
-
- --emsg_off;
+f_getbufvar_end:
+ if (!done && argvars[2].v_type != VAR_UNKNOWN) {
+ // use the default value
+ tv_copy(&argvars[2], rettv);
+ }
}
/*
@@ -10144,10 +9322,9 @@ static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
varnumber_T n;
- int error = FALSE;
+ bool error = false;
- ++no_mapping;
- ++allow_keys;
+ no_mapping++;
for (;; ) {
// Position the cursor. Needed after a message that ends in a space,
// or if event processing caused a redraw.
@@ -10165,7 +9342,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
n = safe_vgetc();
- } else if (get_tv_number_chk(&argvars[0], &error) == 1) {
+ } else if (tv_get_number_chk(&argvars[0], &error) == 1) {
// getchar(1): only check if char avail
n = vpeekc_any();
} else if (error || vpeekc_any() == NUL) {
@@ -10176,12 +9353,12 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
n = safe_vgetc();
}
- if (n == K_IGNORE)
+ if (n == K_IGNORE) {
continue;
+ }
break;
}
- --no_mapping;
- --allow_keys;
+ no_mapping--;
vimvars[VV_MOUSE_WIN].vv_nr = 0;
vimvars[VV_MOUSE_WINID].vv_nr = 0;
@@ -10248,13 +9425,13 @@ static void f_getcharmod(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_getcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv_dict_alloc(rettv);
+ tv_dict_alloc_ret(rettv);
dict_T *dict = rettv->vval.v_dict;
- dict_add_nr_str(dict, "char", 0L, last_csearch());
- dict_add_nr_str(dict, "forward", last_csearch_forward(), NULL);
- dict_add_nr_str(dict, "until", last_csearch_until(), NULL);
+ tv_dict_add_str(dict, S_LEN("char"), last_csearch());
+ tv_dict_add_nr(dict, S_LEN("forward"), last_csearch_forward());
+ tv_dict_add_nr(dict, S_LEN("until"), last_csearch_until());
}
/*
@@ -10305,7 +9482,7 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
| WILD_NO_BEEP;
if (argvars[2].v_type != VAR_UNKNOWN) {
- filtered = get_tv_number_chk(&argvars[2], NULL);
+ filtered = (bool)tv_get_number_chk(&argvars[2], NULL);
}
if (p_wic) {
@@ -10317,41 +9494,51 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
options |= WILD_KEEP_ALL;
}
+ if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_STRING) {
+ EMSG(_(e_invarg));
+ return;
+ }
+
+ if (strcmp(tv_get_string(&argvars[1]), "cmdline") == 0) {
+ set_one_cmd_context(&xpc, tv_get_string(&argvars[0]));
+ xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
+ goto theend;
+ }
+
ExpandInit(&xpc);
- xpc.xp_pattern = get_tv_string(&argvars[0]);
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
- xpc.xp_context = cmdcomplete_str_to_type(get_tv_string(&argvars[1]));
+ xpc.xp_pattern = (char_u *)tv_get_string(&argvars[0]);
+ xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
+ xpc.xp_context = cmdcomplete_str_to_type(
+ (char_u *)tv_get_string(&argvars[1]));
if (xpc.xp_context == EXPAND_NOTHING) {
- if (argvars[1].v_type == VAR_STRING) {
- EMSG2(_(e_invarg2), argvars[1].vval.v_string);
- } else {
- EMSG(_(e_invarg));
- }
+ EMSG2(_(e_invarg2), argvars[1].vval.v_string);
return;
}
if (xpc.xp_context == EXPAND_MENUS) {
set_context_in_menu_cmd(&xpc, (char_u *)"menu", xpc.xp_pattern, false);
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
+ xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
}
if (xpc.xp_context == EXPAND_CSCOPE) {
- set_context_in_cscope_cmd(&xpc, xpc.xp_pattern, CMD_cscope);
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
+ set_context_in_cscope_cmd(&xpc, (const char *)xpc.xp_pattern, CMD_cscope);
+ xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
}
if (xpc.xp_context == EXPAND_SIGN) {
set_context_in_sign_cmd(&xpc, xpc.xp_pattern);
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
+ xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
}
+theend:
pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
if (pat != NULL) {
ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP);
for (int i = 0; i < xpc.xp_numfiles; i++) {
- list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1);
+ tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i],
+ -1);
}
}
xfree(pat);
@@ -10452,13 +9639,15 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (from) {
break;
}
- case kCdScopeTab: // FALLTHROUGH
+ // fallthrough
+ case kCdScopeTab:
assert(tp);
- from = tp->localdir;
+ from = tp->tp_localdir;
if (from) {
break;
}
- case kCdScopeGlobal: // FALLTHROUGH
+ // fallthrough
+ case kCdScopeGlobal:
if (globaldir) { // `globaldir` is not always set.
from = globaldir;
} else if (os_dirname(cwd, MAXPATHL) == FAIL) { // Get the OS CWD.
@@ -10495,20 +9684,21 @@ static void f_getfontname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *perm = NULL;
+ char *perm = NULL;
char_u flags[] = "rwx";
- char_u *filename = get_tv_string(&argvars[0]);
+ const char *filename = tv_get_string(&argvars[0]);
int32_t file_perm = os_getperm(filename);
if (file_perm >= 0) {
- perm = vim_strsave((char_u *)"---------");
+ perm = xstrdup("---------");
for (int i = 0; i < 9; i++) {
- if (file_perm & (1 << (8 - i)))
+ if (file_perm & (1 << (8 - i))) {
perm[i] = flags[i % 3];
+ }
}
}
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = perm;
+ rettv->vval.v_string = (char_u *)perm;
}
/*
@@ -10516,16 +9706,16 @@ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char *fname = (char *)get_tv_string(&argvars[0]);
+ const char *fname = tv_get_string(&argvars[0]);
rettv->v_type = VAR_NUMBER;
FileInfo file_info;
if (os_fileinfo(fname, &file_info)) {
uint64_t filesize = os_fileinfo_size(&file_info);
- if (os_isdir((char_u *)fname))
+ if (os_isdir((const char_u *)fname)) {
rettv->vval.v_number = 0;
- else {
+ } else {
rettv->vval.v_number = (varnumber_T)filesize;
/* non-perfect check for overflow */
@@ -10543,7 +9733,7 @@ static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char *fname = (char *)get_tv_string(&argvars[0]);
+ const char *fname = tv_get_string(&argvars[0]);
FileInfo file_info;
if (os_fileinfo(fname, &file_info)) {
@@ -10558,15 +9748,14 @@ static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *fname;
char_u *type = NULL;
char *t;
- fname = get_tv_string(&argvars[0]);
+ const char *fname = tv_get_string(&argvars[0]);
rettv->v_type = VAR_STRING;
FileInfo file_info;
- if (os_fileinfo_link((char *)fname, &file_info)) {
+ if (os_fileinfo_link(fname, &file_info)) {
uint64_t mode = file_info.stat.st_mode;
#ifdef S_ISREG
if (S_ISREG(mode))
@@ -10618,10 +9807,11 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
default: t = "other";
}
# else
- if (os_isdir(fname))
+ if (os_isdir((const char_u *)fname)) {
t = "dir";
- else
+ } else {
t = "file";
+ }
# endif
#endif
type = vim_strsave((char_u *)t);
@@ -10634,22 +9824,52 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- linenr_T lnum;
linenr_T end;
- int retlist;
+ bool retlist;
- lnum = get_tv_lnum(argvars);
+ const linenr_T lnum = tv_get_lnum(argvars);
if (argvars[1].v_type == VAR_UNKNOWN) {
end = 0;
- retlist = FALSE;
+ retlist = false;
} else {
- end = get_tv_lnum(&argvars[1]);
- retlist = TRUE;
+ end = tv_get_lnum(&argvars[1]);
+ retlist = true;
}
get_buffer_lines(curbuf, lnum, end, retlist, rettv);
}
+static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg,
+ typval_T *rettv)
+{
+ if (what_arg->v_type == VAR_UNKNOWN) {
+ tv_list_alloc_ret(rettv);
+ if (is_qf || wp != NULL) {
+ (void)get_errorlist(wp, -1, rettv->vval.v_list);
+ }
+ } else {
+ tv_dict_alloc_ret(rettv);
+ if (is_qf || wp != NULL) {
+ if (what_arg->v_type == VAR_DICT) {
+ dict_T *d = what_arg->vval.v_dict;
+
+ if (d != NULL) {
+ get_errorlist_properties(wp, d, rettv->vval.v_dict);
+ }
+ } else {
+ EMSG(_(e_dictreq));
+ }
+ }
+ }
+}
+
+/// "getloclist()" function
+static void f_getloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ win_T *wp = find_win_by_nr(&argvars[0], NULL);
+ get_qf_loc_list(false, wp, &argvars[1], rettv);
+}
+
/*
* "getmatches()" function
*/
@@ -10658,43 +9878,45 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
matchitem_T *cur = curwin->w_match_head;
int i;
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
while (cur != NULL) {
- dict_T *dict = dict_alloc();
+ dict_T *dict = tv_dict_alloc();
if (cur->match.regprog == NULL) {
// match added with matchaddpos()
for (i = 0; i < MAXPOSMATCH; ++i) {
llpos_T *llpos;
- char buf[6];
+ char buf[6];
llpos = &cur->pos.pos[i];
if (llpos->lnum == 0) {
break;
}
- list_T *l = list_alloc();
- list_append_number(l, (varnumber_T)llpos->lnum);
+ list_T *l = tv_list_alloc();
+ tv_list_append_number(l, (varnumber_T)llpos->lnum);
if (llpos->col > 0) {
- list_append_number(l, (varnumber_T)llpos->col);
- list_append_number(l, (varnumber_T)llpos->len);
+ tv_list_append_number(l, (varnumber_T)llpos->col);
+ tv_list_append_number(l, (varnumber_T)llpos->len);
}
- sprintf(buf, "pos%d", i + 1);
- dict_add_list(dict, buf, l);
+ int len = snprintf(buf, sizeof(buf), "pos%d", i + 1);
+ assert((size_t)len < sizeof(buf));
+ tv_dict_add_list(dict, buf, (size_t)len, l);
}
} else {
- dict_add_nr_str(dict, "pattern", 0L, cur->pattern);
+ tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cur->pattern);
}
- dict_add_nr_str(dict, "group", 0L, syn_id2name(cur->hlg_id));
- dict_add_nr_str(dict, "priority", (long)cur->priority, NULL);
- dict_add_nr_str(dict, "id", (long)cur->id, NULL);
+ tv_dict_add_str(dict, S_LEN("group"),
+ (const char *)syn_id2name(cur->hlg_id));
+ tv_dict_add_nr(dict, S_LEN("priority"), (varnumber_T)cur->priority);
+ tv_dict_add_nr(dict, S_LEN("id"), (varnumber_T)cur->id);
if (cur->conceal_char) {
- char_u buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXBYTES + 1];
- buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL;
- dict_add_nr_str(dict, "conceal", 0L, (char_u *)&buf);
+ buf[(*mb_char2bytes)((int)cur->conceal_char, (char_u *)buf)] = NUL;
+ tv_dict_add_str(dict, S_LEN("conceal"), buf);
}
- list_append_dict(rettv->vval.v_list, dict);
+ tv_list_append_dict(rettv->vval.v_list, dict);
cur = cur->next;
}
}
@@ -10718,20 +9940,22 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos)
fp = var2fpos(&argvars[0], true, &fnum);
}
- list_T *l = rettv_list_alloc(rettv);
- list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0);
- list_append_number(l, (fp != NULL) ? (varnumber_T)fp->lnum : (varnumber_T)0);
- list_append_number(l,
- (fp != NULL)
- ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1)
- : (varnumber_T)0);
- list_append_number(l,
- (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0);
+ list_T *l = tv_list_alloc_ret(rettv);
+ tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0);
+ tv_list_append_number(l, ((fp != NULL)
+ ? (varnumber_T)fp->lnum
+ : (varnumber_T)0));
+ tv_list_append_number(
+ l, ((fp != NULL)
+ ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1)
+ : (varnumber_T)0));
+ tv_list_append_number(
+ l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0);
if (getcurpos) {
update_curswant();
- list_append_number(l, curwin->w_curswant == MAXCOL
+ tv_list_append_number(l, (curwin->w_curswant == MAXCOL
? (varnumber_T)MAXCOL
- : (varnumber_T)curwin->w_curswant + 1);
+ : (varnumber_T)curwin->w_curswant + 1));
}
}
@@ -10751,58 +9975,48 @@ static void f_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
getpos_both(argvars, rettv, false);
}
-/*
- * "getqflist()" and "getloclist()" functions
- */
+/// "getqflist()" functions
static void f_getqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv_list_alloc(rettv);
- win_T *wp = NULL;
- if (argvars[0].v_type != VAR_UNKNOWN) { /* getloclist() */
- wp = find_win_by_nr(&argvars[0], NULL);
- if (wp == NULL) {
- return;
- }
- }
- (void)get_errorlist(wp, rettv->vval.v_list);
+ get_qf_loc_list(true, NULL, &argvars[0], rettv);
}
/// "getreg()" function
static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *strregname;
- int regname;
+ const char *strregname;
int arg2 = false;
bool return_list = false;
- int error = false;
+ bool error = false;
if (argvars[0].v_type != VAR_UNKNOWN) {
- strregname = get_tv_string_chk(&argvars[0]);
+ strregname = tv_get_string_chk(&argvars[0]);
error = strregname == NULL;
if (argvars[1].v_type != VAR_UNKNOWN) {
- arg2 = get_tv_number_chk(&argvars[1], &error);
+ arg2 = tv_get_number_chk(&argvars[1], &error);
if (!error && argvars[2].v_type != VAR_UNKNOWN) {
- return_list = get_tv_number_chk(&argvars[2], &error);
+ return_list = tv_get_number_chk(&argvars[2], &error);
}
}
} else {
- strregname = vimvars[VV_REG].vv_str;
+ strregname = (const char *)vimvars[VV_REG].vv_str;
}
if (error) {
return;
}
- regname = (strregname == NULL ? '"' : *strregname);
- if (regname == 0)
+ int regname = (uint8_t)(strregname == NULL ? '"' : *strregname);
+ if (regname == 0) {
regname = '"';
+ }
if (return_list) {
rettv->v_type = VAR_LIST;
rettv->vval.v_list =
get_reg_contents(regname, (arg2 ? kGRegExprSrc : 0) | kGRegList);
if (rettv->vval.v_list == NULL) {
- rettv->vval.v_list = list_alloc();
+ rettv->vval.v_list = tv_list_alloc();
}
rettv->vval.v_list->lv_refcount++;
} else {
@@ -10816,23 +10030,24 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *strregname;
- int regname;
+ const char *strregname;
if (argvars[0].v_type != VAR_UNKNOWN) {
- strregname = get_tv_string_chk(&argvars[0]);
- if (strregname == NULL) { /* type error; errmsg already given */
+ strregname = tv_get_string_chk(&argvars[0]);
+ if (strregname == NULL) { // Type error; errmsg already given.
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
return;
}
- } else
- /* Default to v:register */
- strregname = vimvars[VV_REG].vv_str;
+ } else {
+ // Default to v:register.
+ strregname = (const char *)vimvars[VV_REG].vv_str;
+ }
- regname = (strregname == NULL ? '"' : *strregname);
- if (regname == 0)
+ int regname = (uint8_t)(strregname == NULL ? '"' : *strregname);
+ if (regname == 0) {
regname = '"';
+ }
colnr_T reglen = 0;
char buf[NUMBUFLEN + 2];
@@ -10847,18 +10062,18 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// as a dictionary.
static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx)
{
- dict_T *dict = dict_alloc();
+ dict_T *const dict = tv_dict_alloc();
- dict_add_nr_str(dict, "tabnr", tp_idx, NULL);
+ tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx);
- list_T *l = list_alloc();
+ list_T *const l = tv_list_alloc();
FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
- list_append_number(l, (varnumber_T)wp->handle);
+ tv_list_append_number(l, (varnumber_T)wp->handle);
}
- dict_add_list(dict, "windows", l);
+ tv_dict_add_list(dict, S_LEN("windows"), l);
// Make a reference to tabpage variables
- dict_add_dict(dict, "variables", tp->tp_vars);
+ tv_dict_add_dict(dict, S_LEN("variables"), tp->tp_vars);
return dict;
}
@@ -10868,11 +10083,11 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tabpage_T *tparg = NULL;
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
if (argvars[0].v_type != VAR_UNKNOWN) {
// Information about one tab page
- tparg = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL));
+ tparg = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));
if (tparg == NULL) {
return;
}
@@ -10885,9 +10100,9 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (tparg != NULL && tp != tparg) {
continue;
}
- dict_T *d = get_tabpage_info(tp, tpnr);
+ dict_T *const d = get_tabpage_info(tp, tpnr);
if (d != NULL) {
- list_append_dict(rettv->vval.v_list, d);
+ tv_list_append_dict(rettv->vval.v_list, d);
}
if (tparg != NULL) {
return;
@@ -10903,14 +10118,13 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
win_T *oldcurwin;
tabpage_T *tp, *oldtabpage;
dictitem_T *v;
- char_u *varname;
bool done = false;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
- varname = get_tv_string_chk(&argvars[1]);
- tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL));
+ const char *const varname = tv_get_string_chk(&argvars[1]);
+ tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));
if (tp != NULL && varname != NULL) {
// Set tp to be our tabpage, temporarily. Also set the window to the
// first window in the tabpage, otherwise the window is not valid.
@@ -10918,9 +10132,10 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (switch_win(&oldcurwin, &oldtabpage, window, tp, true) == OK) {
// look up the variable
// Let gettabvar({nr}, "") return the "t:" dictionary.
- v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', varname, FALSE);
+ v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't',
+ varname, strlen(varname), false);
if (v != NULL) {
- copy_tv(&v->di_tv, rettv);
+ tv_copy(&v->di_tv, rettv);
done = true;
}
}
@@ -10930,7 +10145,7 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (!done && argvars[2].v_type != VAR_UNKNOWN) {
- copy_tv(&argvars[2], rettv);
+ tv_copy(&argvars[2], rettv);
}
}
@@ -10945,22 +10160,21 @@ static void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// Returns information about a window as a dictionary.
static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr)
{
- dict_T *dict = dict_alloc();
+ dict_T *const dict = tv_dict_alloc();
- dict_add_nr_str(dict, "tabnr", tpnr, NULL);
- dict_add_nr_str(dict, "winnr", winnr, NULL);
- dict_add_nr_str(dict, "winid", wp->handle, NULL);
- dict_add_nr_str(dict, "height", wp->w_height, NULL);
- dict_add_nr_str(dict, "width", wp->w_width, NULL);
- dict_add_nr_str(dict, "bufnr", wp->w_buffer->b_fnum, NULL);
+ tv_dict_add_nr(dict, S_LEN("tabnr"), tpnr);
+ tv_dict_add_nr(dict, S_LEN("winnr"), winnr);
+ tv_dict_add_nr(dict, S_LEN("winid"), wp->handle);
+ tv_dict_add_nr(dict, S_LEN("height"), wp->w_height);
+ tv_dict_add_nr(dict, S_LEN("width"), wp->w_width);
+ tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum);
- dict_add_nr_str(dict, "quickfix", bt_quickfix(wp->w_buffer), NULL);
- dict_add_nr_str(dict, "loclist",
- (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL),
- NULL);
+ tv_dict_add_nr(dict, S_LEN("quickfix"), bt_quickfix(wp->w_buffer));
+ tv_dict_add_nr(dict, S_LEN("loclist"),
+ (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL));
// Add a reference to window variables
- dict_add_dict(dict, "variables", wp->w_vars);
+ tv_dict_add_dict(dict, S_LEN("variables"), wp->w_vars);
return dict;
}
@@ -10970,7 +10184,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
win_T *wparg = NULL;
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
if (argvars[0].v_type != VAR_UNKNOWN) {
wparg = win_id2wp(argvars);
@@ -10990,9 +10204,9 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
continue;
}
winnr++;
- dict_T *d = get_win_info(wp, tabnr, winnr);
+ dict_T *const d = get_win_info(wp, tabnr, winnr);
if (d != NULL) {
- list_append_dict(rettv->vval.v_list, d);
+ tv_list_append_dict(rettv->vval.v_list, d);
}
if (wparg != NULL) {
// found information about a specific window
@@ -11027,7 +10241,7 @@ find_win_by_nr (
tabpage_T *tp /* NULL for current tab page */
)
{
- int nr = get_tv_number_chk(vp, NULL);
+ int nr = (int)tv_get_number_chk(vp, NULL);
if (nr < 0) {
return NULL;
@@ -11062,7 +10276,7 @@ static win_T *find_tabwin(typval_T *wvp, typval_T *tvp)
if (wvp->v_type != VAR_UNKNOWN) {
if (tvp->v_type != VAR_UNKNOWN) {
- long n = get_tv_number(tvp);
+ long n = tv_get_number(tvp);
if (n >= 0) {
tp = find_tabpage(n);
}
@@ -11097,23 +10311,23 @@ getwinvar (
)
{
win_T *win, *oldcurwin;
- char_u *varname;
dictitem_T *v;
tabpage_T *tp = NULL;
tabpage_T *oldtabpage = NULL;
bool done = false;
- if (off == 1)
- tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL));
- else
+ if (off == 1) {
+ tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));
+ } else {
tp = curtab;
+ }
win = find_win_by_nr(&argvars[off], tp);
- varname = get_tv_string_chk(&argvars[off + 1]);
- ++emsg_off;
+ const char *varname = tv_get_string_chk(&argvars[off + 1]);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
+ emsg_off++;
if (win != NULL && varname != NULL) {
// Set curwin to be our win, temporarily. Also set the tabpage,
// otherwise the window is not valid. Only do this when needed,
@@ -11139,9 +10353,10 @@ getwinvar (
} else {
// Look up the variable.
// Let getwinvar({nr}, "") return the "w:" dictionary.
- v = find_var_in_ht(&win->w_vars->dv_hashtab, 'w', varname, FALSE);
+ v = find_var_in_ht(&win->w_vars->dv_hashtab, 'w', varname,
+ strlen(varname), false);
if (v != NULL) {
- copy_tv(&v->di_tv, rettv);
+ tv_copy(&v->di_tv, rettv);
done = true;
}
}
@@ -11152,12 +10367,12 @@ getwinvar (
restore_win(oldcurwin, oldtabpage, true);
}
}
+ emsg_off--;
- if (!done && argvars[off + 2].v_type != VAR_UNKNOWN)
- /* use the default return value */
- copy_tv(&argvars[off + 2], rettv);
-
- --emsg_off;
+ if (!done && argvars[off + 2].v_type != VAR_UNKNOWN) {
+ // use the default return value
+ tv_copy(&argvars[off + 2], rettv);
+ }
}
/*
@@ -11167,21 +10382,22 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int options = WILD_SILENT|WILD_USE_NL;
expand_T xpc;
- int error = FALSE;
+ bool error = false;
/* When the optional second argument is non-zero, don't remove matches
* for 'wildignore' and don't put matches for 'suffixes' at the end. */
rettv->v_type = VAR_STRING;
if (argvars[1].v_type != VAR_UNKNOWN) {
- if (get_tv_number_chk(&argvars[1], &error))
+ if (tv_get_number_chk(&argvars[1], &error)) {
options |= WILD_KEEP_ALL;
+ }
if (argvars[2].v_type != VAR_UNKNOWN) {
- if (get_tv_number_chk(&argvars[2], &error)) {
+ if (tv_get_number_chk(&argvars[2], &error)) {
rettv->v_type = VAR_LIST;
rettv->vval.v_list = NULL;
}
if (argvars[3].v_type != VAR_UNKNOWN
- && get_tv_number_chk(&argvars[3], &error)) {
+ && tv_get_number_chk(&argvars[3], &error)) {
options |= WILD_ALLLINKS;
}
}
@@ -11191,14 +10407,16 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr)
xpc.xp_context = EXPAND_FILES;
if (p_wic)
options += WILD_ICASE;
- if (rettv->v_type == VAR_STRING)
- rettv->vval.v_string = ExpandOne(&xpc, get_tv_string(&argvars[0]),
- NULL, options, WILD_ALL);
- else {
- rettv_list_alloc(rettv);
- ExpandOne(&xpc, get_tv_string(&argvars[0]), NULL, options, WILD_ALL_KEEP);
+ if (rettv->v_type == VAR_STRING) {
+ rettv->vval.v_string = ExpandOne(
+ &xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, WILD_ALL);
+ } else {
+ tv_list_alloc_ret(rettv);
+ ExpandOne(&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options,
+ WILD_ALL_KEEP);
for (int i = 0; i < xpc.xp_numfiles; i++) {
- list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1);
+ tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i],
+ -1);
}
ExpandCleanup(&xpc);
}
@@ -11210,7 +10428,7 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int flags = 0; // Flags for globpath.
- int error = false;
+ bool error = false;
// Return a string, or a list if the optional third argument is non-zero.
rettv->v_type = VAR_STRING;
@@ -11218,36 +10436,36 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[2].v_type != VAR_UNKNOWN) {
// When the optional second argument is non-zero, don't remove matches
// for 'wildignore' and don't put matches for 'suffixes' at the end.
- if (get_tv_number_chk(&argvars[2], &error)) {
+ if (tv_get_number_chk(&argvars[2], &error)) {
flags |= WILD_KEEP_ALL;
}
if (argvars[3].v_type != VAR_UNKNOWN) {
- if (get_tv_number_chk(&argvars[3], &error)) {
+ if (tv_get_number_chk(&argvars[3], &error)) {
rettv->v_type = VAR_LIST;
rettv->vval.v_list = NULL;
}
if (argvars[4].v_type != VAR_UNKNOWN
- && get_tv_number_chk(&argvars[4], &error)) {
+ && tv_get_number_chk(&argvars[4], &error)) {
flags |= WILD_ALLLINKS;
}
}
}
- char_u buf1[NUMBUFLEN];
- char_u *file = get_tv_string_buf_chk(&argvars[1], buf1);
+ char buf1[NUMBUFLEN];
+ const char *const file = tv_get_string_buf_chk(&argvars[1], buf1);
if (file != NULL && !error) {
garray_T ga;
ga_init(&ga, (int)sizeof(char_u *), 10);
- globpath(get_tv_string(&argvars[0]), file, &ga, flags);
+ globpath((char_u *)tv_get_string(&argvars[0]), (char_u *)file, &ga, flags);
if (rettv->v_type == VAR_STRING) {
rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n");
} else {
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
for (int i = 0; i < ga.ga_len; i++) {
- list_append_string(rettv->vval.v_list,
- ((char_u **)(ga.ga_data))[i], -1);
+ tv_list_append_string(rettv->vval.v_list,
+ ((const char **)(ga.ga_data))[i], -1);
}
}
@@ -11260,18 +10478,19 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// "glob2regpat()" function
static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *pat = get_tv_string_chk(&argvars[0]); // NULL on type error
+ const char *const pat = tv_get_string_chk(&argvars[0]); // NULL on type error
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (pat == NULL)
- ? NULL
- : file_pat_to_reg_pat(pat, NULL, NULL, false);
+ rettv->vval.v_string = ((pat == NULL)
+ ? NULL
+ : file_pat_to_reg_pat((char_u *)pat, NULL, NULL,
+ false));
}
/// "has()" function
static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- static char *(has_list[]) = {
+ static const char *const has_list[] = {
#ifdef UNIX
"unix",
#endif
@@ -11322,6 +10541,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"insert_expand",
"jumplist",
"keymap",
+ "lambda",
"langmap",
"libcall",
"linebreak",
@@ -11338,6 +10558,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"mouse",
"multi_byte",
"multi_lang",
+ "num64",
"packages",
"path_extra",
"persistent_undo",
@@ -11381,13 +10602,11 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"winaltkeys",
"writebackup",
"nvim",
- NULL
};
bool n = false;
- char *name = (char *)get_tv_string(&argvars[0]);
-
- for (int i = 0; has_list[i] != NULL; i++) {
+ const char *const name = tv_get_string(&argvars[0]);
+ for (size_t i = 0; i < ARRAY_SIZE(has_list); i++) {
if (STRICMP(name, has_list[i]) == 0) {
n = true;
break;
@@ -11426,6 +10645,10 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
#endif
} else if (STRICMP(name, "syntax_items") == 0) {
n = syntax_present(curwin);
+#ifdef UNIX
+ } else if (STRICMP(name, "unnamedplus") == 0) {
+ n = eval_has_provider("clipboard");
+#endif
}
}
@@ -11448,8 +10671,9 @@ static void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[0].vval.v_dict == NULL)
return;
- rettv->vval.v_number = dict_find(argvars[0].vval.v_dict,
- get_tv_string(&argvars[1]), -1) != NULL;
+ rettv->vval.v_number = tv_dict_find(argvars[0].vval.v_dict,
+ tv_get_string(&argvars[1]),
+ -1) != NULL;
}
/// `haslocaldir([{win}[, {tab}]])` function
@@ -11541,7 +10765,7 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
break;
case kCdScopeTab:
assert(tp);
- rettv->vval.v_number = tp->localdir ? 1 : 0;
+ rettv->vval.v_number = tp->tp_localdir ? 1 : 0;
break;
case kCdScopeGlobal:
// The global scope never has a local directory
@@ -11558,24 +10782,24 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *name;
- char_u *mode;
- char_u buf[NUMBUFLEN];
- int abbr = FALSE;
-
- name = get_tv_string(&argvars[0]);
- if (argvars[1].v_type == VAR_UNKNOWN)
- mode = (char_u *)"nvo";
- else {
- mode = get_tv_string_buf(&argvars[1], buf);
- if (argvars[2].v_type != VAR_UNKNOWN)
- abbr = get_tv_number(&argvars[2]);
+ const char *mode;
+ const char *const name = tv_get_string(&argvars[0]);
+ bool abbr = false;
+ char buf[NUMBUFLEN];
+ if (argvars[1].v_type == VAR_UNKNOWN) {
+ mode = "nvo";
+ } else {
+ mode = tv_get_string_buf(&argvars[1], buf);
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ abbr = tv_get_number(&argvars[2]);
+ }
}
- if (map_to_exists(name, mode, abbr))
- rettv->vval.v_number = TRUE;
- else
- rettv->vval.v_number = FALSE;
+ if (map_to_exists(name, mode, abbr)) {
+ rettv->vval.v_number = true;
+ } else {
+ rettv->vval.v_number = false;
+ }
}
/*
@@ -11584,20 +10808,19 @@ static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
HistoryType histype;
- char_u *str;
- char_u buf[NUMBUFLEN];
rettv->vval.v_number = false;
if (check_restricted() || check_secure()) {
return;
}
- str = get_tv_string_chk(&argvars[0]); // NULL on type error
- histype = str != NULL ? get_histtype(str, STRLEN(str), false) : HIST_INVALID;
+ const char *str = tv_get_string_chk(&argvars[0]); // NULL on type error
+ histype = str != NULL ? get_histtype(str, strlen(str), false) : HIST_INVALID;
if (histype != HIST_INVALID) {
- str = get_tv_string_buf(&argvars[1], buf);
+ char buf[NUMBUFLEN];
+ str = tv_get_string_buf(&argvars[1], buf);
if (*str != NUL) {
init_history();
- add_to_history(histype, str, false, NUL);
+ add_to_history(histype, (char_u *)str, false, NUL);
rettv->vval.v_number = true;
return;
}
@@ -11610,23 +10833,21 @@ static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int n;
- char_u buf[NUMBUFLEN];
- char_u *str;
-
- str = get_tv_string_chk(&argvars[0]); // NULL on type error
+ const char *const str = tv_get_string_chk(&argvars[0]); // NULL on type error
if (str == NULL) {
n = 0;
} else if (argvars[1].v_type == VAR_UNKNOWN) {
// only one argument: clear entire history
- n = clr_history(get_histtype(str, STRLEN(str), false));
+ n = clr_history(get_histtype(str, strlen(str), false));
} else if (argvars[1].v_type == VAR_NUMBER) {
// index given: remove that entry
- n = del_history_idx(get_histtype(str, STRLEN(str), false),
- (int) get_tv_number(&argvars[1]));
+ n = del_history_idx(get_histtype(str, strlen(str), false),
+ (int)tv_get_number(&argvars[1]));
} else {
// string given: remove all matching entries
- n = del_history_entry(get_histtype(str, STRLEN(str), false),
- get_tv_string_buf(&argvars[1], buf));
+ char buf[NUMBUFLEN];
+ n = del_history_entry(get_histtype(str, strlen(str), false),
+ (char_u *)tv_get_string_buf(&argvars[1], buf));
}
rettv->vval.v_number = n;
}
@@ -11638,17 +10859,16 @@ static void f_histget(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
HistoryType type;
int idx;
- char_u *str;
- str = get_tv_string_chk(&argvars[0]); // NULL on type error
+ const char *const str = tv_get_string_chk(&argvars[0]); // NULL on type error
if (str == NULL) {
rettv->vval.v_string = NULL;
} else {
- type = get_histtype(str, STRLEN(str), false);
+ type = get_histtype(str, strlen(str), false);
if (argvars[1].v_type == VAR_UNKNOWN) {
idx = get_history_idx(type);
} else {
- idx = (int)get_tv_number_chk(&argvars[1], NULL);
+ idx = (int)tv_get_number_chk(&argvars[1], NULL);
}
// -1 on type error
rettv->vval.v_string = vim_strsave(get_history_entry(type, idx));
@@ -11663,9 +10883,9 @@ static void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int i;
- char_u *history = get_tv_string_chk(&argvars[0]);
+ const char *const history = tv_get_string_chk(&argvars[0]);
- i = history == NULL ? HIST_CMD - 1 : get_histtype(history, STRLEN(history),
+ i = history == NULL ? HIST_CMD - 1 : get_histtype(history, strlen(history),
false);
if (i != HIST_INVALID) {
i = get_history_idx(i);
@@ -11680,7 +10900,8 @@ static void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv->vval.v_number = syn_name2id(get_tv_string(&argvars[0]));
+ rettv->vval.v_number = syn_name2id(
+ (const char_u *)tv_get_string(&argvars[0]));
}
/*
@@ -11688,7 +10909,8 @@ static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_hlexists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv->vval.v_number = highlight_exists(get_tv_string(&argvars[0]));
+ rettv->vval.v_number = highlight_exists(
+ (const char_u *)tv_get_string(&argvars[0]));
}
/*
@@ -11708,25 +10930,27 @@ static void f_hostname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u buf1[NUMBUFLEN];
- char_u buf2[NUMBUFLEN];
- char_u *from, *to, *str;
vimconv_T vimconv;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
- str = get_tv_string(&argvars[0]);
- from = enc_canonize(enc_skip(get_tv_string_buf(&argvars[1], buf1)));
- to = enc_canonize(enc_skip(get_tv_string_buf(&argvars[2], buf2)));
+ const char *const str = tv_get_string(&argvars[0]);
+ char buf1[NUMBUFLEN];
+ char_u *const from = enc_canonize(enc_skip(
+ (char_u *)tv_get_string_buf(&argvars[1], buf1)));
+ char buf2[NUMBUFLEN];
+ char_u *const to = enc_canonize(enc_skip(
+ (char_u *)tv_get_string_buf(&argvars[2], buf2)));
vimconv.vc_type = CONV_NONE;
convert_setup(&vimconv, from, to);
- /* If the encodings are equal, no conversion needed. */
- if (vimconv.vc_type == CONV_NONE)
- rettv->vval.v_string = vim_strsave(str);
- else
- rettv->vval.v_string = string_convert(&vimconv, str, NULL);
+ // If the encodings are equal, no conversion needed.
+ if (vimconv.vc_type == CONV_NONE) {
+ rettv->vval.v_string = (char_u *)xstrdup(str);
+ } else {
+ rettv->vval.v_string = string_convert(&vimconv, (char_u *)str, NULL);
+ }
convert_setup(&vimconv, NULL, NULL);
xfree(from);
@@ -11738,13 +10962,12 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- linenr_T lnum;
-
- lnum = get_tv_lnum(argvars);
- if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count)
+ const linenr_T lnum = tv_get_lnum(argvars);
+ if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
rettv->vval.v_number = get_indent_lnum(lnum);
- else
+ } else {
rettv->vval.v_number = -1;
+ }
}
/*
@@ -11766,16 +10989,18 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (l != NULL) {
item = l->lv_first;
if (argvars[2].v_type != VAR_UNKNOWN) {
- int error = FALSE;
+ bool error = false;
- /* Start at specified item. Use the cached index that list_find()
- * sets, so that a negative number also works. */
- item = list_find(l, get_tv_number_chk(&argvars[2], &error));
+ // Start at specified item. Use the cached index that tv_list_find()
+ // sets, so that a negative number also works.
+ item = tv_list_find(l, tv_get_number_chk(&argvars[2], &error));
idx = l->lv_idx;
- if (argvars[3].v_type != VAR_UNKNOWN)
- ic = get_tv_number_chk(&argvars[3], &error);
- if (error)
+ if (argvars[3].v_type != VAR_UNKNOWN) {
+ ic = tv_get_number_chk(&argvars[3], &error);
+ }
+ if (error) {
item = NULL;
+ }
}
for (; item != NULL; item = item->li_next, ++idx)
@@ -11795,85 +11020,131 @@ static int inputsecret_flag = 0;
* prompt. The third argument to f_inputdialog() specifies the value to return
* when the user cancels the prompt.
*/
-static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog)
+void get_user_input(const typval_T *const argvars,
+ typval_T *const rettv, const bool inputdialog)
+ FUNC_ATTR_NONNULL_ALL
{
- char_u *prompt = get_tv_string_chk(&argvars[0]);
- char_u *p = NULL;
- int c;
- char_u buf[NUMBUFLEN];
- int cmd_silent_save = cmd_silent;
- char_u *defstr = (char_u *)"";
- int xp_type = EXPAND_NOTHING;
- char_u *xp_arg = NULL;
-
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
- cmd_silent = FALSE; /* Want to see the prompt. */
- if (prompt != NULL) {
- /* Only the part of the message after the last NL is considered as
- * prompt for the command line */
- p = vim_strrchr(prompt, '\n');
- if (p == NULL)
- p = prompt;
- else {
- ++p;
- c = *p;
- *p = NUL;
- msg_start();
- msg_clr_eos();
- msg_puts_attr(prompt, echo_attr);
- msg_didout = FALSE;
- msg_starthere();
- *p = c;
+ const char *prompt = "";
+ const char *defstr = "";
+ const char *cancelreturn = NULL;
+ 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];
+ if (argvars[0].v_type == VAR_DICT) {
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ emsgf(_("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;
+ }
+ char def[1] = { 0 };
+ cancelreturn = tv_dict_get_string_buf_chk(dict, S_LEN("cancelreturn"),
+ cancelreturn_buf, def);
+ if (cancelreturn == NULL) { // error
+ return;
+ }
+ if (*cancelreturn == NUL) {
+ cancelreturn = NULL;
+ }
+ 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;
}
- cmdline_row = msg_row;
-
if (argvars[1].v_type != VAR_UNKNOWN) {
- defstr = get_tv_string_buf_chk(&argvars[1], buf);
- if (defstr != NULL)
- stuffReadbuffSpec(defstr);
-
- if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) {
- char_u *xp_name;
- int xp_namelen;
- uint32_t argt;
-
- /* input() with a third argument: completion */
- rettv->vval.v_string = NULL;
-
- xp_name = get_tv_string_buf_chk(&argvars[2], buf);
- if (xp_name == NULL)
- return;
-
- xp_namelen = (int)STRLEN(xp_name);
-
- if (parse_compl_arg(xp_name, xp_namelen, &xp_type, &argt,
- &xp_arg) == FAIL)
+ defstr = tv_get_string_buf_chk(&argvars[1], defstr_buf);
+ if (defstr == NULL) {
+ return;
+ }
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ const char *const arg2 = tv_get_string_buf_chk(&argvars[2],
+ cancelreturn_buf);
+ if (arg2 == NULL) {
return;
+ }
+ if (inputdialog) {
+ cancelreturn = arg2;
+ } else {
+ xp_name = arg2;
+ }
}
}
+ }
- if (defstr != NULL) {
- int save_ex_normal_busy = ex_normal_busy;
- ex_normal_busy = 0;
- rettv->vval.v_string =
- getcmdline_prompt(inputsecret_flag ? NUL : '@', p, echo_attr,
- xp_type, xp_arg);
- ex_normal_busy = save_ex_normal_busy;
+ 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;
+ if (parse_compl_arg((char_u *)xp_name, xp_namelen, &xp_type,
+ &argt, (char_u **)&xp_arg) == FAIL) {
+ return;
}
- if (inputdialog && rettv->vval.v_string == NULL
- && argvars[1].v_type != VAR_UNKNOWN
- && argvars[2].v_type != VAR_UNKNOWN)
- rettv->vval.v_string = vim_strsave(get_tv_string_buf(
- &argvars[2], buf));
+ }
- xfree(xp_arg);
+ int cmd_silent_save = cmd_silent;
- /* since the user typed this, no need to wait for return */
- need_wait_return = FALSE;
- msg_didout = FALSE;
+ 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.
+ const char *p = strrchr(prompt, '\n');
+ if (p == NULL) {
+ p = prompt;
+ } else {
+ p++;
+ msg_start();
+ msg_clr_eos();
+ msg_puts_attr_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 =
+ (char_u *)getcmdline_prompt(inputsecret_flag ? 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) {
+ rettv->vval.v_string = (char_u *)xstrdup(cancelreturn);
}
+
+ 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;
}
@@ -11915,7 +11186,7 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
msg_clr_eos();
for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next) {
- msg_puts(get_tv_string(&li->li_tv));
+ msg_puts(tv_get_string(&li->li_tv));
msg_putchar('\n');
}
@@ -11973,36 +11244,34 @@ static void f_inputsecret(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- long before = 0;
- listitem_T *item;
- list_T *l;
- int error = FALSE;
+ list_T *l;
+ bool error = false;
if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listarg), "insert()");
} else if ((l = argvars[0].vval.v_list) != NULL
- && !tv_check_lock(l->lv_lock,
- (char_u *)N_("insert() argument"), true)) {
+ && !tv_check_lock(l->lv_lock, N_("insert() argument"),
+ TV_TRANSLATE)) {
+ long before = 0;
if (argvars[2].v_type != VAR_UNKNOWN) {
- before = get_tv_number_chk(&argvars[2], &error);
+ before = tv_get_number_chk(&argvars[2], &error);
}
if (error) {
// type error; errmsg already given
return;
}
- if (before == l->lv_len)
- item = NULL;
- else {
- item = list_find(l, before);
+ listitem_T *item = NULL;
+ if (before != l->lv_len) {
+ item = tv_list_find(l, before);
if (item == NULL) {
EMSGN(_(e_listidx), before);
l = NULL;
}
}
if (l != NULL) {
- list_insert_tv(l, &argvars[1], item);
- copy_tv(&argvars[0], rettv);
+ tv_list_insert_tv(l, &argvars[1], item);
+ tv_copy(&argvars[0], rettv);
}
}
}
@@ -12012,7 +11281,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_invert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv->vval.v_number = ~get_tv_number_chk(&argvars[0], NULL);
+ rettv->vval.v_number = ~tv_get_number_chk(&argvars[0], NULL);
}
/*
@@ -12020,7 +11289,7 @@ static void f_invert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_isdirectory(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv->vval.v_number = os_isdir(get_tv_string(&argvars[0]));
+ rettv->vval.v_number = os_isdir((const char_u *)tv_get_string(&argvars[0]));
}
/*
@@ -12029,41 +11298,39 @@ static void f_isdirectory(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
lval_T lv;
- char_u *end;
dictitem_T *di;
rettv->vval.v_number = -1;
- end = get_lval(get_tv_string(&argvars[0]), NULL, &lv, FALSE, FALSE,
- GLV_NO_AUTOLOAD, FNE_CHECK_START);
+ const char_u *const end = get_lval((char_u *)tv_get_string(&argvars[0]),
+ NULL,
+ &lv, false, false,
+ GLV_NO_AUTOLOAD|GLV_READ_ONLY,
+ FNE_CHECK_START);
if (end != NULL && lv.ll_name != NULL) {
- if (*end != NUL)
+ if (*end != NUL) {
EMSG(_(e_trailing));
- else {
+ } else {
if (lv.ll_tv == NULL) {
- if (check_changedtick(lv.ll_name))
- rettv->vval.v_number = 1; /* always locked */
- else {
- di = find_var(lv.ll_name, NULL, TRUE);
- if (di != NULL) {
- /* Consider a variable locked when:
- * 1. the variable itself is locked
- * 2. the value of the variable is locked.
- * 3. the List or Dict value is locked.
- */
- rettv->vval.v_number = ((di->di_flags & DI_FLAGS_LOCK)
- || tv_islocked(&di->di_tv));
- }
+ di = find_var((const char *)lv.ll_name, lv.ll_name_len, NULL, true);
+ if (di != NULL) {
+ // Consider a variable locked when:
+ // 1. the variable itself is locked
+ // 2. the value of the variable is locked.
+ // 3. the List or Dict value is locked.
+ rettv->vval.v_number = ((di->di_flags & DI_FLAGS_LOCK)
+ || tv_islocked(&di->di_tv));
}
- } else if (lv.ll_range)
+ } else if (lv.ll_range) {
EMSG(_("E786: Range not allowed"));
- else if (lv.ll_newkey != NULL)
+ } else if (lv.ll_newkey != NULL) {
EMSG2(_(e_dictkey), lv.ll_newkey);
- else if (lv.ll_list != NULL)
- /* List item. */
+ } else if (lv.ll_list != NULL) {
+ // List item.
rettv->vval.v_number = tv_islocked(&lv.ll_li->li_tv);
- else
- /* Dictionary item. */
+ } else {
+ // Dictionary item.
rettv->vval.v_number = tv_islocked(&lv.ll_di->di_tv);
+ }
}
}
@@ -12071,68 +11338,71 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
-/*
- * Turn a dict into a list:
- * "what" == 0: list of keys
- * "what" == 1: list of values
- * "what" == 2: list of items
- */
-static void dict_list(typval_T *argvars, typval_T *rettv, int what)
-{
- list_T *l2;
- dictitem_T *di;
- hashitem_T *hi;
- listitem_T *li;
- listitem_T *li2;
- dict_T *d;
- int todo;
-
- if (argvars[0].v_type != VAR_DICT) {
- EMSG(_(e_dictreq));
+/// Turn a dictionary into a list
+///
+/// @param[in] tv Dictionary to convert. Is checked for actually being
+/// a dictionary, will give an error if not.
+/// @param[out] rettv Location where result will be saved.
+/// @param[in] what What to save in rettv.
+static void dict_list(typval_T *const tv, typval_T *const rettv,
+ const DictListType what)
+{
+ if (tv->v_type != VAR_DICT) {
+ emsgf(_(e_dictreq));
return;
}
- if ((d = argvars[0].vval.v_dict) == NULL)
+ if (tv->vval.v_dict == NULL) {
return;
+ }
- rettv_list_alloc(rettv);
-
- todo = (int)d->dv_hashtab.ht_used;
- for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) {
- if (!HASHITEM_EMPTY(hi)) {
- --todo;
- di = HI2DI(hi);
+ tv_list_alloc_ret(rettv);
- li = listitem_alloc();
- list_append(rettv->vval.v_list, li);
+ TV_DICT_ITER(tv->vval.v_dict, di, {
+ listitem_T *const li = tv_list_item_alloc();
+ tv_list_append(rettv->vval.v_list, li);
- if (what == 0) {
- /* keys() */
+ switch (what) {
+ case kDictListKeys: {
li->li_tv.v_type = VAR_STRING;
- li->li_tv.v_lock = 0;
+ li->li_tv.v_lock = VAR_UNLOCKED;
li->li_tv.vval.v_string = vim_strsave(di->di_key);
- } else if (what == 1) {
- /* values() */
- copy_tv(&di->di_tv, &li->li_tv);
- } else {
- /* items() */
- l2 = list_alloc();
+ break;
+ }
+ case kDictListValues: {
+ tv_copy(&di->di_tv, &li->li_tv);
+ break;
+ }
+ case kDictListItems: {
+ // items()
+ list_T *const sub_l = tv_list_alloc();
li->li_tv.v_type = VAR_LIST;
- li->li_tv.v_lock = 0;
- li->li_tv.vval.v_list = l2;
- ++l2->lv_refcount;
-
- li2 = listitem_alloc();
- list_append(l2, li2);
- li2->li_tv.v_type = VAR_STRING;
- li2->li_tv.v_lock = 0;
- li2->li_tv.vval.v_string = vim_strsave(di->di_key);
-
- li2 = listitem_alloc();
- list_append(l2, li2);
- copy_tv(&di->di_tv, &li2->li_tv);
+ li->li_tv.v_lock = VAR_UNLOCKED;
+ li->li_tv.vval.v_list = sub_l;
+ sub_l->lv_refcount++;
+
+ listitem_T *sub_li = tv_list_item_alloc();
+ tv_list_append(sub_l, sub_li);
+ sub_li->li_tv.v_type = VAR_STRING;
+ sub_li->li_tv.v_lock = VAR_UNLOCKED;
+ sub_li->li_tv.vval.v_string = vim_strsave(di->di_key);
+
+ sub_li = tv_list_item_alloc();
+ tv_list_append(sub_l, sub_li);
+ tv_copy(&di->di_tv, &sub_li->li_tv);
+ break;
}
}
- }
+ });
+}
+
+/// "id()" function
+static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+ FUNC_ATTR_NONNULL_ALL
+{
+ const int len = vim_vsnprintf(NULL, 0, "%p", dummy_ap, argvars);
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = xmalloc(len + 1);
+ vim_vsnprintf((char *)rettv->vval.v_string, len + 1, "%p", dummy_ap, argvars);
}
/*
@@ -12263,8 +11533,8 @@ static void f_jobsend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- ssize_t input_len;
- char *input = (char *) save_tv_as_string(&argvars[1], &input_len, false);
+ ptrdiff_t input_len = 0;
+ char *input = save_tv_as_string(&argvars[1], &input_len, false);
if (!input) {
// Either the error has been handled by save_tv_as_string(), or there is no
// input to send.
@@ -12309,10 +11579,10 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = 1;
}
-static char **tv_to_argv(typval_T *cmd_tv, char **cmd, bool *executable)
+static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable)
{
if (cmd_tv->v_type == VAR_STRING) {
- char *cmd_str = (char *)get_tv_string(cmd_tv);
+ const char *cmd_str = tv_get_string(cmd_tv);
if (cmd) {
*cmd = cmd_str;
}
@@ -12333,8 +11603,8 @@ static char **tv_to_argv(typval_T *cmd_tv, char **cmd, bool *executable)
assert(argl->lv_first);
- const char_u *exe = get_tv_string_chk(&argl->lv_first->li_tv);
- if (!exe || !os_can_exe(exe, NULL, true)) {
+ const char *exe = tv_get_string_chk(&argl->lv_first->li_tv);
+ if (!exe || !os_can_exe((const char_u *)exe, NULL, true)) {
if (exe && executable) {
*executable = false;
}
@@ -12342,16 +11612,16 @@ static char **tv_to_argv(typval_T *cmd_tv, char **cmd, bool *executable)
}
if (cmd) {
- *cmd = (char *)exe;
+ *cmd = exe;
}
// Build the argument vector
int i = 0;
char **argv = xcalloc(argc + 1, sizeof(char *));
for (listitem_T *arg = argl->lv_first; arg != NULL; arg = arg->li_next) {
- char *a = (char *)get_tv_string_chk(&arg->li_tv);
+ const char *a = tv_get_string_chk(&arg->li_tv);
if (!a) {
- // Did emsg in get_tv_string; just deallocate argv.
+ // Did emsg in tv_get_string_chk; just deallocate argv.
shell_free_argv(argv);
return NULL;
}
@@ -12387,23 +11657,26 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
dict_T *job_opts = NULL;
- bool detach = false, rpc = false, pty = false;
- Callback on_stdout = CALLBACK_NONE, on_stderr = CALLBACK_NONE,
- on_exit = CALLBACK_NONE;
+ bool detach = false;
+ bool rpc = false;
+ bool pty = false;
+ Callback on_stdout = CALLBACK_NONE;
+ Callback on_stderr = CALLBACK_NONE;
+ Callback on_exit = CALLBACK_NONE;
char *cwd = NULL;
if (argvars[1].v_type == VAR_DICT) {
job_opts = argvars[1].vval.v_dict;
- detach = get_dict_number(job_opts, "detach") != 0;
- rpc = get_dict_number(job_opts, "rpc") != 0;
- pty = get_dict_number(job_opts, "pty") != 0;
+ detach = tv_dict_get_number(job_opts, "detach") != 0;
+ rpc = tv_dict_get_number(job_opts, "rpc") != 0;
+ pty = tv_dict_get_number(job_opts, "pty") != 0;
if (pty && rpc) {
EMSG2(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set");
shell_free_argv(argv);
return;
}
- char *new_cwd = (char *)get_dict_string(job_opts, "cwd", false);
+ char *new_cwd = tv_dict_get_string(job_opts, "cwd", false);
if (new_cwd && strlen(new_cwd) > 0) {
cwd = new_cwd;
// The new cwd must be a directory.
@@ -12425,15 +11698,15 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
Process *proc = (Process *)&data->proc;
if (pty) {
- uint16_t width = get_dict_number(job_opts, "width");
+ uint16_t width = (uint16_t)tv_dict_get_number(job_opts, "width");
if (width > 0) {
data->proc.pty.width = width;
}
- uint16_t height = get_dict_number(job_opts, "height");
+ uint16_t height = (uint16_t)tv_dict_get_number(job_opts, "height");
if (height > 0) {
data->proc.pty.height = height;
}
- char *term = (char *)get_dict_string(job_opts, "TERM", true);
+ char *term = tv_dict_get_string(job_opts, "TERM", true);
if (term) {
data->proc.pty.term_name = term;
}
@@ -12493,7 +11766,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
list_T *args = argvars[0].vval.v_list;
- list_T *rv = list_alloc();
+ list_T *rv = tv_list_alloc();
ui_busy_start();
MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop);
@@ -12504,11 +11777,11 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
TerminalJobData *data = NULL;
if (arg->li_tv.v_type != VAR_NUMBER
|| !(data = find_job(arg->li_tv.vval.v_number))) {
- list_append_number(rv, -3);
+ tv_list_append_number(rv, -3);
} else {
// append the list item and set the status pointer so we'll collect the
// status code when the job exits
- list_append_number(rv, -1);
+ tv_list_append_number(rv, -1);
data->status_ptr = &rv->lv_last->li_tv.vval.v_number;
// Process any pending events for the job because we'll temporarily
// replace the parent queue
@@ -12589,50 +11862,49 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- garray_T ga;
- char_u *sep;
-
if (argvars[0].v_type != VAR_LIST) {
EMSG(_(e_listreq));
return;
}
- if (argvars[0].vval.v_list == NULL)
+ if (argvars[0].vval.v_list == NULL) {
return;
- if (argvars[1].v_type == VAR_UNKNOWN)
- sep = (char_u *)" ";
- else
- sep = get_tv_string_chk(&argvars[1]);
+ }
+ const char *const sep = (argvars[1].v_type == VAR_UNKNOWN
+ ? " "
+ : tv_get_string_chk(&argvars[1]));
rettv->v_type = VAR_STRING;
if (sep != NULL) {
+ garray_T ga;
ga_init(&ga, (int)sizeof(char), 80);
- list_join(&ga, argvars[0].vval.v_list, (char *) sep);
+ tv_list_join(&ga, argvars[0].vval.v_list, sep);
ga_append(&ga, NUL);
rettv->vval.v_string = (char_u *)ga.ga_data;
- } else
+ } else {
rettv->vval.v_string = NULL;
+ }
}
/// json_decode() function
static void f_json_decode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char numbuf[NUMBUFLEN];
- char *s = NULL;
+ const char *s = NULL;
char *tofree = NULL;
size_t len;
if (argvars[0].v_type == VAR_LIST) {
- if (!encode_vim_list_to_buf(argvars[0].vval.v_list, &len, &s)) {
+ if (!encode_vim_list_to_buf(argvars[0].vval.v_list, &len, &tofree)) {
EMSG(_("E474: Failed to convert list to string"));
return;
}
- tofree = s;
+ s = tofree;
if (s == NULL) {
assert(len == 0);
s = "";
}
} else {
- s = (char *) get_tv_string_buf_chk(&argvars[0], (char_u *) numbuf);
+ s = tv_get_string_buf_chk(&argvars[0], numbuf);
if (s) {
len = strlen(s);
} else {
@@ -12685,24 +11957,28 @@ static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
switch (argvars[0].v_type) {
- case VAR_STRING:
- case VAR_NUMBER:
- rettv->vval.v_number = (varnumber_T)STRLEN(
- get_tv_string(&argvars[0]));
- break;
- case VAR_LIST:
- rettv->vval.v_number = list_len(argvars[0].vval.v_list);
- break;
- case VAR_DICT:
- rettv->vval.v_number = dict_len(argvars[0].vval.v_dict);
- break;
- case VAR_UNKNOWN:
- case VAR_SPECIAL:
- case VAR_FLOAT:
- case VAR_FUNC:
- case VAR_PARTIAL:
- EMSG(_("E701: Invalid type for len()"));
- break;
+ case VAR_STRING:
+ case VAR_NUMBER: {
+ rettv->vval.v_number = (varnumber_T)strlen(
+ tv_get_string(&argvars[0]));
+ break;
+ }
+ case VAR_LIST: {
+ rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list);
+ break;
+ }
+ case VAR_DICT: {
+ rettv->vval.v_number = tv_dict_len(argvars[0].vval.v_dict);
+ break;
+ }
+ case VAR_UNKNOWN:
+ case VAR_SPECIAL:
+ case VAR_FLOAT:
+ case VAR_PARTIAL:
+ case VAR_FUNC: {
+ EMSG(_("E701: Invalid type for len()"));
+ break;
+ }
}
}
@@ -12787,15 +12063,15 @@ static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- linenr_T lnum;
-
- lnum = get_tv_lnum(argvars);
- if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1)
+ const linenr_T lnum = tv_get_lnum(argvars);
+ if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) {
rettv->vval.v_number = -1;
- else
+ } else {
rettv->vval.v_number = ml_find_line_or_offset(curbuf, lnum, NULL);
- if (rettv->vval.v_number >= 0)
- ++rettv->vval.v_number;
+ }
+ if (rettv->vval.v_number >= 0) {
+ rettv->vval.v_number++;
+ }
}
/*
@@ -12803,17 +12079,15 @@ static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- pos_T pos;
- linenr_T lnum;
-
- pos = curwin->w_cursor;
- lnum = get_tv_lnum(argvars);
+ const pos_T pos = curwin->w_cursor;
+ const linenr_T lnum = tv_get_lnum(argvars);
if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
curwin->w_cursor.lnum = lnum;
rettv->vval.v_number = get_lisp_indent();
curwin->w_cursor = pos;
- } else
+ } else {
rettv->vval.v_number = -1;
+ }
}
/*
@@ -12827,71 +12101,117 @@ static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
{
- char_u *keys;
- char_u *which;
- char_u buf[NUMBUFLEN];
- char_u *keys_buf = NULL;
- char_u *rhs;
+ char_u *keys_buf = NULL;
+ char_u *rhs;
int mode;
int abbr = FALSE;
int get_dict = FALSE;
mapblock_T *mp;
int buffer_local;
- /* return empty string for failure */
+ // Return empty string for failure.
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
- keys = get_tv_string(&argvars[0]);
- if (*keys == NUL)
+ char_u *keys = (char_u *)tv_get_string(&argvars[0]);
+ if (*keys == NUL) {
return;
+ }
+ char buf[NUMBUFLEN];
+ const char *which;
if (argvars[1].v_type != VAR_UNKNOWN) {
- which = get_tv_string_buf_chk(&argvars[1], buf);
+ which = tv_get_string_buf_chk(&argvars[1], buf);
if (argvars[2].v_type != VAR_UNKNOWN) {
- abbr = get_tv_number(&argvars[2]);
- if (argvars[3].v_type != VAR_UNKNOWN)
- get_dict = get_tv_number(&argvars[3]);
+ abbr = tv_get_number(&argvars[2]);
+ if (argvars[3].v_type != VAR_UNKNOWN) {
+ get_dict = tv_get_number(&argvars[3]);
+ }
}
- } else
- which = (char_u *)"";
- if (which == NULL)
+ } else {
+ which = "";
+ }
+ if (which == NULL) {
return;
+ }
- mode = get_map_mode(&which, 0);
+ mode = get_map_mode((char_u **)&which, 0);
- keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, false,
+ keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, true,
CPO_TO_CPO_FLAGS);
rhs = check_map(keys, mode, exact, false, abbr, &mp, &buffer_local);
xfree(keys_buf);
if (!get_dict) {
- /* Return a string. */
- if (rhs != NULL)
- rettv->vval.v_string = str2special_save(rhs, FALSE);
+ // Return a string.
+ if (rhs != NULL) {
+ rettv->vval.v_string = (char_u *)str2special_save(
+ (const char *)rhs, false, false);
+ }
} else {
- rettv_dict_alloc(rettv);
+ tv_dict_alloc_ret(rettv);
if (rhs != NULL) {
// Return a dictionary.
- char_u *lhs = str2special_save(mp->m_keys, TRUE);
- char_u *mapmode = map_mode_to_chars(mp->m_mode);
- dict_T *dict = rettv->vval.v_dict;
+ mapblock_fill_dict(rettv->vval.v_dict, mp, buffer_local, true);
+ }
+ }
+}
+
+/// luaeval() function implementation
+static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+ FUNC_ATTR_NONNULL_ALL
+{
+ const char *const str = (const char *)tv_get_string_chk(&argvars[0]);
+ if (str == NULL) {
+ return;
+ }
- dict_add_nr_str(dict, "lhs", 0L, lhs);
- dict_add_nr_str(dict, "rhs", 0L, mp->m_orig_str);
- dict_add_nr_str(dict, "noremap", mp->m_noremap ? 1L : 0L, NULL);
- dict_add_nr_str(dict, "expr", mp->m_expr ? 1L : 0L, NULL);
- dict_add_nr_str(dict, "silent", mp->m_silent ? 1L : 0L, NULL);
- dict_add_nr_str(dict, "sid", (long)mp->m_script_ID, NULL);
- dict_add_nr_str(dict, "buffer", (long)buffer_local, NULL);
- dict_add_nr_str(dict, "nowait", mp->m_nowait ? 1L : 0L, NULL);
- dict_add_nr_str(dict, "mode", 0L, mapmode);
+ executor_eval_lua(cstr_as_string((char *)str), &argvars[1], rettv);
+}
- xfree(lhs);
- xfree(mapmode);
- }
+/// Fill a dictionary with all applicable maparg() like dictionaries
+///
+/// @param dict The dictionary to be filled
+/// @param mp The maphash that contains the mapping information
+/// @param buffer_value The "buffer" value
+/// @param compatible True for compatible with old maparg() dict
+void mapblock_fill_dict(dict_T *const dict,
+ const mapblock_T *const mp,
+ long buffer_value,
+ bool compatible)
+ FUNC_ATTR_NONNULL_ALL
+{
+ char *const lhs = str2special_save((const char *)mp->m_keys,
+ compatible, !compatible);
+ char *const mapmode = map_mode_to_chars(mp->m_mode);
+ varnumber_T noremap_value;
+
+ if (compatible) {
+ // Keep old compatible behavior
+ // This is unable to determine whether a mapping is a <script> mapping
+ noremap_value = !!mp->m_noremap;
+ } else {
+ // Distinguish between <script> mapping
+ // If it's not a <script> mapping, check if it's a noremap
+ noremap_value = mp->m_noremap == REMAP_SCRIPT ? 2 : !!mp->m_noremap;
}
+
+ if (compatible) {
+ tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str);
+ } else {
+ tv_dict_add_allocated_str(dict, S_LEN("rhs"),
+ str2special_save((const char *)mp->m_str, false,
+ true));
+ }
+ tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs);
+ tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value);
+ tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0);
+ tv_dict_add_nr(dict, S_LEN("silent"), mp->m_silent ? 1 : 0);
+ tv_dict_add_nr(dict, S_LEN("sid"), (varnumber_T)mp->m_script_ID);
+ tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_value);
+ tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0);
+ tv_dict_add_allocated_str(dict, S_LEN("mode"), mapmode);
}
/*
@@ -12924,9 +12244,7 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
char_u *str = NULL;
long len = 0;
char_u *expr = NULL;
- char_u *pat;
regmatch_T regmatch;
- char_u patbuf[NUMBUFLEN];
char_u *save_cpo;
long start = 0;
long nth = 1;
@@ -12945,12 +12263,12 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
if (type == 3 || type == 4) {
// type 3: return empty list when there are no matches.
// type 4: return ["", -1, -1, -1]
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
if (type == 4) {
- list_append_string(rettv->vval.v_list, (char_u *)"", 0);
- list_append_number(rettv->vval.v_list, (varnumber_T)-1);
- list_append_number(rettv->vval.v_list, (varnumber_T)-1);
- list_append_number(rettv->vval.v_list, (varnumber_T)-1);
+ tv_list_append_string(rettv->vval.v_list, "", 0);
+ tv_list_append_number(rettv->vval.v_list, -1);
+ tv_list_append_number(rettv->vval.v_list, -1);
+ tv_list_append_number(rettv->vval.v_list, -1);
}
} else if (type == 2) {
rettv->v_type = VAR_STRING;
@@ -12962,25 +12280,29 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
goto theend;
li = l->lv_first;
} else {
- expr = str = get_tv_string(&argvars[0]);
+ expr = str = (char_u *)tv_get_string(&argvars[0]);
len = (long)STRLEN(str);
}
- pat = get_tv_string_buf_chk(&argvars[1], patbuf);
- if (pat == NULL)
+ char patbuf[NUMBUFLEN];
+ const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf);
+ if (pat == NULL) {
goto theend;
+ }
if (argvars[2].v_type != VAR_UNKNOWN) {
- int error = FALSE;
+ bool error = false;
- start = get_tv_number_chk(&argvars[2], &error);
- if (error)
+ start = tv_get_number_chk(&argvars[2], &error);
+ if (error) {
goto theend;
+ }
if (l != NULL) {
- li = list_find(l, start);
- if (li == NULL)
+ li = tv_list_find(l, start);
+ if (li == NULL) {
goto theend;
- idx = l->lv_idx; /* use the cached index */
+ }
+ idx = l->lv_idx; // Use the cached index.
} else {
if (start < 0)
start = 0;
@@ -12997,13 +12319,15 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
}
}
- if (argvars[3].v_type != VAR_UNKNOWN)
- nth = get_tv_number_chk(&argvars[3], &error);
- if (error)
+ if (argvars[3].v_type != VAR_UNKNOWN) {
+ nth = tv_get_number_chk(&argvars[3], &error);
+ }
+ if (error) {
goto theend;
+ }
}
- regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
+ regmatch.regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL) {
regmatch.rm_ic = p_ic;
@@ -13062,29 +12386,32 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
/* return list with matched string and submatches */
for (i = 0; i < NSUBEXP; ++i) {
if (regmatch.endp[i] == NULL) {
- list_append_string(rettv->vval.v_list, (char_u *)"", 0);
+ tv_list_append_string(rettv->vval.v_list, NULL, 0);
} else {
- list_append_string(rettv->vval.v_list,
- regmatch.startp[i],
- (int)(regmatch.endp[i] - regmatch.startp[i]));
+ tv_list_append_string(rettv->vval.v_list,
+ (const char *)regmatch.startp[i],
+ (regmatch.endp[i] - regmatch.startp[i]));
}
}
} else if (type == 2) {
- /* return matched string */
- if (l != NULL)
- copy_tv(&li->li_tv, rettv);
- else
- rettv->vval.v_string = vim_strnsave(regmatch.startp[0],
- (int)(regmatch.endp[0] - regmatch.startp[0]));
- } else if (l != NULL)
+ // Return matched string.
+ if (l != NULL) {
+ tv_copy(&li->li_tv, rettv);
+ } else {
+ rettv->vval.v_string = (char_u *)xmemdupz(
+ (const char *)regmatch.startp[0],
+ (size_t)(regmatch.endp[0] - regmatch.startp[0]));
+ }
+ } else if (l != NULL) {
rettv->vval.v_number = idx;
- else {
- if (type != 0)
+ } else {
+ if (type != 0) {
rettv->vval.v_number =
(varnumber_T)(regmatch.startp[0] - str);
- else
+ } else {
rettv->vval.v_number =
(varnumber_T)(regmatch.endp[0] - str);
+ }
rettv->vval.v_number += (varnumber_T)(str - expr);
}
}
@@ -13093,7 +12420,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
if (type == 4 && l == NULL) {
// matchstrpos() without a list: drop the second item
- listitem_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first->li_next);
+ tv_list_item_remove(rettv->vval.v_list,
+ rettv->vval.v_list->lv_first->li_next);
}
theend:
@@ -13114,40 +12442,42 @@ static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u buf[NUMBUFLEN];
- char_u *grp = get_tv_string_buf_chk(&argvars[0], buf); /* group */
- char_u *pat = get_tv_string_buf_chk(&argvars[1], buf); /* pattern */
- int prio = 10; /* default priority */
+ char grpbuf[NUMBUFLEN];
+ char patbuf[NUMBUFLEN];
+ const char *const grp = tv_get_string_buf_chk(&argvars[0], grpbuf);
+ const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf);
+ int prio = 10;
int id = -1;
- int error = false;
- char_u *conceal_char = NULL;
+ bool error = false;
+ const char *conceal_char = NULL;
rettv->vval.v_number = -1;
- if (grp == NULL || pat == NULL)
+ if (grp == NULL || pat == NULL) {
return;
+ }
if (argvars[2].v_type != VAR_UNKNOWN) {
- prio = get_tv_number_chk(&argvars[2], &error);
+ prio = tv_get_number_chk(&argvars[2], &error);
if (argvars[3].v_type != VAR_UNKNOWN) {
- id = get_tv_number_chk(&argvars[3], &error);
+ id = tv_get_number_chk(&argvars[3], &error);
if (argvars[4].v_type != VAR_UNKNOWN) {
if (argvars[4].v_type != VAR_DICT) {
EMSG(_(e_dictreq));
return;
}
- if (dict_find(argvars[4].vval.v_dict,
- (char_u *)"conceal", -1) != NULL) {
- conceal_char = get_dict_string(argvars[4].vval.v_dict,
- "conceal", false);
+ dictitem_T *di;
+ if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal")))
+ != NULL) {
+ conceal_char = tv_get_string(&di->di_tv);
}
}
}
}
- if (error == true) {
+ if (error) {
return;
}
if (id >= 1 && id <= 3) {
- EMSGN("E798: ID is reserved for \":match\": %" PRId64, id);
+ EMSGN(_("E798: ID is reserved for \":match\": %" PRId64), id);
return;
}
@@ -13158,10 +12488,9 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = -1;
-
- char_u buf[NUMBUFLEN];
- char_u *group;
- group = get_tv_string_buf_chk(&argvars[0], buf);
+
+ char buf[NUMBUFLEN];
+ const char *const group = tv_get_string_buf_chk(&argvars[0], buf);
if (group == NULL) {
return;
}
@@ -13177,24 +12506,24 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- int error = false;
+ bool error = false;
int prio = 10;
int id = -1;
- char_u *conceal_char = NULL;
+ const char *conceal_char = NULL;
if (argvars[2].v_type != VAR_UNKNOWN) {
- prio = get_tv_number_chk(&argvars[2], &error);
+ prio = tv_get_number_chk(&argvars[2], &error);
if (argvars[3].v_type != VAR_UNKNOWN) {
- id = get_tv_number_chk(&argvars[3], &error);
+ id = tv_get_number_chk(&argvars[3], &error);
if (argvars[4].v_type != VAR_UNKNOWN) {
if (argvars[4].v_type != VAR_DICT) {
EMSG(_(e_dictreq));
return;
}
- if (dict_find(argvars[4].vval.v_dict,
- (char_u *)"conceal", -1) != NULL) {
- conceal_char = get_dict_string(argvars[4].vval.v_dict,
- "conceal", false);
+ dictitem_T *di;
+ if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal")))
+ != NULL) {
+ conceal_char = tv_get_string(&di->di_tv);
}
}
}
@@ -13205,7 +12534,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// id == 3 is ok because matchaddpos() is supposed to substitute :3match
if (id == 1 || id == 2) {
- EMSGN("E798: ID is reserved for \"match\": %" PRId64, id);
+ EMSGN(_("E798: ID is reserved for \"match\": %" PRId64), id);
return;
}
@@ -13218,19 +12547,20 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
- int id = get_tv_number(&argvars[0]);
+ int id = tv_get_number(&argvars[0]);
if (id >= 1 && id <= 3) {
matchitem_T *m;
if ((m = (matchitem_T *)get_match(curwin, id)) != NULL) {
- list_append_string(rettv->vval.v_list, syn_id2name(m->hlg_id), -1);
- list_append_string(rettv->vval.v_list, m->pattern, -1);
+ tv_list_append_string(rettv->vval.v_list,
+ (const char *)syn_id2name(m->hlg_id), -1);
+ tv_list_append_string(rettv->vval.v_list, (const char *)m->pattern, -1);
} else {
- list_append_string(rettv->vval.v_list, NULL, -1);
- list_append_string(rettv->vval.v_list, NULL, -1);
+ tv_list_append_string(rettv->vval.v_list, NULL, 0);
+ tv_list_append_string(rettv->vval.v_list, NULL, 0);
}
}
}
@@ -13241,7 +12571,7 @@ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = match_delete(curwin,
- (int)get_tv_number(&argvars[0]), TRUE);
+ (int)tv_get_number(&argvars[0]), true);
}
/*
@@ -13274,54 +12604,53 @@ static void f_matchstrpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
find_some_match(argvars, rettv, 4);
}
-static void max_min(typval_T *argvars, typval_T *rettv, int domax)
+/// Get maximal/minimal number value in a list or dictionary
+///
+/// @param[in] tv List or dictionary to work with. If it contains something
+/// that is not an integer number (or cannot be coerced to
+/// it) error is given.
+/// @param[out] rettv Location where result will be saved. Only assigns
+/// vval.v_number, type is not touched. Returns zero for
+/// empty lists/dictionaries.
+/// @param[in] domax Determines whether maximal or minimal value is desired.
+static void max_min(const typval_T *const tv, typval_T *const rettv,
+ const bool domax)
+ FUNC_ATTR_NONNULL_ALL
{
- long n = 0;
- long i;
- int error = FALSE;
+ varnumber_T n = 0;
+ bool error = false;
- if (argvars[0].v_type == VAR_LIST) {
- list_T *l;
- listitem_T *li;
-
- l = argvars[0].vval.v_list;
- if (l != NULL) {
- li = l->lv_first;
- if (li != NULL) {
- n = get_tv_number_chk(&li->li_tv, &error);
- for (;; ) {
- li = li->li_next;
- if (li == NULL)
- break;
- i = get_tv_number_chk(&li->li_tv, &error);
- if (domax ? i > n : i < n)
- n = i;
+ if (tv->v_type == VAR_LIST) {
+ const list_T *const l = tv->vval.v_list;
+ if (tv_list_len(l) != 0) {
+ n = tv_get_number_chk(&l->lv_first->li_tv, &error);
+ for (const listitem_T *li = l->lv_first->li_next; li != NULL && !error;
+ li = li->li_next) {
+ const varnumber_T i = tv_get_number_chk(&li->li_tv, &error);
+ if (domax ? i > n : i < n) {
+ n = i;
}
}
}
- } else if (argvars[0].v_type == VAR_DICT) {
- dict_T *d;
- int first = TRUE;
- hashitem_T *hi;
- int todo;
-
- d = argvars[0].vval.v_dict;
- if (d != NULL) {
- todo = (int)d->dv_hashtab.ht_used;
- for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) {
- if (!HASHITEM_EMPTY(hi)) {
- --todo;
- i = get_tv_number_chk(&HI2DI(hi)->di_tv, &error);
- if (first) {
- n = i;
- first = FALSE;
- } else if (domax ? i > n : i < n)
- n = i;
+ } else if (tv->v_type == VAR_DICT) {
+ if (tv->vval.v_dict != NULL) {
+ bool first = true;
+ TV_DICT_ITER(tv->vval.v_dict, di, {
+ const varnumber_T i = tv_get_number_chk(&di->di_tv, &error);
+ if (error) {
+ break;
}
- }
+ if (first) {
+ n = i;
+ first = true;
+ } else if (domax ? i > n : i < n) {
+ n = i;
+ }
+ });
}
- } else
- EMSG(_(e_listdictarg));
+ } else {
+ EMSG2(_(e_listdictarg), domax ? "max()" : "min()");
+ }
rettv->vval.v_number = error ? 0 : n;
}
@@ -13346,28 +12675,29 @@ static void f_min(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *dir;
- char_u buf[NUMBUFLEN];
- int prot = 0755;
+ int prot = 0755; // -V536
rettv->vval.v_number = FAIL;
if (check_restricted() || check_secure())
return;
- dir = get_tv_string_buf(&argvars[0], buf);
- if (*dir == NUL)
+ char buf[NUMBUFLEN];
+ const char *const dir = tv_get_string_buf(&argvars[0], buf);
+ if (*dir == NUL) {
rettv->vval.v_number = FAIL;
- else {
- if (*path_tail(dir) == NUL)
- /* remove trailing slashes */
- *path_tail_with_sep(dir) = NUL;
+ } else {
+ if (*path_tail((char_u *)dir) == NUL) {
+ // Remove trailing slashes.
+ *path_tail_with_sep((char_u *)dir) = NUL;
+ }
if (argvars[1].v_type != VAR_UNKNOWN) {
- if (argvars[2].v_type != VAR_UNKNOWN)
- prot = get_tv_number_chk(&argvars[2], NULL);
- if (prot != -1 && STRCMP(get_tv_string(&argvars[1]), "p") == 0) {
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ prot = tv_get_number_chk(&argvars[2], NULL);
+ }
+ if (prot != -1 && strcmp(tv_get_string(&argvars[1]), "p") == 0) {
char *failed_dir;
- int ret = os_mkdir_recurse((char *) dir, prot, &failed_dir);
+ int ret = os_mkdir_recurse(dir, prot, &failed_dir);
if (ret != 0) {
EMSG3(_(e_mkdir), failed_dir, os_strerror(ret));
xfree(failed_dir);
@@ -13383,59 +12713,18 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "mode()" function
- */
+/// "mode()" function
static void f_mode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u buf[3];
-
- buf[1] = NUL;
- buf[2] = NUL;
+ char *mode = get_mode();
- if (VIsual_active) {
- if (VIsual_select)
- buf[0] = VIsual_mode + 's' - 'v';
- else
- buf[0] = VIsual_mode;
- } else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE
- || State == CONFIRM) {
- buf[0] = 'r';
- if (State == ASKMORE)
- buf[1] = 'm';
- else if (State == CONFIRM)
- buf[1] = '?';
- } else if (State == EXTERNCMD)
- buf[0] = '!';
- else if (State & INSERT) {
- if (State & VREPLACE_FLAG) {
- buf[0] = 'R';
- buf[1] = 'v';
- } else if (State & REPLACE_FLAG)
- buf[0] = 'R';
- else
- buf[0] = 'i';
- } else if (State & CMDLINE) {
- buf[0] = 'c';
- if (exmode_active)
- buf[1] = 'v';
- } else if (exmode_active) {
- buf[0] = 'c';
- buf[1] = 'e';
- } else if (State & TERM_FOCUS) {
- buf[0] = 't';
- } else {
- buf[0] = 'n';
- if (finish_op)
- buf[1] = 'o';
+ // Clear out the minor mode when the argument is not a non-zero number or
+ // non-empty string.
+ if (!non_zero_arg(&argvars[0])) {
+ mode[1] = NUL;
}
- /* Clear out the minor mode when the argument is not a non-zero number or
- * non-empty string. */
- if (!non_zero_arg(&argvars[0]))
- buf[1] = NUL;
-
- rettv->vval.v_string = vim_strsave(buf);
+ rettv->vval.v_string = (char_u *)mode;
rettv->v_type = VAR_STRING;
}
@@ -13447,7 +12736,7 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr)
EMSG2(_(e_listarg), "msgpackdump()");
return;
}
- list_T *ret_list = rettv_list_alloc(rettv);
+ list_T *ret_list = tv_list_alloc_ret(rettv);
const list_T *list = argvars[0].vval.v_list;
if (list == NULL) {
return;
@@ -13475,7 +12764,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
EMSG2(_(e_listarg), "msgpackparse()");
return;
}
- list_T *ret_list = rettv_list_alloc(rettv);
+ list_T *ret_list = tv_list_alloc_ret(rettv);
const list_T *list = argvars[0].vval.v_list;
if (list == NULL || list->lv_first == NULL) {
return;
@@ -13520,9 +12809,9 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
goto f_msgpackparse_exit;
}
if (result == MSGPACK_UNPACK_SUCCESS) {
- listitem_T *li = listitem_alloc();
+ listitem_T *li = tv_list_item_alloc();
li->li_tv.v_type = VAR_UNKNOWN;
- list_append(ret_list, li);
+ tv_list_append(ret_list, li);
if (msgpack_to_vim(unpacked.data, &li->li_tv) == FAIL) {
EMSG2(_(e_invarg2), "Failed to convert msgpack string");
goto f_msgpackparse_exit;
@@ -13553,13 +12842,14 @@ static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
linenr_T lnum;
- for (lnum = get_tv_lnum(argvars);; ++lnum) {
+ for (lnum = tv_get_lnum(argvars);; lnum++) {
if (lnum < 0 || lnum > curbuf->b_ml.ml_line_count) {
lnum = 0;
break;
}
- if (*skipwhite(ml_get(lnum)) != NUL)
+ if (*skipwhite(ml_get(lnum)) != NUL) {
break;
+ }
}
rettv->vval.v_number = lnum;
}
@@ -13569,23 +12859,32 @@ static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u buf[NUMBUFLEN];
-
- if (has_mbyte) {
- int utf8 = 0;
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ if (!tv_check_num(&argvars[1])) {
+ return;
+ }
+ }
- if (argvars[1].v_type != VAR_UNKNOWN)
- utf8 = get_tv_number_chk(&argvars[1], NULL);
- if (utf8)
- buf[(*utf_char2bytes)((int)get_tv_number(&argvars[0]), buf)] = NUL;
- else
- buf[(*mb_char2bytes)((int)get_tv_number(&argvars[0]), buf)] = NUL;
- } else {
- buf[0] = (char_u)get_tv_number(&argvars[0]);
- buf[1] = NUL;
+ bool error = false;
+ const varnumber_T num = tv_get_number_chk(&argvars[0], &error);
+ if (error) {
+ return;
}
+ if (num < 0) {
+ emsgf(_("E5070: Character number must not be less than zero"));
+ return;
+ }
+ if (num > INT_MAX) {
+ emsgf(_("E5071: Character number must not be greater than INT_MAX (%i)"),
+ INT_MAX);
+ return;
+ }
+
+ char buf[MB_MAXBYTES];
+ const int len = utf_char2bytes((int)num, (char_u *)buf);
+
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = vim_strsave(buf);
+ rettv->vval.v_string = xmemdupz(buf, (size_t)len);
}
/*
@@ -13593,8 +12892,8 @@ static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_or(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL)
- | get_tv_number_chk(&argvars[1], NULL);
+ rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL)
+ | tv_get_number_chk(&argvars[1], NULL);
}
/*
@@ -13603,11 +12902,11 @@ static void f_or(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = get_tv_string_chk(&argvars[0]);
- if (!rettv->vval.v_string) {
+ const char *const s = tv_get_string_chk(&argvars[0]);
+ if (!s) {
return;
}
- rettv->vval.v_string = shorten_dir(vim_strsave(rettv->vval.v_string));
+ rettv->vval.v_string = shorten_dir((char_u *)xstrdup(s));
}
/*
@@ -13615,14 +12914,15 @@ static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- float_T fx, fy;
+ float_T fx;
+ float_T fy;
rettv->v_type = VAR_FLOAT;
- if (get_float_arg(argvars, &fx) == OK
- && get_float_arg(&argvars[1], &fy) == OK)
+ if (tv_get_float_chk(argvars, &fx) && tv_get_float_chk(&argvars[1], &fy)) {
rettv->vval.v_float = pow(fx, fy);
- else
+ } else {
rettv->vval.v_float = 0.0;
+ }
}
/*
@@ -13630,23 +12930,17 @@ static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_prevnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- linenr_T lnum;
-
- lnum = get_tv_lnum(argvars);
- if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
+ linenr_T lnum = tv_get_lnum(argvars);
+ if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) {
lnum = 0;
- else
- while (lnum >= 1 && *skipwhite(ml_get(lnum)) == NUL)
- --lnum;
+ } else {
+ while (lnum >= 1 && *skipwhite(ml_get(lnum)) == NUL) {
+ lnum--;
+ }
+ }
rettv->vval.v_number = lnum;
}
-/* This dummy va_list is here because:
- * - passing a NULL pointer doesn't work when va_list isn't a pointer
- * - locally in the function results in a "used before set" warning
- * - using va_start() to initialize it gives "function with fixed args" error */
-static va_list ap;
-
/*
* "printf()" function
*/
@@ -13655,19 +12949,18 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
{
- char_u buf[NUMBUFLEN];
int len;
int saved_did_emsg = did_emsg;
- char *fmt;
- /* Get the required length, allocate the buffer and do it for real. */
- did_emsg = FALSE;
- fmt = (char *)get_tv_string_buf(&argvars[0], buf);
- len = vim_vsnprintf(NULL, 0, fmt, ap, argvars + 1);
+ // Get the required length, allocate the buffer and do it for real.
+ did_emsg = false;
+ char buf[NUMBUFLEN];
+ const char *fmt = tv_get_string_buf(&argvars[0], buf);
+ len = vim_vsnprintf(NULL, 0, fmt, dummy_ap, argvars + 1);
if (!did_emsg) {
char *s = xmalloc(len + 1);
rettv->vval.v_string = (char_u *)s;
- (void)vim_vsnprintf(s, len + 1, fmt, ap, argvars + 1);
+ (void)vim_vsnprintf(s, len + 1, fmt, dummy_ap, argvars + 1);
}
did_emsg |= saved_did_emsg;
}
@@ -13703,32 +12996,34 @@ static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- long start;
- long end;
- long stride = 1;
- long i;
- int error = FALSE;
+ varnumber_T start;
+ varnumber_T end;
+ varnumber_T stride = 1;
+ varnumber_T i;
+ bool error = false;
- start = get_tv_number_chk(&argvars[0], &error);
+ start = tv_get_number_chk(&argvars[0], &error);
if (argvars[1].v_type == VAR_UNKNOWN) {
end = start - 1;
start = 0;
} else {
- end = get_tv_number_chk(&argvars[1], &error);
- if (argvars[2].v_type != VAR_UNKNOWN)
- stride = get_tv_number_chk(&argvars[2], &error);
+ end = tv_get_number_chk(&argvars[1], &error);
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ stride = tv_get_number_chk(&argvars[2], &error);
+ }
}
- if (error)
- return; /* type error; errmsg already given */
- if (stride == 0)
- EMSG(_("E726: Stride is zero"));
- else if (stride > 0 ? end + 1 < start : end - 1 > start)
- EMSG(_("E727: Start past end"));
- else {
- rettv_list_alloc(rettv);
+ if (error) {
+ return; // Type error; errmsg already given.
+ }
+ if (stride == 0) {
+ emsgf(_("E726: Stride is zero"));
+ } else if (stride > 0 ? end + 1 < start : end - 1 > start) {
+ emsgf(_("E727: Start past end"));
+ } else {
+ tv_list_alloc_ret(rettv);
for (i = start; stride > 0 ? i <= end : i >= end; i += stride) {
- list_append_number(rettv->vval.v_list, (varnumber_T)i);
+ tv_list_append_number(rettv->vval.v_list, (varnumber_T)i);
}
}
}
@@ -13738,8 +13033,7 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- int binary = FALSE;
- char_u *fname;
+ bool binary = false;
FILE *fd;
char_u buf[(IOSIZE/256)*256]; /* rounded to avoid odd + 1 */
int io_size = sizeof(buf);
@@ -13753,19 +13047,21 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char_u *start; /* start of current line */
if (argvars[1].v_type != VAR_UNKNOWN) {
- if (STRCMP(get_tv_string(&argvars[1]), "b") == 0)
- binary = TRUE;
- if (argvars[2].v_type != VAR_UNKNOWN)
- maxline = get_tv_number(&argvars[2]);
+ if (strcmp(tv_get_string(&argvars[1]), "b") == 0) {
+ binary = true;
+ }
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ maxline = tv_get_number(&argvars[2]);
+ }
}
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
- /* Always open the file in binary mode, library functions have a mind of
- * their own about CR-LF conversion. */
- fname = get_tv_string(&argvars[0]);
- if (*fname == NUL || (fd = mch_fopen((char *)fname, READBIN)) == NULL) {
- EMSG2(_(e_notopen), *fname == NUL ? (char_u *)_("<empty>") : fname);
+ // Always open the file in binary mode, library functions have a mind of
+ // their own about CR-LF conversion.
+ const char *const fname = tv_get_string(&argvars[0]);
+ if (*fname == NUL || (fd = mch_fopen(fname, READBIN)) == NULL) {
+ EMSG2(_(e_notopen), *fname == NUL ? _("<empty>") : fname);
return;
}
@@ -13808,11 +13104,11 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
prevlen = prevsize = 0;
}
- li = listitem_alloc();
+ li = tv_list_item_alloc();
li->li_tv.v_type = VAR_STRING;
li->li_tv.v_lock = 0;
li->li_tv.vval.v_string = s;
- list_append(rettv->vval.v_list, li);
+ tv_list_append(rettv->vval.v_list, li);
start = p + 1; /* step over newline */
if ((++cnt >= maxline && maxline >= 0) || readlen <= 0)
@@ -13842,8 +13138,9 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/* have to shuffle buf to close gap */
int adjust_prevlen = 0;
- if (dest < buf) {
- adjust_prevlen = (int)(buf - dest); /* must be 1 or 2 */
+ if (dest < buf) { // -V782
+ adjust_prevlen = (int)(buf - dest); // -V782
+ // adjust_prevlen must be 1 or 2.
dest = buf;
}
if (readlen > p - buf + 1)
@@ -13887,8 +13184,8 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
if (maxline < 0)
while (cnt > -maxline) {
- listitem_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first);
- --cnt;
+ tv_list_item_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first);
+ cnt--;
}
xfree(prev);
@@ -13910,9 +13207,9 @@ static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL
return FAIL;
}
- int error = false;
- varnumber_T n1 = list_find_nr(arg->vval.v_list, 0L, &error);
- varnumber_T n2 = list_find_nr(arg->vval.v_list, 1L, &error);
+ bool error = false;
+ varnumber_T n1 = tv_list_find_nr(arg->vval.v_list, 0L, &error);
+ varnumber_T n2 = tv_list_find_nr(arg->vval.v_list, 1L, &error);
if (error) {
return FAIL;
}
@@ -13920,7 +13217,7 @@ static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL
// in f_reltime() we split up the 64-bit proftime_T into two 32-bit
// values, now we combine them again.
union {
- struct { varnumber_T low, high; } split;
+ struct { int32_t low, high; } split;
proftime_T prof;
} u = { .split.high = n1, .split.low = n2 };
@@ -13961,7 +13258,7 @@ static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// (varnumber_T is defined as int). For all our supported platforms, int's
// are at least 32-bits wide. So we'll use two 32-bit values to store it.
union {
- struct { varnumber_T low, high; } split;
+ struct { int32_t low, high; } split;
proftime_T prof;
} u = { .prof = res };
@@ -13971,9 +13268,9 @@ static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
STATIC_ASSERT(sizeof(u.prof) == sizeof(u) && sizeof(u.split) == sizeof(u),
"type punning will produce incorrect results on this platform");
- rettv_list_alloc(rettv);
- list_append_number(rettv->vval.v_list, u.split.high);
- list_append_number(rettv->vval.v_list, u.split.low);
+ tv_list_alloc_ret(rettv);
+ tv_list_append_number(rettv->vval.v_list, u.split.high);
+ tv_list_append_number(rettv->vval.v_list, u.split.low);
}
/// f_reltimestr - return a string that represents the value of {time}
@@ -14003,28 +13300,27 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
listitem_T *li;
long idx;
long end;
- char_u *key;
dict_T *d;
dictitem_T *di;
- char_u *arg_errmsg = (char_u *)N_("remove() argument");
+ const char *const arg_errmsg = N_("remove() argument");
if (argvars[0].v_type == VAR_DICT) {
if (argvars[2].v_type != VAR_UNKNOWN) {
EMSG2(_(e_toomanyarg), "remove()");
} else if ((d = argvars[0].vval.v_dict) != NULL
- && !tv_check_lock(d->dv_lock, arg_errmsg, true)) {
- key = get_tv_string_chk(&argvars[1]);
+ && !tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) {
+ const char *key = tv_get_string_chk(&argvars[1]);
if (key != NULL) {
- di = dict_find(d, key, -1);
+ di = tv_dict_find(d, key, -1);
if (di == NULL) {
EMSG2(_(e_dictkey), key);
- } else if (!var_check_fixed(di->di_flags, arg_errmsg, true)
- && !var_check_ro(di->di_flags, arg_errmsg, true)) {
+ } else if (!var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE)
+ && !var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) {
*rettv = di->di_tv;
- init_tv(&di->di_tv);
- dictitem_remove(d, di);
- if (is_watched(d)) {
- dictwatcher_notify(d, (char *)key, NULL, rettv);
+ di->di_tv = TV_INITIAL_VALUE;
+ tv_dict_item_remove(d, di);
+ if (tv_dict_is_watched(d)) {
+ tv_dict_watcher_notify(d, key, NULL, rettv);
}
}
}
@@ -14032,28 +13328,28 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} else if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listdictarg), "remove()");
} else if ((l = argvars[0].vval.v_list) != NULL
- && !tv_check_lock(l->lv_lock, arg_errmsg, true)) {
- int error = (int)false;
+ && !tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE)) {
+ bool error = false;
- idx = get_tv_number_chk(&argvars[1], &error);
- if (error)
- ; /* type error: do nothing, errmsg already given */
- else if ((item = list_find(l, idx)) == NULL)
+ idx = tv_get_number_chk(&argvars[1], &error);
+ if (error) {
+ // Type error: do nothing, errmsg already given.
+ } else if ((item = tv_list_find(l, idx)) == NULL) {
EMSGN(_(e_listidx), idx);
- else {
+ } else {
if (argvars[2].v_type == VAR_UNKNOWN) {
// Remove one item, return its value.
- vim_list_remove(l, item, item);
+ tv_list_remove_items(l, item, item);
*rettv = item->li_tv;
xfree(item);
} else {
- /* Remove range of items, return list with values. */
- end = get_tv_number_chk(&argvars[2], &error);
- if (error)
- ; /* type error: do nothing */
- else if ((item2 = list_find(l, end)) == NULL)
+ // Remove range of items, return list with values.
+ end = tv_get_number_chk(&argvars[2], &error);
+ if (error) {
+ // Type error: do nothing.
+ } else if ((item2 = tv_list_find(l, end)) == NULL) {
EMSGN(_(e_listidx), end);
- else {
+ } else {
int cnt = 0;
for (li = item; li != NULL; li = li->li_next) {
@@ -14061,11 +13357,11 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (li == item2)
break;
}
- if (li == NULL) /* didn't find "item2" after "item" */
- EMSG(_(e_invrange));
- else {
- vim_list_remove(l, item, item2);
- l = rettv_list_alloc(rettv);
+ if (li == NULL) { // Didn't find "item2" after "item".
+ emsgf(_(e_invrange));
+ } else {
+ tv_list_remove_items(l, item, item2);
+ l = tv_list_alloc_ret(rettv);
l->lv_first = item;
l->lv_last = item2;
item->li_prev = NULL;
@@ -14083,13 +13379,14 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u buf[NUMBUFLEN];
-
- if (check_restricted() || check_secure())
+ if (check_restricted() || check_secure()) {
rettv->vval.v_number = -1;
- else
- rettv->vval.v_number = vim_rename(get_tv_string(&argvars[0]),
- get_tv_string_buf(&argvars[1], buf));
+ } else {
+ char buf[NUMBUFLEN];
+ rettv->vval.v_number = vim_rename(
+ (const char_u *)tv_get_string(&argvars[0]),
+ (const char_u *)tv_get_string_buf(&argvars[1], buf));
+ }
}
/*
@@ -14097,32 +13394,37 @@ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *p;
- int n;
-
- n = get_tv_number(&argvars[1]);
+ varnumber_T n = tv_get_number(&argvars[1]);
if (argvars[0].v_type == VAR_LIST) {
- rettv_list_alloc(rettv);
- if (argvars[0].vval.v_list != NULL) {
- while (n-- > 0) {
- list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL);
- }
+ tv_list_alloc_ret(rettv);
+ while (n-- > 0) {
+ tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL);
}
} else {
- p = get_tv_string(&argvars[0]);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
+ if (n <= 0) {
+ return;
+ }
- int slen = (int)STRLEN(p);
- int len = slen * n;
- if (len <= 0)
+ const char *const p = tv_get_string(&argvars[0]);
+
+ const size_t slen = strlen(p);
+ if (slen == 0) {
+ return;
+ }
+ const size_t len = slen * n;
+ // Detect overflow.
+ if (len / n != slen) {
return;
+ }
- char_u *r = xmallocz(len);
- for (int i = 0; i < n; i++)
- memmove(r + i * slen, p, (size_t)slen);
+ char *const r = xmallocz(len);
+ for (varnumber_T i = 0; i < n; i++) {
+ memmove(r + i * slen, p, slen);
+ }
- rettv->vval.v_string = r;
+ rettv->vval.v_string = (char_u *)r;
}
}
@@ -14131,61 +13433,49 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *p;
-#ifdef HAVE_READLINK
- char_u *buf = NULL;
-#endif
-
- p = get_tv_string(&argvars[0]);
+ rettv->v_type = VAR_STRING;
+ const char *fname = tv_get_string(&argvars[0]);
#ifdef WIN32
- {
- char_u *v = NULL;
-
- v = os_resolve_shortcut(p);
- if (v != NULL) {
- rettv->vval.v_string = v;
- } else {
- rettv->vval.v_string = vim_strsave(p);
- }
- }
+ char *const v = os_resolve_shortcut(fname);
+ rettv->vval.v_string = (char_u *)(v == NULL ? xstrdup(fname) : v);
#else
# ifdef HAVE_READLINK
{
- char_u *cpy;
- int len;
- char_u *remain = NULL;
- char_u *q;
- int is_relative_to_current = FALSE;
- int has_trailing_pathsep = FALSE;
+ bool is_relative_to_current = false;
+ bool has_trailing_pathsep = false;
int limit = 100;
- p = vim_strsave(p);
+ char *p = xstrdup(fname);
if (p[0] == '.' && (vim_ispathsep(p[1])
- || (p[1] == '.' && (vim_ispathsep(p[2])))))
- is_relative_to_current = TRUE;
+ || (p[1] == '.' && (vim_ispathsep(p[2]))))) {
+ is_relative_to_current = true;
+ }
- len = STRLEN(p);
- if (len > 0 && after_pathsep((char *)p, (char *)p + len)) {
- has_trailing_pathsep = TRUE;
- p[len - 1] = NUL; /* the trailing slash breaks readlink() */
+ ptrdiff_t len = (ptrdiff_t)strlen(p);
+ if (len > 0 && after_pathsep(p, p + len)) {
+ has_trailing_pathsep = true;
+ p[len - 1] = NUL; // The trailing slash breaks readlink().
}
- q = path_next_component(p);
+ char *q = (char *)path_next_component(p);
+ char *remain = NULL;
if (*q != NUL) {
- /* Separate the first path component in "p", and keep the
- * remainder (beginning with the path separator). */
- remain = vim_strsave(q - 1);
+ // Separate the first path component in "p", and keep the
+ // remainder (beginning with the path separator).
+ remain = xstrdup(q - 1);
q[-1] = NUL;
}
- buf = xmallocz(MAXPATHL);
+ char *const buf = xmallocz(MAXPATHL);
+ char *cpy;
for (;; ) {
for (;; ) {
- len = readlink((char *)p, (char *)buf, MAXPATHL);
- if (len <= 0)
+ len = readlink(p, buf, MAXPATHL);
+ if (len <= 0) {
break;
+ }
buf[len] = NUL;
if (limit-- == 0) {
@@ -14193,66 +13483,72 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
xfree(remain);
EMSG(_("E655: Too many symbolic links (cycle?)"));
rettv->vval.v_string = NULL;
- goto fail;
+ xfree(buf);
+ return;
}
- /* Ensure that the result will have a trailing path separator
- * if the argument has one. */
- if (remain == NULL && has_trailing_pathsep)
- add_pathsep((char *)buf);
+ // Ensure that the result will have a trailing path separator
+ // if the argument has one. */
+ if (remain == NULL && has_trailing_pathsep) {
+ add_pathsep(buf);
+ }
- /* Separate the first path component in the link value and
- * concatenate the remainders. */
- q = path_next_component(vim_ispathsep(*buf) ? buf + 1 : buf);
+ // Separate the first path component in the link value and
+ // concatenate the remainders. */
+ q = (char *)path_next_component(vim_ispathsep(*buf) ? buf + 1 : buf);
if (*q != NUL) {
cpy = remain;
- remain = remain ?
- concat_str(q - 1, remain) : (char_u *) xstrdup((char *)q - 1);
+ remain = (remain
+ ? (char *)concat_str((char_u *)q - 1, (char_u *)remain)
+ : xstrdup(q - 1));
xfree(cpy);
q[-1] = NUL;
}
- q = path_tail(p);
+ q = (char *)path_tail((char_u *)p);
if (q > p && *q == NUL) {
- /* Ignore trailing path separator. */
+ // Ignore trailing path separator.
q[-1] = NUL;
- q = path_tail(p);
+ q = (char *)path_tail((char_u *)p);
}
- if (q > p && !path_is_absolute_path(buf)) {
- /* symlink is relative to directory of argument */
- cpy = xmalloc(STRLEN(p) + STRLEN(buf) + 1);
- STRCPY(cpy, p);
- STRCPY(path_tail(cpy), buf);
- xfree(p);
- p = cpy;
+ if (q > p && !path_is_absolute_path((const char_u *)buf)) {
+ // Symlink is relative to directory of argument. Replace the
+ // symlink with the resolved name in the same directory.
+ const size_t p_len = strlen(p);
+ const size_t buf_len = strlen(buf);
+ p = xrealloc(p, p_len + buf_len + 1);
+ memcpy(path_tail((char_u *)p), buf, buf_len + 1);
} else {
xfree(p);
- p = vim_strsave(buf);
+ p = xstrdup(buf);
}
}
- if (remain == NULL)
+ if (remain == NULL) {
break;
+ }
- /* Append the first path component of "remain" to "p". */
- q = path_next_component(remain + 1);
+ // Append the first path component of "remain" to "p".
+ q = (char *)path_next_component(remain + 1);
len = q - remain - (*q != NUL);
- cpy = vim_strnsave(p, STRLEN(p) + len);
- STRNCAT(cpy, remain, len);
+ const size_t p_len = strlen(p);
+ cpy = xmallocz(p_len + len);
+ memcpy(cpy, p, p_len + 1);
+ xstrlcat(cpy + p_len, remain, len + 1);
xfree(p);
p = cpy;
- /* Shorten "remain". */
- if (*q != NUL)
+ // Shorten "remain".
+ if (*q != NUL) {
STRMOVE(remain, q - 1);
- else {
+ } else {
xfree(remain);
remain = NULL;
}
}
- /* If the result is a relative path name, make it explicitly relative to
- * the current directory if and only if the argument had this form. */
+ // If the result is a relative path name, make it explicitly relative to
+ // the current directory if and only if the argument had this form.
if (!vim_ispathsep(*p)) {
if (is_relative_to_current
&& *p != NUL
@@ -14262,42 +13558,40 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|| (p[1] == '.'
&& (p[2] == NUL
|| vim_ispathsep(p[2])))))) {
- /* Prepend "./". */
- cpy = concat_str((char_u *)"./", p);
+ // Prepend "./".
+ cpy = (char *)concat_str((const char_u *)"./", (const char_u *)p);
xfree(p);
p = cpy;
} else if (!is_relative_to_current) {
- /* Strip leading "./". */
+ // Strip leading "./".
q = p;
- while (q[0] == '.' && vim_ispathsep(q[1]))
+ while (q[0] == '.' && vim_ispathsep(q[1])) {
q += 2;
- if (q > p)
+ }
+ if (q > p) {
STRMOVE(p, p + 2);
+ }
}
}
- /* Ensure that the result will have no trailing path separator
- * if the argument had none. But keep "/" or "//". */
+ // Ensure that the result will have no trailing path separator
+ // if the argument had none. But keep "/" or "//".
if (!has_trailing_pathsep) {
- q = p + STRLEN(p);
- if (after_pathsep((char *)p, (char *)q))
- *path_tail_with_sep(p) = NUL;
+ q = p + strlen(p);
+ if (after_pathsep(p, q)) {
+ *path_tail_with_sep((char_u *)p) = NUL;
+ }
}
- rettv->vval.v_string = p;
+ rettv->vval.v_string = (char_u *)p;
+ xfree(buf);
}
# else
- rettv->vval.v_string = vim_strsave(p);
+ rettv->vval.v_string = (char_u *)xstrdup(p);
# endif
#endif
simplify_filename(rettv->vval.v_string);
-
-#ifdef HAVE_READLINK
-fail:
- xfree(buf);
-#endif
- rettv->v_type = VAR_STRING;
}
/*
@@ -14305,25 +13599,23 @@ fail:
*/
static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- list_T *l;
- listitem_T *li, *ni;
-
+ list_T *l;
if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listarg), "reverse()");
} else if ((l = argvars[0].vval.v_list) != NULL
- && !tv_check_lock(l->lv_lock,
- (char_u *)N_("reverse() argument"), true)) {
- li = l->lv_last;
+ && !tv_check_lock(l->lv_lock, N_("reverse() argument"),
+ TV_TRANSLATE)) {
+ listitem_T *li = l->lv_last;
l->lv_first = l->lv_last = NULL;
l->lv_len = 0;
while (li != NULL) {
- ni = li->li_prev;
- list_append(l, li);
+ listitem_T *const ni = li->li_prev;
+ tv_list_append(l, li);
li = ni;
}
rettv->vval.v_list = l;
rettv->v_type = VAR_LIST;
- ++l->lv_refcount;
+ l->lv_refcount++;
l->lv_idx = l->lv_len - l->lv_idx - 1;
}
}
@@ -14345,40 +13637,45 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static int get_search_arg(typval_T *varp, int *flagsp)
{
int dir = FORWARD;
- char_u *flags;
- char_u nbuf[NUMBUFLEN];
int mask;
if (varp->v_type != VAR_UNKNOWN) {
- flags = get_tv_string_buf_chk(varp, nbuf);
- if (flags == NULL)
- return 0; /* type error; errmsg already given */
+ char nbuf[NUMBUFLEN];
+ const char *flags = tv_get_string_buf_chk(varp, nbuf);
+ if (flags == NULL) {
+ return 0; // Type error; errmsg already given.
+ }
while (*flags != NUL) {
switch (*flags) {
- case 'b': dir = BACKWARD; break;
- case 'w': p_ws = true; break;
- case 'W': p_ws = false; break;
- default: mask = 0;
- if (flagsp != NULL)
- switch (*flags) {
- case 'c': mask = SP_START; break;
- case 'e': mask = SP_END; break;
- case 'm': mask = SP_RETCOUNT; break;
- case 'n': mask = SP_NOMOVE; break;
- case 'p': mask = SP_SUBPAT; break;
- case 'r': mask = SP_REPEAT; break;
- case 's': mask = SP_SETPCMARK; break;
- case 'z': mask = SP_COLUMN; break;
+ case 'b': dir = BACKWARD; break;
+ case 'w': p_ws = true; break;
+ case 'W': p_ws = false; break;
+ default: {
+ mask = 0;
+ if (flagsp != NULL) {
+ switch (*flags) {
+ case 'c': mask = SP_START; break;
+ case 'e': mask = SP_END; break;
+ case 'm': mask = SP_RETCOUNT; break;
+ case 'n': mask = SP_NOMOVE; break;
+ case 'p': mask = SP_SUBPAT; break;
+ case 'r': mask = SP_REPEAT; break;
+ case 's': mask = SP_SETPCMARK; break;
+ case 'z': mask = SP_COLUMN; break;
+ }
}
- if (mask == 0) {
- EMSG2(_(e_invarg2), flags);
- dir = 0;
- } else
- *flagsp |= mask;
+ if (mask == 0) {
+ emsgf(_(e_invarg2), flags);
+ dir = 0;
+ } else {
+ *flagsp |= mask;
+ }
+ }
}
- if (dir == 0)
+ if (dir == 0) {
break;
- ++flags;
+ }
+ flags++;
}
}
return dir;
@@ -14388,7 +13685,6 @@ static int get_search_arg(typval_T *varp, int *flagsp)
static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
{
int flags;
- char_u *pat;
pos_T pos;
pos_T save_cursor;
bool save_p_ws = p_ws;
@@ -14400,10 +13696,11 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
int options = SEARCH_KEEP;
int subpatnum;
- pat = get_tv_string(&argvars[0]);
- dir = get_search_arg(&argvars[1], flagsp); /* may set p_ws */
- if (dir == 0)
+ const char *const pat = tv_get_string(&argvars[0]);
+ dir = get_search_arg(&argvars[1], flagsp); // May set p_ws.
+ if (dir == 0) {
goto theend;
+ }
flags = *flagsp;
if (flags & SP_START) {
options |= SEARCH_START;
@@ -14417,13 +13714,15 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
/* Optional arguments: line number to stop searching and timeout. */
if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) {
- lnum_stop = get_tv_number_chk(&argvars[2], NULL);
- if (lnum_stop < 0)
+ lnum_stop = tv_get_number_chk(&argvars[2], NULL);
+ if (lnum_stop < 0) {
goto theend;
+ }
if (argvars[3].v_type != VAR_UNKNOWN) {
- time_limit = get_tv_number_chk(&argvars[3], NULL);
- if (time_limit < 0)
+ time_limit = tv_get_number_chk(&argvars[3], NULL);
+ if (time_limit < 0) {
goto theend;
+ }
}
}
@@ -14438,13 +13737,13 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
*/
if (((flags & (SP_REPEAT | SP_RETCOUNT)) != 0)
|| ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) {
- EMSG2(_(e_invarg2), get_tv_string(&argvars[1]));
+ EMSG2(_(e_invarg2), tv_get_string(&argvars[1]));
goto theend;
}
pos = save_cursor = curwin->w_cursor;
- subpatnum = searchit(curwin, curbuf, &pos, dir, pat, 1L,
- options, RE_SEARCH, (linenr_T)lnum_stop, &tm);
+ subpatnum = searchit(curwin, curbuf, &pos, dir, (char_u *)pat, 1,
+ options, RE_SEARCH, (linenr_T)lnum_stop, &tm);
if (subpatnum != FAIL) {
if (flags & SP_SUBPAT)
retval = subpatnum;
@@ -14501,7 +13800,7 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (!channel_send_event((uint64_t)argvars[0].vval.v_number,
- (char *)get_tv_string(&argvars[1]),
+ tv_get_string(&argvars[1]),
args)) {
EMSG2(_(e_invarg2), "Channel doesn't exist");
return;
@@ -14568,7 +13867,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
Error err = ERROR_INIT;
Object result = channel_send_call((uint64_t)argvars[0].vval.v_number,
- (char *)get_tv_string(&argvars[1]),
+ tv_get_string(&argvars[1]),
args,
&err);
@@ -14583,7 +13882,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
restore_funccal(save_funccalp);
}
- if (err.set) {
+ if (ERROR_SET(&err)) {
nvim_err_writeln(cstr_as_string(err.msg));
goto end;
}
@@ -14594,6 +13893,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
end:
api_free_object(result);
+ api_clear_error(&err);
}
// "rpcstart()" function (DEPRECATED)
@@ -14643,7 +13943,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// Copy arguments to the vector
if (argsl > 0) {
for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) {
- argv[i++] = xstrdup((char *) get_tv_string(&arg->li_tv));
+ argv[i++] = xstrdup(tv_get_string(&arg->li_tv));
}
}
@@ -14685,17 +13985,16 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- int row;
- int col;
int c;
- row = get_tv_number_chk(&argvars[0], NULL) - 1;
- col = get_tv_number_chk(&argvars[1], NULL) - 1;
+ const int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1;
+ const int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1;
if (row < 0 || row >= screen_Rows
- || col < 0 || col >= screen_Columns)
+ || col < 0 || col >= screen_Columns) {
c = -1;
- else
+ } else {
c = ScreenAttrs[LineOffset[row] + col];
+ }
rettv->vval.v_number = c;
}
@@ -14704,17 +14003,15 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- int row;
- int col;
int off;
int c;
- row = get_tv_number_chk(&argvars[0], NULL) - 1;
- col = get_tv_number_chk(&argvars[1], NULL) - 1;
+ const int row = tv_get_number_chk(&argvars[0], NULL) - 1;
+ const int col = tv_get_number_chk(&argvars[1], NULL) - 1;
if (row < 0 || row >= screen_Rows
- || col < 0 || col >= screen_Columns)
+ || col < 0 || col >= screen_Columns) {
c = -1;
- else {
+ } else {
off = LineOffset[row] + col;
if (enc_utf8 && ScreenLinesUC[off] != 0)
c = ScreenLinesUC[off];
@@ -14759,19 +14056,21 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int locally = 1;
int thisblock = 0;
- int error = FALSE;
+ bool error = false;
rettv->vval.v_number = 1; /* default: FAIL */
- char_u *name = get_tv_string_chk(&argvars[0]);
+ const char *const name = tv_get_string_chk(&argvars[0]);
if (argvars[1].v_type != VAR_UNKNOWN) {
- locally = get_tv_number_chk(&argvars[1], &error) == 0;
- if (!error && argvars[2].v_type != VAR_UNKNOWN)
- thisblock = get_tv_number_chk(&argvars[2], &error) != 0;
+ locally = tv_get_number_chk(&argvars[1], &error) == 0;
+ if (!error && argvars[2].v_type != VAR_UNKNOWN) {
+ thisblock = tv_get_number_chk(&argvars[2], &error) != 0;
+ }
}
- if (!error && name != NULL)
- rettv->vval.v_number = find_decl(name, STRLEN(name), locally,
+ if (!error && name != NULL) {
+ rettv->vval.v_number = find_decl((char_u *)name, strlen(name), locally,
thisblock, SEARCH_KEEP) == FAIL;
+ }
}
/*
@@ -14779,65 +14078,70 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static int searchpair_cmn(typval_T *argvars, pos_T *match_pos)
{
- char_u *spat, *mpat, *epat;
- char_u *skip;
bool save_p_ws = p_ws;
int dir;
int flags = 0;
- char_u nbuf1[NUMBUFLEN];
- char_u nbuf2[NUMBUFLEN];
- char_u nbuf3[NUMBUFLEN];
- int retval = 0; /* default: FAIL */
+ int retval = 0; // default: FAIL
long lnum_stop = 0;
long time_limit = 0;
- /* Get the three pattern arguments: start, middle, end. */
- spat = get_tv_string_chk(&argvars[0]);
- mpat = get_tv_string_buf_chk(&argvars[1], nbuf1);
- epat = get_tv_string_buf_chk(&argvars[2], nbuf2);
- if (spat == NULL || mpat == NULL || epat == NULL)
- goto theend; /* type error */
+ // Get the three pattern arguments: start, middle, end.
+ char nbuf1[NUMBUFLEN];
+ char nbuf2[NUMBUFLEN];
+ char nbuf3[NUMBUFLEN];
+ const char *spat = tv_get_string_chk(&argvars[0]);
+ const char *mpat = tv_get_string_buf_chk(&argvars[1], nbuf1);
+ const char *epat = tv_get_string_buf_chk(&argvars[2], nbuf2);
+ if (spat == NULL || mpat == NULL || epat == NULL) {
+ goto theend; // Type error.
+ }
- /* Handle the optional fourth argument: flags */
- dir = get_search_arg(&argvars[3], &flags); /* may set p_ws */
- if (dir == 0)
+ // Handle the optional fourth argument: flags.
+ dir = get_search_arg(&argvars[3], &flags); // may set p_ws.
+ if (dir == 0) {
goto theend;
+ }
- /* Don't accept SP_END or SP_SUBPAT.
- * Only one of the SP_NOMOVE or SP_SETPCMARK flags can be set.
- */
+ // Don't accept SP_END or SP_SUBPAT.
+ // Only one of the SP_NOMOVE or SP_SETPCMARK flags can be set.
if ((flags & (SP_END | SP_SUBPAT)) != 0
|| ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) {
- EMSG2(_(e_invarg2), get_tv_string(&argvars[3]));
+ EMSG2(_(e_invarg2), tv_get_string(&argvars[3]));
goto theend;
}
- /* Using 'r' implies 'W', otherwise it doesn't work. */
- if (flags & SP_REPEAT)
+ // Using 'r' implies 'W', otherwise it doesn't work.
+ if (flags & SP_REPEAT) {
p_ws = false;
+ }
- /* Optional fifth argument: skip expression */
+ // Optional fifth argument: skip expression.
+ const char *skip;
if (argvars[3].v_type == VAR_UNKNOWN
- || argvars[4].v_type == VAR_UNKNOWN)
- skip = (char_u *)"";
- else {
- skip = get_tv_string_buf_chk(&argvars[4], nbuf3);
+ || argvars[4].v_type == VAR_UNKNOWN) {
+ skip = "";
+ } else {
+ skip = tv_get_string_buf_chk(&argvars[4], nbuf3);
+ if (skip == NULL) {
+ goto theend; // Type error.
+ }
if (argvars[5].v_type != VAR_UNKNOWN) {
- lnum_stop = get_tv_number_chk(&argvars[5], NULL);
- if (lnum_stop < 0)
+ lnum_stop = tv_get_number_chk(&argvars[5], NULL);
+ if (lnum_stop < 0) {
goto theend;
+ }
if (argvars[6].v_type != VAR_UNKNOWN) {
- time_limit = get_tv_number_chk(&argvars[6], NULL);
- if (time_limit < 0)
+ time_limit = tv_get_number_chk(&argvars[6], NULL);
+ if (time_limit < 0) {
goto theend;
+ }
}
}
}
- if (skip == NULL)
- goto theend; /* type error */
- retval = do_searchpair(spat, mpat, epat, dir, skip, flags,
- match_pos, lnum_stop, time_limit);
+ retval = do_searchpair(
+ (char_u *)spat, (char_u *)mpat, (char_u *)epat, dir, (char_u *)skip,
+ flags, match_pos, lnum_stop, time_limit);
theend:
p_ws = save_p_ws;
@@ -14862,15 +14166,15 @@ static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int lnum = 0;
int col = 0;
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
if (searchpair_cmn(argvars, &match_pos) > 0) {
lnum = match_pos.lnum;
col = match_pos.col;
}
- list_append_number(rettv->vval.v_list, (varnumber_T)lnum);
- list_append_number(rettv->vval.v_list, (varnumber_T)col);
+ tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum);
+ tv_list_append_number(rettv->vval.v_list, (varnumber_T)col);
}
/*
@@ -14902,9 +14206,10 @@ do_searchpair (
int n;
int r;
int nest = 1;
- int err;
int options = SEARCH_KEEP;
proftime_T tm;
+ size_t pat2_len;
+ size_t pat3_len;
/* Make 'cpoptions' empty, the 'l' flag should not be used here. */
save_cpo = p_cpo;
@@ -14913,18 +14218,22 @@ do_searchpair (
/* Set the time limit, if there is one. */
tm = profile_setlimit(time_limit);
- /* Make two search patterns: start/end (pat2, for in nested pairs) and
- * start/middle/end (pat3, for the top pair). */
- pat2 = xmalloc(STRLEN(spat) + STRLEN(epat) + 15);
- pat3 = xmalloc(STRLEN(spat) + STRLEN(mpat) + STRLEN(epat) + 23);
- sprintf((char *)pat2, "\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat);
- if (*mpat == NUL)
+ // Make two search patterns: start/end (pat2, for in nested pairs) and
+ // start/middle/end (pat3, for the top pair).
+ pat2_len = STRLEN(spat) + STRLEN(epat) + 17;
+ pat2 = xmalloc(pat2_len);
+ pat3_len = STRLEN(spat) + STRLEN(mpat) + STRLEN(epat) + 25;
+ pat3 = xmalloc(pat3_len);
+ snprintf((char *)pat2, pat2_len, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat);
+ if (*mpat == NUL) {
STRCPY(pat3, pat2);
- else
- sprintf((char *)pat3, "\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)",
- spat, epat, mpat);
- if (flags & SP_START)
+ } else {
+ snprintf((char *)pat3, pat3_len,
+ "\\m\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat, mpat);
+ }
+ if (flags & SP_START) {
options |= SEARCH_START;
+ }
save_cursor = curwin->w_cursor;
pos = curwin->w_cursor;
@@ -14958,7 +14267,8 @@ do_searchpair (
if (*skip != NUL) {
save_pos = curwin->w_cursor;
curwin->w_cursor = pos;
- r = eval_to_bool(skip, &err, NULL, FALSE);
+ bool err;
+ r = eval_to_bool(skip, &err, NULL, false);
curwin->w_cursor = save_pos;
if (err) {
/* Evaluating {skip} caused an error, break here. */
@@ -15029,7 +14339,7 @@ static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int n;
int flags = 0;
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
n = search_cmn(argvars, &match_pos, &flags);
if (n > 0) {
@@ -15037,10 +14347,11 @@ static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
col = match_pos.col;
}
- list_append_number(rettv->vval.v_list, (varnumber_T)lnum);
- list_append_number(rettv->vval.v_list, (varnumber_T)col);
- if (flags & SP_SUBPAT)
- list_append_number(rettv->vval.v_list, (varnumber_T)n);
+ tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum);
+ tv_list_append_number(rettv->vval.v_list, (varnumber_T)col);
+ if (flags & SP_SUBPAT) {
+ tv_list_append_number(rettv->vval.v_list, (varnumber_T)n);
+ }
}
/// "serverlist()" function
@@ -15050,13 +14361,13 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char **addrs = server_address_list(&n);
// Copy addrs into a linked list.
- list_T *l = rettv_list_alloc(rettv);
+ list_T *l = tv_list_alloc_ret(rettv);
for (size_t i = 0; i < n; i++) {
- listitem_T *li = listitem_alloc();
+ listitem_T *li = tv_list_item_alloc();
li->li_tv.v_type = VAR_STRING;
li->li_tv.v_lock = 0;
- li->li_tv.vval.v_string = (char_u *) addrs[i];
- list_append(l, li);
+ li->li_tv.vval.v_string = (char_u *)addrs[i];
+ tv_list_append(l, li);
}
xfree(addrs);
}
@@ -15071,22 +14382,39 @@ static void f_serverstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
+ char *address;
// If the user supplied an address, use it, otherwise use a temp.
if (argvars[0].v_type != VAR_UNKNOWN) {
if (argvars[0].v_type != VAR_STRING) {
EMSG(_(e_invarg));
return;
} else {
- rettv->vval.v_string = vim_strsave(get_tv_string(argvars));
+ address = xstrdup(tv_get_string(argvars));
}
} else {
- rettv->vval.v_string = (char_u *)server_address_new();
+ address = server_address_new();
}
- int result = server_start((char *) rettv->vval.v_string);
+ int result = server_start(address);
+ xfree(address);
+
if (result != 0) {
- EMSG2("Failed to start server: %s", uv_strerror(result));
+ EMSG2("Failed to start server: %s",
+ result > 0 ? "Unknown system error" : uv_strerror(result));
+ return;
+ }
+
+ // Since it's possible server_start adjusted the given {address} (e.g.,
+ // "localhost:" will now have a port), return the final value to the user.
+ size_t n;
+ char **addrs = server_address_list(&n);
+ rettv->vval.v_string = (char_u *)addrs[n - 1];
+
+ n--;
+ for (size_t i = 0; i < n; i++) {
+ xfree(addrs[i]);
}
+ xfree(addrs);
}
/// "serverstop()" function
@@ -15096,7 +14424,7 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- if (argvars[0].v_type == VAR_UNKNOWN || argvars[0].v_type != VAR_STRING) {
+ if (argvars[0].v_type != VAR_STRING) {
EMSG(_(e_invarg));
return;
}
@@ -15111,44 +14439,43 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- buf_T *buf;
- char_u *varname, *bufvarname;
- typval_T *varp;
- char_u nbuf[NUMBUFLEN];
-
- if (check_restricted() || check_secure())
+ if (check_restricted()
+ || check_secure()
+ || !tv_check_str_or_nr(&argvars[0])) {
return;
- (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */
- varname = get_tv_string_chk(&argvars[1]);
- buf = get_buf_tv(&argvars[0], FALSE);
- varp = &argvars[2];
+ }
+ const char *varname = tv_get_string_chk(&argvars[1]);
+ buf_T *const buf = get_buf_tv(&argvars[0], false);
+ typval_T *varp = &argvars[2];
- if (buf != NULL && varname != NULL && varp != NULL) {
+ if (buf != NULL && varname != NULL) {
if (*varname == '&') {
long numval;
- char_u *strval;
- int error = false;
- aco_save_T aco;
+ bool error = false;
+ aco_save_T aco;
// set curbuf to be our buf, temporarily
aucmd_prepbuf(&aco, buf);
- ++varname;
- numval = get_tv_number_chk(varp, &error);
- strval = get_tv_string_buf_chk(varp, nbuf);
- if (!error && strval != NULL)
+ varname++;
+ numval = tv_get_number_chk(varp, &error);
+ char nbuf[NUMBUFLEN];
+ const char *const strval = tv_get_string_buf_chk(varp, nbuf);
+ if (!error && strval != NULL) {
set_option_value(varname, numval, strval, OPT_LOCAL);
+ }
// reset notion of buffer
aucmd_restbuf(&aco);
} else {
buf_T *save_curbuf = curbuf;
- bufvarname = xmalloc(STRLEN(varname) + 3);
+ const size_t varname_len = STRLEN(varname);
+ char *const bufvarname = xmalloc(varname_len + 3);
curbuf = buf;
- STRCPY(bufvarname, "b:");
- STRCPY(bufvarname + 2, varname);
- set_var(bufvarname, varp, TRUE);
+ memcpy(bufvarname, "b:", 2);
+ memcpy(bufvarname + 2, varname, varname_len + 1);
+ set_var(bufvarname, varname_len + 2, varp, true);
xfree(bufvarname);
curbuf = save_curbuf;
}
@@ -15159,7 +14486,6 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
dict_T *d;
dictitem_T *di;
- char_u *csearch;
if (argvars[0].v_type != VAR_DICT) {
EMSG(_(e_dictreq));
@@ -15167,7 +14493,7 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if ((d = argvars[0].vval.v_dict) != NULL) {
- csearch = get_dict_string(d, "char", false);
+ char_u *const csearch = (char_u *)tv_dict_get_string(d, "char", false);
if (csearch != NULL) {
if (enc_utf8) {
int pcc[MAX_MCO];
@@ -15179,13 +14505,15 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
csearch, MB_PTR2LEN(csearch));
}
- di = dict_find(d, (char_u *)"forward", -1);
- if (di != NULL)
- set_csearch_direction(get_tv_number(&di->di_tv) ? FORWARD : BACKWARD);
+ di = tv_dict_find(d, S_LEN("forward"));
+ if (di != NULL) {
+ set_csearch_direction(tv_get_number(&di->di_tv) ? FORWARD : BACKWARD);
+ }
- di = dict_find(d, (char_u *)"until", -1);
- if (di != NULL)
- set_csearch_until(!!get_tv_number(&di->di_tv));
+ di = tv_dict_find(d, S_LEN("until"));
+ if (di != NULL) {
+ set_csearch_until(!!tv_get_number(&di->di_tv));
+ }
}
}
@@ -15194,10 +14522,11 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_setcmdpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- int pos = (int)get_tv_number(&argvars[0]) - 1;
+ const int pos = (int)tv_get_number(&argvars[0]) - 1;
- if (pos >= 0)
+ if (pos >= 0) {
rettv->vval.v_number = set_cmdline_pos(pos);
+ }
}
@@ -15206,17 +14535,17 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = 0;
- char_u *fname = get_tv_string_chk(&argvars[0]);
+ const char *const fname = tv_get_string_chk(&argvars[0]);
if (fname == NULL) {
return;
}
- char_u modebuf[NUMBUFLEN];
- char_u *mode_str = get_tv_string_buf_chk(&argvars[1], modebuf);
+ char modebuf[NUMBUFLEN];
+ const char *const mode_str = tv_get_string_buf_chk(&argvars[1], modebuf);
if (mode_str == NULL) {
return;
}
- if (STRLEN(mode_str) != 9) {
+ if (strlen(mode_str) != 9) {
EMSG2(_(e_invarg2), mode_str);
return;
}
@@ -15237,27 +14566,28 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- linenr_T lnum;
- char_u *line = NULL;
list_T *l = NULL;
listitem_T *li = NULL;
long added = 0;
linenr_T lcount = curbuf->b_ml.ml_line_count;
- lnum = get_tv_lnum(&argvars[0]);
+ linenr_T lnum = tv_get_lnum(&argvars[0]);
+ const char *line = NULL;
if (argvars[1].v_type == VAR_LIST) {
l = argvars[1].vval.v_list;
li = l->lv_first;
- } else
- line = get_tv_string_chk(&argvars[1]);
+ } else {
+ line = tv_get_string_chk(&argvars[1]);
+ }
/* default result is zero == OK */
for (;; ) {
if (l != NULL) {
- /* list argument, get next string */
- if (li == NULL)
+ // List argument, get next string.
+ if (li == NULL) {
break;
- line = get_tv_string_chk(&li->li_tv);
+ }
+ line = tv_get_string_chk(&li->li_tv);
li = li->li_next;
}
@@ -15273,18 +14603,20 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (lnum <= curbuf->b_ml.ml_line_count) {
- /* existing line, replace it */
- if (u_savesub(lnum) == OK && ml_replace(lnum, line, TRUE) == OK) {
+ // Existing line, replace it.
+ if (u_savesub(lnum) == OK
+ && ml_replace(lnum, (char_u *)line, true) == OK) {
changed_bytes(lnum, 0);
if (lnum == curwin->w_cursor.lnum)
check_cursor_col();
rettv->vval.v_number = 0; /* OK */
}
} else if (added > 0 || u_save(lnum - 1, lnum) == OK) {
- /* lnum is one past the last line, append the line */
- ++added;
- if (ml_append(lnum - 1, line, (colnr_T)0, FALSE) == OK)
- rettv->vval.v_number = 0; /* OK */
+ // lnum is one past the last line, append the line.
+ added++;
+ if (ml_append(lnum - 1, (char_u *)line, 0, false) == OK) {
+ rettv->vval.v_number = 0; // OK
+ }
}
if (l == NULL) /* only one string argument */
@@ -15299,7 +14631,7 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// Create quickfix/location list from VimL values
///
/// Used by `setqflist()` and `setloclist()` functions. Accepts invalid
-/// list_arg, action_arg and title_arg arguments in which case errors out,
+/// list_arg, action_arg and what_arg arguments in which case errors out,
/// including VAR_UNKNOWN parameters.
///
/// @param[in,out] wp Window to create location list for. May be NULL in
@@ -15313,9 +14645,10 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
FUNC_ATTR_NONNULL_ARG(2, 3)
{
static char *e_invact = N_("E927: Invalid action: '%s'");
- char_u *title = NULL;
+ const char *title = NULL;
int action = ' ';
rettv->vval.v_number = -1;
+ dict_T *d = NULL;
typval_T *list_arg = &args[0];
if (list_arg->v_type != VAR_LIST) {
@@ -15331,7 +14664,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
EMSG(_(e_stringreq));
return;
}
- char_u *act = get_tv_string_chk(action_arg);
+ const char *const act = tv_get_string_chk(action_arg);
if ((*act == 'a' || *act == 'r' || *act == ' ') && act[1] == NUL) {
action = *act;
} else {
@@ -15343,20 +14676,26 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
if (title_arg->v_type == VAR_UNKNOWN) {
// Option argument was not given.
goto skip_args;
- }
- title = get_tv_string_chk(title_arg);
- if (!title) {
- // Type error. Error already printed by get_tv_string_chk().
+ } else if (title_arg->v_type == VAR_STRING) {
+ title = tv_get_string_chk(title_arg);
+ if (!title) {
+ // Type error. Error already printed by tv_get_string_chk().
+ return;
+ }
+ } else if (title_arg->v_type == VAR_DICT) {
+ d = title_arg->vval.v_dict;
+ } else {
+ emsgf(_(e_dictreq));
return;
}
skip_args:
if (!title) {
- title = (char_u*)(wp ? "setloclist()" : "setqflist()");
+ title = (wp ? "setloclist()" : "setqflist()");
}
list_T *l = list_arg->vval.v_list;
- if (l && set_errorlist(wp, l, action, title) == OK) {
+ if (l && set_errorlist(wp, l, action, (char_u *)title, d) == OK) {
rettv->vval.v_number = 0;
}
}
@@ -15402,11 +14741,11 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
EMSG(_(e_invarg));
return;
}
- if (!(dict_find(d, (char_u *)"group", -1) != NULL
- && (dict_find(d, (char_u *)"pattern", -1) != NULL
- || dict_find(d, (char_u *)"pos1", -1) != NULL)
- && dict_find(d, (char_u *)"priority", -1) != NULL
- && dict_find(d, (char_u *)"id", -1) != NULL)) {
+ if (!(tv_dict_find(d, S_LEN("group")) != NULL
+ && (tv_dict_find(d, S_LEN("pattern")) != NULL
+ || tv_dict_find(d, S_LEN("pos1")) != NULL)
+ && tv_dict_find(d, S_LEN("priority")) != NULL
+ && tv_dict_find(d, S_LEN("id")) != NULL)) {
EMSG(_(e_invarg));
return;
}
@@ -15415,29 +14754,31 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
clear_matches(curwin);
li = l->lv_first;
+ bool match_add_failed = false;
while (li != NULL) {
int i = 0;
- char_u buf[5];
- dictitem_T *di;
d = li->li_tv.vval.v_dict;
- if (dict_find(d, (char_u *)"pattern", -1) == NULL) {
+ dictitem_T *const di = tv_dict_find(d, S_LEN("pattern"));
+ if (di == NULL) {
if (s == NULL) {
- s = list_alloc();
+ s = tv_list_alloc();
if (s == NULL) {
return;
}
}
// match from matchaddpos()
- for (i = 1; i < 9; ++i) {
- snprintf((char *)buf, sizeof(buf), (char *)"pos%d", i);
- if ((di = dict_find(d, (char_u *)buf, -1)) != NULL) {
- if (di->di_tv.v_type != VAR_LIST) {
+ for (i = 1; i < 9; i++) {
+ char buf[5];
+ snprintf(buf, sizeof(buf), "pos%d", i);
+ dictitem_T *const pos_di = tv_dict_find(d, buf, -1);
+ if (pos_di != NULL) {
+ if (pos_di->di_tv.v_type != VAR_LIST) {
return;
}
- list_append_tv(s, &di->di_tv);
+ tv_list_append_tv(s, &pos_di->di_tv);
s->lv_refcount++;
} else {
break;
@@ -15445,25 +14786,39 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- char_u *group = get_dict_string(d, "group", false);
- int priority = get_dict_number(d, "priority");
- int id = get_dict_number(d, "id");
- char_u *conceal = dict_find(d, (char_u *)"conceal", -1) != NULL
- ? get_dict_string(d, "conceal",
- false)
- : NULL;
+ // Note: there are three number buffers involved:
+ // - group_buf below.
+ // - numbuf in tv_dict_get_string().
+ // - mybuf in tv_get_string().
+ //
+ // If you change this code make sure that buffers will not get
+ // accidentally reused.
+ char group_buf[NUMBUFLEN];
+ const char *const group = tv_dict_get_string_buf(d, "group", group_buf);
+ const int priority = (int)tv_dict_get_number(d, "priority");
+ const int id = (int)tv_dict_get_number(d, "id");
+ dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal"));
+ const char *const conceal = (conceal_di != NULL
+ ? tv_get_string(&conceal_di->di_tv)
+ : NULL);
if (i == 0) {
- match_add(curwin, group,
- get_dict_string(d, "pattern", false),
- priority, id, NULL, conceal);
+ if (match_add(curwin, group,
+ tv_dict_get_string(d, "pattern", false),
+ priority, id, NULL, conceal) != id) {
+ match_add_failed = true;
+ }
} else {
- match_add(curwin, group, NULL, priority, id, s, conceal);
- list_unref(s);
+ if (match_add(curwin, group, NULL, priority, id, s, conceal) != id) {
+ match_add_failed = true;
+ }
+ tv_list_unref(s);
s = NULL;
}
li = li->li_next;
}
- rettv->vval.v_number = 0;
+ if (!match_add_failed) {
+ rettv->vval.v_number = 0;
+ }
}
}
@@ -15474,11 +14829,10 @@ static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
pos_T pos;
int fnum;
- char_u *name;
colnr_T curswant = -1;
rettv->vval.v_number = -1;
- name = get_tv_string_chk(argvars);
+ const char *const name = tv_get_string_chk(argvars);
if (name != NULL) {
if (list2fpos(&argvars[1], &pos, &fnum, &curswant) == OK) {
if (--pos.col < 0) {
@@ -15499,7 +14853,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
} else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) {
// set mark
- if (setmark_pos(name[1], &pos, fnum) == OK) {
+ if (setmark_pos((uint8_t)name[1], &pos, fnum) == OK) {
rettv->vval.v_number = 0;
}
} else {
@@ -15523,8 +14877,6 @@ static void f_setqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int regname;
- char_u *strregname;
- char_u *stropt;
bool append = false;
MotionType yank_type;
long block_len;
@@ -15532,39 +14884,52 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
block_len = -1;
yank_type = kMTUnknown;
- strregname = get_tv_string_chk(argvars);
- rettv->vval.v_number = 1; /* FAIL is default */
+ rettv->vval.v_number = 1; // FAIL is default.
- if (strregname == NULL)
- return; /* type error; errmsg already given */
- regname = *strregname;
- if (regname == 0 || regname == '@')
+ const char *const strregname = tv_get_string_chk(argvars);
+ if (strregname == NULL) {
+ return; // Type error; errmsg already given.
+ }
+ regname = (uint8_t)(*strregname);
+ if (regname == 0 || regname == '@') {
regname = '"';
+ }
+ bool set_unnamed = false;
if (argvars[2].v_type != VAR_UNKNOWN) {
- stropt = get_tv_string_chk(&argvars[2]);
- if (stropt == NULL)
- return; /* type error */
- for (; *stropt != NUL; ++stropt)
+ const char *stropt = tv_get_string_chk(&argvars[2]);
+ if (stropt == NULL) {
+ return; // Type error.
+ }
+ for (; *stropt != NUL; stropt++) {
switch (*stropt) {
- case 'a': case 'A': // append
- append = true;
- break;
- case 'v': case 'c': // character-wise selection
- yank_type = kMTCharWise;
- break;
- case 'V': case 'l': // line-wise selection
- yank_type = kMTLineWise;
- break;
- case 'b': case Ctrl_V: // block-wise selection
- yank_type = kMTBlockWise;
- if (ascii_isdigit(stropt[1])) {
- ++stropt;
- block_len = getdigits_long(&stropt) - 1;
- --stropt;
+ case 'a': case 'A': { // append
+ append = true;
+ break;
+ }
+ case 'v': case 'c': { // character-wise selection
+ yank_type = kMTCharWise;
+ break;
+ }
+ case 'V': case 'l': { // line-wise selection
+ yank_type = kMTLineWise;
+ break;
+ }
+ case 'b': case Ctrl_V: { // block-wise selection
+ yank_type = kMTBlockWise;
+ if (ascii_isdigit(stropt[1])) {
+ stropt++;
+ block_len = getdigits_long((char_u **)&stropt) - 1;
+ stropt--;
+ }
+ break;
+ }
+ case 'u': case '"': { // unnamed register
+ set_unnamed = true;
+ break;
}
- break;
}
+ }
}
if (argvars[1].v_type == VAR_LIST) {
@@ -15574,45 +14939,52 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// First half: use for pointers to result lines; second half: use for
// pointers to allocated copies.
- char_u **lstval = xmalloc(sizeof(char_u *) * ((len + 1) * 2));
- char_u **curval = lstval;
- char_u **allocval = lstval + len + 2;
- char_u **curallocval = allocval;
+ char **lstval = xmalloc(sizeof(char *) * ((len + 1) * 2));
+ const char **curval = (const char **)lstval;
+ char **allocval = lstval + len + 2;
+ char **curallocval = allocval;
- char_u buf[NUMBUFLEN];
for (listitem_T *li = ll == NULL ? NULL : ll->lv_first;
li != NULL;
li = li->li_next) {
- char_u *strval = get_tv_string_buf_chk(&li->li_tv, buf);
- if (strval == NULL) {
+ char buf[NUMBUFLEN];
+ *curval = tv_get_string_buf_chk(&li->li_tv, buf);
+ if (*curval == NULL) {
goto free_lstval;
}
- if (strval == buf) {
+ if (*curval == buf) {
// Need to make a copy,
- // next get_tv_string_buf_chk() will overwrite the string.
- strval = vim_strsave(buf);
- *curallocval++ = strval;
+ // next tv_get_string_buf_chk() will overwrite the string.
+ *curallocval = xstrdup(*curval);
+ *curval = *curallocval;
+ curallocval++;
}
- *curval++ = strval;
+ curval++;
}
*curval++ = NULL;
- write_reg_contents_lst(regname, lstval, STRLEN(lstval),
- append, yank_type, block_len);
+ write_reg_contents_lst(regname, (char_u **)lstval, append, yank_type,
+ block_len);
free_lstval:
- while (curallocval > allocval)
- xfree(*--curallocval);
+ while (curallocval > allocval) {
+ xfree(*--curallocval);
+ }
xfree(lstval);
} else {
- char_u *strval = get_tv_string_chk(&argvars[1]);
+ const char *strval = tv_get_string_chk(&argvars[1]);
if (strval == NULL) {
return;
}
- write_reg_contents_ex(regname, strval, STRLEN(strval),
+ write_reg_contents_ex(regname, (const char_u *)strval, STRLEN(strval),
append, yank_type, block_len);
}
rettv->vval.v_number = 0;
+
+ if (set_unnamed) {
+ // Discard the result. We already handle the error case.
+ if (op_register_set_previous(regname)) { }
+ }
}
/*
@@ -15620,35 +14992,31 @@ free_lstval:
*/
static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- tabpage_T *save_curtab;
- tabpage_T *tp;
- char_u *varname, *tabvarname;
- typval_T *varp;
-
rettv->vval.v_number = 0;
- if (check_restricted() || check_secure())
+ if (check_restricted() || check_secure()) {
return;
+ }
- tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL));
- varname = get_tv_string_chk(&argvars[1]);
- varp = &argvars[2];
+ tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));
+ const char *const varname = tv_get_string_chk(&argvars[1]);
+ typval_T *const varp = &argvars[2];
- if (varname != NULL && varp != NULL
- && tp != NULL
- ) {
- save_curtab = curtab;
- goto_tabpage_tp(tp, FALSE, FALSE);
+ if (varname != NULL && tp != NULL) {
+ tabpage_T *const save_curtab = curtab;
+ goto_tabpage_tp(tp, false, false);
- tabvarname = xmalloc(STRLEN(varname) + 3);
- STRCPY(tabvarname, "t:");
- STRCPY(tabvarname + 2, varname);
- set_var(tabvarname, varp, TRUE);
+ const size_t varname_len = strlen(varname);
+ char *const tabvarname = xmalloc(varname_len + 3);
+ memcpy(tabvarname, "t:", 2);
+ memcpy(tabvarname + 2, varname, varname_len + 1);
+ set_var(tabvarname, varname_len + 2, varp, true);
xfree(tabvarname);
- /* Restore current tabpage */
- if (valid_tabpage(save_curtab))
- goto_tabpage_tp(save_curtab, FALSE, FALSE);
+ // Restore current tabpage.
+ if (valid_tabpage(save_curtab)) {
+ goto_tabpage_tp(save_curtab, false, false);
+ }
}
}
@@ -15674,45 +15042,43 @@ static void f_setwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void setwinvar(typval_T *argvars, typval_T *rettv, int off)
{
- win_T *win;
- win_T *save_curwin;
- tabpage_T *save_curtab;
- char_u *varname, *winvarname;
- typval_T *varp;
- char_u nbuf[NUMBUFLEN];
- tabpage_T *tp = NULL;
-
- if (check_restricted() || check_secure())
+ if (check_restricted() || check_secure()) {
return;
+ }
- if (off == 1)
- tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL));
- else
+ tabpage_T *tp = NULL;
+ if (off == 1) {
+ tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));
+ } else {
tp = curtab;
- win = find_win_by_nr(&argvars[off], tp);
- varname = get_tv_string_chk(&argvars[off + 1]);
- varp = &argvars[off + 2];
+ }
+ win_T *const win = find_win_by_nr(&argvars[off], tp);
+ const char *varname = tv_get_string_chk(&argvars[off + 1]);
+ typval_T *varp = &argvars[off + 2];
if (win != NULL && varname != NULL && varp != NULL) {
+ win_T *save_curwin;
+ tabpage_T *save_curtab;
bool need_switch_win = tp != curtab || win != curwin;
if (!need_switch_win
|| switch_win(&save_curwin, &save_curtab, win, tp, true) == OK) {
if (*varname == '&') {
long numval;
- char_u *strval;
- int error = false;
+ bool error = false;
- ++varname;
- numval = get_tv_number_chk(varp, &error);
- strval = get_tv_string_buf_chk(varp, nbuf);
+ varname++;
+ numval = tv_get_number_chk(varp, &error);
+ char nbuf[NUMBUFLEN];
+ const char *const strval = tv_get_string_buf_chk(varp, nbuf);
if (!error && strval != NULL) {
set_option_value(varname, numval, strval, OPT_LOCAL);
}
} else {
- winvarname = xmalloc(STRLEN(varname) + 3);
- STRCPY(winvarname, "w:");
- STRCPY(winvarname + 2, varname);
- set_var(winvarname, varp, true);
+ const size_t varname_len = strlen(varname);
+ char *const winvarname = xmalloc(varname_len + 3);
+ memcpy(winvarname, "w:", 2);
+ memcpy(winvarname + 2, varname, varname_len + 1);
+ set_var(winvarname, varname_len + 2, varp, true);
xfree(winvarname);
}
}
@@ -15725,11 +15091,11 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off)
/// f_sha256 - sha256({string}) function
static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *p = get_tv_string(&argvars[0]);
- const char_u *hash = sha256_bytes(p, (int) STRLEN(p) , NULL, 0);
+ const char *p = tv_get_string(&argvars[0]);
+ const char *hash = sha256_bytes((const uint8_t *)p, strlen(p) , NULL, 0);
// make a copy of the hash (sha256_bytes returns a static buffer)
- rettv->vval.v_string = (char_u *) xstrdup((char *) hash);
+ rettv->vval.v_string = (char_u *)xstrdup(hash);
rettv->v_type = VAR_STRING;
}
@@ -15739,7 +15105,8 @@ static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_shellescape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_string = vim_strsave_shellescape(
- get_tv_string(&argvars[0]), non_zero_arg(&argvars[1]), true);
+ (const char_u *)tv_get_string(&argvars[0]), non_zero_arg(&argvars[1]),
+ true);
rettv->v_type = VAR_STRING;
}
@@ -15756,14 +15123,61 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_simplify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *p;
-
- p = get_tv_string(&argvars[0]);
- rettv->vval.v_string = vim_strsave(p);
- simplify_filename(rettv->vval.v_string); /* simplify in place */
+ const char *const p = tv_get_string(&argvars[0]);
+ rettv->vval.v_string = (char_u *)xstrdup(p);
+ simplify_filename(rettv->vval.v_string); // Simplify in place.
rettv->v_type = VAR_STRING;
}
+/// "sockconnect()" function
+static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_STRING) {
+ EMSG(_(e_invarg));
+ return;
+ }
+ if (argvars[2].v_type != VAR_DICT && argvars[2].v_type != VAR_UNKNOWN) {
+ // Wrong argument types
+ EMSG2(_(e_invarg2), "expected dictionary");
+ return;
+ }
+
+ const char *mode = tv_get_string(&argvars[0]);
+ const char *address = tv_get_string(&argvars[1]);
+
+ bool tcp;
+ if (strcmp(mode, "tcp") == 0) {
+ tcp = true;
+ } else if (strcmp(mode, "pipe") == 0) {
+ tcp = false;
+ } else {
+ EMSG2(_(e_invarg2), "invalid mode");
+ return;
+ }
+
+ bool rpc = false;
+ if (argvars[2].v_type == VAR_DICT) {
+ dict_T *opts = argvars[2].vval.v_dict;
+ rpc = tv_dict_get_number(opts, "rpc") != 0;
+ }
+
+ if (!rpc) {
+ EMSG2(_(e_invarg2), "rpc option must be true");
+ return;
+ }
+
+ const char *error = NULL;
+ eval_format_source_name_line((char *)IObuff, sizeof(IObuff));
+ uint64_t id = channel_connect(tcp, address, 50, (char *)IObuff, &error);
+
+ if (error) {
+ EMSG2(_("connection failed: %s"), error);
+ }
+
+ rettv->vval.v_number = (varnumber_T)id;
+ rettv->v_type = VAR_NUMBER;
+}
+
/// struct used in the array that's given to qsort()
typedef struct {
listitem_T *item;
@@ -15776,10 +15190,10 @@ typedef struct {
bool item_compare_numeric;
bool item_compare_numbers;
bool item_compare_float;
- char_u *item_compare_func;
+ const char *item_compare_func;
partial_T *item_compare_partial;
dict_T *item_compare_selfdict;
- int item_compare_func_err;
+ bool item_compare_func_err;
} sortinfo_T;
static sortinfo_T *sortinfo = NULL;
@@ -15790,58 +15204,61 @@ static sortinfo_T *sortinfo = NULL;
*/
static int item_compare(const void *s1, const void *s2, bool keep_zero)
{
- sortItem_T *si1, *si2;
- char_u *p1;
- char_u *p2;
- char_u *tofree1 = NULL;
- char_u *tofree2 = NULL;
- int res;
+ sortItem_T *const si1 = (sortItem_T *)s1;
+ sortItem_T *const si2 = (sortItem_T *)s2;
- si1 = (sortItem_T *)s1;
- si2 = (sortItem_T *)s2;
- typval_T *tv1 = &si1->item->li_tv;
- typval_T *tv2 = &si2->item->li_tv;
+ typval_T *const tv1 = &si1->item->li_tv;
+ typval_T *const tv2 = &si2->item->li_tv;
+
+ int res;
if (sortinfo->item_compare_numbers) {
- long v1 = get_tv_number(tv1);
- long v2 = get_tv_number(tv2);
+ const varnumber_T v1 = tv_get_number(tv1);
+ const varnumber_T v2 = tv_get_number(tv2);
- return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
+ res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
+ goto item_compare_end;
}
if (sortinfo->item_compare_float) {
- float_T v1 = get_tv_float(tv1);
- float_T v2 = get_tv_float(tv2);
+ const float_T v1 = tv_get_float(tv1);
+ const float_T v2 = tv_get_float(tv2);
- return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
+ res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
+ goto item_compare_end;
}
+ char *tofree1 = NULL;
+ char *tofree2 = NULL;
+ char *p1;
+ char *p2;
+
// encode_tv2string() puts quotes around a string and allocates memory. Don't
// do that for string variables. Use a single quote when comparing with
// a non-string to do what the docs promise.
if (tv1->v_type == VAR_STRING) {
if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) {
- p1 = (char_u *)"'";
+ p1 = "'";
} else {
- p1 = tv1->vval.v_string;
+ p1 = (char *)tv1->vval.v_string;
}
} else {
- tofree1 = p1 = (char_u *) encode_tv2string(tv1, NULL);
+ tofree1 = p1 = encode_tv2string(tv1, NULL);
}
if (tv2->v_type == VAR_STRING) {
if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) {
- p2 = (char_u *)"'";
+ p2 = "'";
} else {
- p2 = tv2->vval.v_string;
+ p2 = (char *)tv2->vval.v_string;
}
} else {
- tofree2 = p2 = (char_u *) encode_tv2string(tv2, NULL);
+ tofree2 = p2 = encode_tv2string(tv2, NULL);
}
if (p1 == NULL) {
- p1 = (char_u *)"";
+ p1 = "";
}
if (p2 == NULL) {
- p2 = (char_u *)"";
+ p2 = "";
}
if (!sortinfo->item_compare_numeric) {
if (sortinfo->item_compare_ic) {
@@ -15851,19 +15268,20 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
}
} else {
double n1, n2;
- n1 = strtod((char *)p1, (char **)&p1);
- n2 = strtod((char *)p2, (char **)&p2);
+ n1 = strtod(p1, &p1);
+ n2 = strtod(p2, &p2);
res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1;
}
+ xfree(tofree1);
+ xfree(tofree2);
+
+item_compare_end:
// When the result would be zero, compare the item indexes. Makes the
// sort stable.
if (res == 0 && !keep_zero) {
res = si1->idx > si2->idx ? 1 : -1;
}
-
- xfree(tofree1);
- xfree(tofree2);
return res;
}
@@ -15884,7 +15302,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
typval_T rettv;
typval_T argv[3];
int dummy;
- char_u *func_name;
+ const char *func_name;
partial_T *partial = sortinfo->item_compare_partial;
// shortcut after failure in previous call; compare all items equal
@@ -15898,30 +15316,31 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
if (partial == NULL) {
func_name = sortinfo->item_compare_func;
} else {
- func_name = partial->pt_name;
+ func_name = (const char *)partial_name(partial);
}
+
// Copy the values. This is needed to be able to set v_lock to VAR_FIXED
// in the copy without changing the original list items.
- copy_tv(&si1->item->li_tv, &argv[0]);
- copy_tv(&si2->item->li_tv, &argv[1]);
+ tv_copy(&si1->item->li_tv, &argv[0]);
+ tv_copy(&si2->item->li_tv, &argv[1]);
- rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this
- res = call_func(func_name,
+ rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this
+ res = call_func((const char_u *)func_name,
(int)STRLEN(func_name),
- &rettv, 2, argv, 0L, 0L, &dummy, true,
+ &rettv, 2, argv, NULL, 0L, 0L, &dummy, true,
partial, sortinfo->item_compare_selfdict);
- clear_tv(&argv[0]);
- clear_tv(&argv[1]);
+ tv_clear(&argv[0]);
+ tv_clear(&argv[1]);
if (res == FAIL) {
res = ITEM_COMPARE_FAIL;
} else {
- res = get_tv_number_chk(&rettv, &sortinfo->item_compare_func_err);
+ res = tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err);
}
if (sortinfo->item_compare_func_err) {
res = ITEM_COMPARE_FAIL; // return value has wrong type
}
- clear_tv(&rettv);
+ tv_clear(&rettv);
// When the result would be zero, compare the pointers themselves. Makes
// the sort stable.
@@ -15959,23 +15378,22 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
sortinfo_T *old_sortinfo = sortinfo;
sortinfo = &info;
+ const char *const arg_errmsg = (sort
+ ? N_("sort() argument")
+ : N_("uniq() argument"));
+
if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listarg), sort ? "sort()" : "uniq()");
} else {
l = argvars[0].vval.v_list;
- if (l == NULL
- || tv_check_lock(l->lv_lock,
- (char_u *)(sort
- ? N_("sort() argument")
- : N_("uniq() argument")),
- true)) {
+ if (l == NULL || tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE)) {
goto theend;
}
rettv->vval.v_list = l;
rettv->v_type = VAR_LIST;
++l->lv_refcount;
- len = list_len(l);
+ len = tv_list_len(l);
if (len <= 1) {
goto theend; // short list sorts pretty quickly
}
@@ -15991,20 +15409,20 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
if (argvars[1].v_type != VAR_UNKNOWN) {
/* optional second argument: {func} */
if (argvars[1].v_type == VAR_FUNC) {
- info.item_compare_func = argvars[1].vval.v_string;
+ info.item_compare_func = (const char *)argvars[1].vval.v_string;
} else if (argvars[1].v_type == VAR_PARTIAL) {
info.item_compare_partial = argvars[1].vval.v_partial;
} else {
- int error = FALSE;
+ bool error = false;
- i = get_tv_number_chk(&argvars[1], &error);
+ i = tv_get_number_chk(&argvars[1], &error);
if (error) {
goto theend; // type error; errmsg already given
}
if (i == 1) {
info.item_compare_ic = true;
} else if (argvars[1].v_type != VAR_NUMBER) {
- info.item_compare_func = get_tv_string(&argvars[1]);
+ info.item_compare_func = tv_get_string(&argvars[1]);
} else if (i != 0) {
EMSG(_(e_invarg));
goto theend;
@@ -16013,16 +15431,16 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
if (*info.item_compare_func == NUL) {
// empty string means default sort
info.item_compare_func = NULL;
- } else if (STRCMP(info.item_compare_func, "n") == 0) {
+ } else if (strcmp(info.item_compare_func, "n") == 0) {
info.item_compare_func = NULL;
info.item_compare_numeric = true;
- } else if (STRCMP(info.item_compare_func, "N") == 0) {
+ } else if (strcmp(info.item_compare_func, "N") == 0) {
info.item_compare_func = NULL;
info.item_compare_numbers = true;
- } else if (STRCMP(info.item_compare_func, "f") == 0) {
+ } else if (strcmp(info.item_compare_func, "f") == 0) {
info.item_compare_func = NULL;
info.item_compare_float = true;
- } else if (STRCMP(info.item_compare_func, "i") == 0) {
+ } else if (strcmp(info.item_compare_func, "i") == 0) {
info.item_compare_func = NULL;
info.item_compare_ic = true;
}
@@ -16074,7 +15492,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
l->lv_len = 0;
for (i = 0; i < len; i++) {
- list_append(l, ptrs[i].item);
+ tv_list_append(l, ptrs[i].item);
}
}
}
@@ -16110,8 +15528,8 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
} else {
l->lv_last = ptrs[i].item;
}
- list_fix_watch(l, li);
- listitem_free(li);
+ tv_list_watch_fix(l, li);
+ tv_list_item_free(li);
l->lv_len--;
}
}
@@ -16156,11 +15574,9 @@ static void f_reltimefloat(typval_T *argvars , typval_T *rettv, FunPtr fptr)
*/
static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *s;
-
rettv->v_type = VAR_STRING;
- s = get_tv_string(&argvars[0]);
- rettv->vval.v_string = eval_soundfold(s);
+ const char *const s = tv_get_string(&argvars[0]);
+ rettv->vval.v_string = (char_u *)eval_soundfold(s);
}
/*
@@ -16168,25 +15584,26 @@ static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *word = (char_u *)"";
+ const char *word = "";
hlf_T attr = HLF_COUNT;
size_t len = 0;
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
if (argvars[0].v_type == VAR_UNKNOWN) {
- /* Find the start and length of the badly spelled word. */
- len = spell_move_to(curwin, FORWARD, TRUE, TRUE, &attr);
- if (len != 0)
- word = get_cursor_pos_ptr();
+ // Find the start and length of the badly spelled word.
+ len = spell_move_to(curwin, FORWARD, true, true, &attr);
+ if (len != 0) {
+ word = (char *)get_cursor_pos_ptr();
+ }
} else if (curwin->w_p_spell && *curbuf->b_s.b_p_spl != NUL) {
- char_u *str = get_tv_string_chk(&argvars[0]);
+ const char *str = tv_get_string_chk(&argvars[0]);
int capcol = -1;
if (str != NULL) {
- /* Check the argument for spelling. */
+ // Check the argument for spelling.
while (*str != NUL) {
- len = spell_check(curwin, str, &attr, &capcol, false);
+ len = spell_check(curwin, (char_u *)str, &attr, &capcol, false);
if (attr != HLF_COUNT) {
word = str;
break;
@@ -16197,14 +15614,13 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
assert(len <= INT_MAX);
- list_append_string(rettv->vval.v_list, word, (int)len);
- list_append_string(rettv->vval.v_list,
- (char_u *)(attr == HLF_SPB ? "bad" :
- attr == HLF_SPR ? "rare" :
- attr == HLF_SPL ? "local" :
- attr == HLF_SPC ? "caps" :
- ""),
- -1);
+ tv_list_append_string(rettv->vval.v_list, word, len);
+ tv_list_append_string(rettv->vval.v_list,
+ (attr == HLF_SPB ? "bad"
+ : attr == HLF_SPR ? "rare"
+ : attr == HLF_SPL ? "local"
+ : attr == HLF_SPC ? "caps"
+ : NULL), -1);
}
/*
@@ -16212,39 +15628,40 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *str;
- int typeerr = FALSE;
+ bool typeerr = false;
int maxcount;
garray_T ga;
listitem_T *li;
bool need_capital = false;
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) {
- str = get_tv_string(&argvars[0]);
+ const char *const str = tv_get_string(&argvars[0]);
if (argvars[1].v_type != VAR_UNKNOWN) {
- maxcount = get_tv_number_chk(&argvars[1], &typeerr);
- if (maxcount <= 0)
+ maxcount = tv_get_number_chk(&argvars[1], &typeerr);
+ if (maxcount <= 0) {
return;
+ }
if (argvars[2].v_type != VAR_UNKNOWN) {
- need_capital = get_tv_number_chk(&argvars[2], &typeerr);
- if (typeerr)
+ need_capital = tv_get_number_chk(&argvars[2], &typeerr);
+ if (typeerr) {
return;
+ }
}
} else
maxcount = 25;
- spell_suggest_list(&ga, str, maxcount, need_capital, false);
+ spell_suggest_list(&ga, (char_u *)str, maxcount, need_capital, false);
- for (int i = 0; i < ga.ga_len; ++i) {
- str = ((char_u **)ga.ga_data)[i];
+ for (int i = 0; i < ga.ga_len; i++) {
+ char *p = ((char **)ga.ga_data)[i];
- li = listitem_alloc();
+ li = tv_list_item_alloc();
li->li_tv.v_type = VAR_STRING;
li->li_tv.v_lock = 0;
- li->li_tv.vval.v_string = str;
- list_append(rettv->vval.v_list, li);
+ li->li_tv.vval.v_string = (char_u *)p;
+ tv_list_append(rettv->vval.v_list, li);
}
ga_clear(&ga);
}
@@ -16252,64 +15669,69 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *str;
- char_u *end;
- char_u *pat = NULL;
regmatch_T regmatch;
- char_u patbuf[NUMBUFLEN];
char_u *save_cpo;
int match;
colnr_T col = 0;
- int keepempty = FALSE;
- int typeerr = FALSE;
+ bool keepempty = false;
+ bool typeerr = false;
/* Make 'cpoptions' empty, the 'l' flag should not be used here. */
save_cpo = p_cpo;
p_cpo = (char_u *)"";
- str = get_tv_string(&argvars[0]);
+ const char *str = tv_get_string(&argvars[0]);
+ const char *pat = NULL;
+ char patbuf[NUMBUFLEN];
if (argvars[1].v_type != VAR_UNKNOWN) {
- pat = get_tv_string_buf_chk(&argvars[1], patbuf);
- if (pat == NULL)
- typeerr = TRUE;
- if (argvars[2].v_type != VAR_UNKNOWN)
- keepempty = get_tv_number_chk(&argvars[2], &typeerr);
+ pat = tv_get_string_buf_chk(&argvars[1], patbuf);
+ if (pat == NULL) {
+ typeerr = true;
+ }
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ keepempty = (bool)tv_get_number_chk(&argvars[2], &typeerr);
+ }
+ }
+ if (pat == NULL || *pat == NUL) {
+ pat = "[\\x01- ]\\+";
}
- if (pat == NULL || *pat == NUL)
- pat = (char_u *)"[\\x01- ]\\+";
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
if (typeerr)
return;
- regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
+ regmatch.regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL) {
regmatch.rm_ic = FALSE;
while (*str != NUL || keepempty) {
- if (*str == NUL)
- match = FALSE; /* empty item at the end */
- else
- match = vim_regexec_nl(&regmatch, str, col);
- if (match)
- end = regmatch.startp[0];
- else
- end = str + STRLEN(str);
+ if (*str == NUL) {
+ match = false; // Empty item at the end.
+ } else {
+ match = vim_regexec_nl(&regmatch, (char_u *)str, col);
+ }
+ const char *end;
+ if (match) {
+ end = (const char *)regmatch.startp[0];
+ } else {
+ end = str + strlen(str);
+ }
if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0
- && *str != NUL && match && end <
- regmatch.endp[0])) {
- list_append_string(rettv->vval.v_list, str, (int)(end - str));
+ && *str != NUL
+ && match
+ && end < (const char *)regmatch.endp[0])) {
+ tv_list_append_string(rettv->vval.v_list, str, end - str);
}
if (!match)
break;
- /* Advance to just after the match. */
- if (regmatch.endp[0] > str)
+ // Advance to just after the match.
+ if (regmatch.endp[0] > (char_u *)str) {
col = 0;
- else {
- /* Don't get stuck at the same match. */
+ } else {
+ // Don't get stuck at the same match.
col = (*mb_ptr2len)(regmatch.endp[0]);
}
- str = regmatch.endp[0];
+ str = (const char *)regmatch.endp[0];
}
vim_regfree(regmatch.regprog);
@@ -16323,11 +15745,16 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *p = skipwhite(get_tv_string(&argvars[0]));
+ char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0]));
+ bool isneg = (*p == '-');
- if (*p == '+')
+ if (*p == '+' || *p == '-') {
p = skipwhite(p + 1);
- (void) string2float((char *) p, &rettv->vval.v_float);
+ }
+ (void)string2float((char *)p, &rettv->vval.v_float);
+ if (isneg) {
+ rettv->vval.v_float *= -1;
+ }
rettv->v_type = VAR_FLOAT;
}
@@ -16335,37 +15762,45 @@ static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int base = 10;
- char_u *p;
- long n;
+ varnumber_T n;
int what;
if (argvars[1].v_type != VAR_UNKNOWN) {
- base = get_tv_number(&argvars[1]);
+ base = tv_get_number(&argvars[1]);
if (base != 2 && base != 8 && base != 10 && base != 16) {
EMSG(_(e_invarg));
return;
}
}
- p = skipwhite(get_tv_string(&argvars[0]));
- if (*p == '+') {
+ char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0]));
+ bool isneg = (*p == '-');
+ if (*p == '+' || *p == '-') {
p = skipwhite(p + 1);
}
switch (base) {
- case 2:
- what = STR2NR_BIN + STR2NR_FORCE;
+ case 2: {
+ what = STR2NR_BIN | STR2NR_FORCE;
break;
- case 8:
- what = STR2NR_OCT + STR2NR_FORCE;
+ }
+ case 8: {
+ what = STR2NR_OCT | STR2NR_FORCE;
break;
- case 16:
- what = STR2NR_HEX + STR2NR_FORCE;
+ }
+ case 16: {
+ what = STR2NR_HEX | STR2NR_FORCE;
break;
- default:
+ }
+ default: {
what = 0;
+ }
}
vim_str2nr(p, NULL, NULL, what, &n, NULL, 0);
- rettv->vval.v_number = n;
+ if (isneg) {
+ rettv->vval.v_number = -n;
+ } else {
+ rettv->vval.v_number = n;
+ }
}
/*
@@ -16373,17 +15808,16 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u result_buf[256];
time_t seconds;
- char_u *p;
rettv->v_type = VAR_STRING;
- p = get_tv_string(&argvars[0]);
- if (argvars[1].v_type == VAR_UNKNOWN)
+ char *p = (char *)tv_get_string(&argvars[0]);
+ if (argvars[1].v_type == VAR_UNKNOWN) {
seconds = time(NULL);
- else
- seconds = (time_t)get_tv_number(&argvars[1]);
+ } else {
+ seconds = (time_t)tv_get_number(&argvars[1]);
+ }
struct tm curtime;
struct tm *curtime_ptr = os_localtime_r(&seconds, &curtime);
@@ -16397,23 +15831,27 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
conv.vc_type = CONV_NONE;
enc = enc_locale();
convert_setup(&conv, p_enc, enc);
- if (conv.vc_type != CONV_NONE)
- p = string_convert(&conv, p, NULL);
- if (p != NULL)
- (void)strftime((char *)result_buf, sizeof(result_buf),
- (char *)p, curtime_ptr);
- else
+ if (conv.vc_type != CONV_NONE) {
+ p = (char *)string_convert(&conv, (char_u *)p, NULL);
+ }
+ char result_buf[256];
+ if (p != NULL) {
+ (void)strftime(result_buf, sizeof(result_buf), p, curtime_ptr);
+ } else {
result_buf[0] = NUL;
+ }
- if (conv.vc_type != CONV_NONE)
+ if (conv.vc_type != CONV_NONE) {
xfree(p);
+ }
convert_setup(&conv, enc, p_enc);
- if (conv.vc_type != CONV_NONE)
- rettv->vval.v_string = string_convert(&conv, result_buf, NULL);
- else
- rettv->vval.v_string = vim_strsave(result_buf);
+ if (conv.vc_type != CONV_NONE) {
+ rettv->vval.v_string = string_convert(&conv, (char_u *)result_buf, NULL);
+ } else {
+ rettv->vval.v_string = (char_u *)xstrdup(result_buf);
+ }
- /* Release conversion descriptors */
+ // Release conversion descriptors.
convert_setup(&conv, NULL, NULL);
xfree(enc);
}
@@ -16422,33 +15860,28 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// "strgetchar()" function
static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *str;
- int len;
- int error = false;
- int charidx;
-
rettv->vval.v_number = -1;
- str = get_tv_string_chk(&argvars[0]);
+
+ const char *const str = tv_get_string_chk(&argvars[0]);
if (str == NULL) {
return;
}
- len = (int)STRLEN(str);
- charidx = get_tv_number_chk(&argvars[1], &error);
+ bool error = false;
+ varnumber_T charidx = tv_get_number_chk(&argvars[1], &error);
if (error) {
return;
}
- {
- int byteidx = 0;
+ const size_t len = STRLEN(str);
+ size_t byteidx = 0;
- while (charidx >= 0 && byteidx < len) {
- if (charidx == 0) {
- rettv->vval.v_number = mb_ptr2char(str + byteidx);
- break;
- }
- charidx--;
- byteidx += mb_cptr2len(str + byteidx);
+ while (charidx >= 0 && byteidx < len) {
+ if (charidx == 0) {
+ rettv->vval.v_number = mb_ptr2char((const char_u *)str + byteidx);
+ break;
}
+ charidx--;
+ byteidx += MB_CPTR2LEN((const char_u *)str + byteidx);
}
}
@@ -16457,32 +15890,33 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u buf[NUMBUFLEN];
- char_u *needle;
- char_u *haystack;
- char_u *save_haystack;
- char_u *pos;
- int start_idx;
-
- needle = get_tv_string_chk(&argvars[1]);
- save_haystack = haystack = get_tv_string_buf_chk(&argvars[0], buf);
rettv->vval.v_number = -1;
- if (needle == NULL || haystack == NULL)
- return; /* type error; errmsg already given */
+
+ char buf[NUMBUFLEN];
+ const char *const needle = tv_get_string_chk(&argvars[1]);
+ const char *haystack = tv_get_string_buf_chk(&argvars[0], buf);
+ const char *const haystack_start = haystack;
+ if (needle == NULL || haystack == NULL) {
+ return; // Type error; errmsg already given.
+ }
if (argvars[2].v_type != VAR_UNKNOWN) {
- int error = FALSE;
+ bool error = false;
- start_idx = get_tv_number_chk(&argvars[2], &error);
- if (error || start_idx >= (int)STRLEN(haystack))
+ const ptrdiff_t start_idx = (ptrdiff_t)tv_get_number_chk(&argvars[2],
+ &error);
+ if (error || start_idx >= (ptrdiff_t)strlen(haystack)) {
return;
- if (start_idx >= 0)
+ }
+ if (start_idx >= 0) {
haystack += start_idx;
+ }
}
- pos = (char_u *)strstr((char *)haystack, (char *)needle);
- if (pos != NULL)
- rettv->vval.v_number = (varnumber_T)(pos - save_haystack);
+ const char *pos = strstr(haystack, needle);
+ if (pos != NULL) {
+ rettv->vval.v_number = (varnumber_T)(pos - haystack_start);
+ }
}
/*
@@ -16499,8 +15933,7 @@ static void f_string(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_strlen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv->vval.v_number = (varnumber_T)(STRLEN(
- get_tv_string(&argvars[0])));
+ rettv->vval.v_number = (varnumber_T)strlen(tv_get_string(&argvars[0]));
}
/*
@@ -16508,21 +15941,21 @@ static void f_strlen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *s = get_tv_string(&argvars[0]);
+ const char *s = tv_get_string(&argvars[0]);
int skipcc = 0;
varnumber_T len = 0;
- int (*func_mb_ptr2char_adv)(char_u **pp);
+ int (*func_mb_ptr2char_adv)(const char_u **pp);
if (argvars[1].v_type != VAR_UNKNOWN) {
- skipcc = get_tv_number_chk(&argvars[1], NULL);
+ skipcc = tv_get_number_chk(&argvars[1], NULL);
}
if (skipcc < 0 || skipcc > 1) {
EMSG(_(e_invarg));
} else {
func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
while (*s != NUL) {
- func_mb_ptr2char_adv(&s);
- ++len;
+ func_mb_ptr2char_adv((const char_u **)&s);
+ len++;
}
rettv->vval.v_number = len;
}
@@ -16533,13 +15966,14 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *s = get_tv_string(&argvars[0]);
+ const char *const s = tv_get_string(&argvars[0]);
int col = 0;
- if (argvars[1].v_type != VAR_UNKNOWN)
- col = get_tv_number(&argvars[1]);
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ col = tv_get_number(&argvars[1]);
+ }
- rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, s) - col);
+ rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char_u *)s) - col);
}
/*
@@ -16547,44 +15981,40 @@ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_strwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *s = get_tv_string(&argvars[0]);
+ const char *const s = tv_get_string(&argvars[0]);
- rettv->vval.v_number = (varnumber_T) mb_string2cells(s);
+ rettv->vval.v_number = (varnumber_T)mb_string2cells((const char_u *)s);
}
// "strcharpart()" function
-static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) {
- char_u *p;
- int nchar;
- int nbyte = 0;
- int charlen;
- int len = 0;
- int slen;
- int error = false;
-
- p = get_tv_string(&argvars[0]);
- slen = (int)STRLEN(p);
+static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ const char *const p = tv_get_string(&argvars[0]);
+ const size_t slen = STRLEN(p);
- nchar = get_tv_number_chk(&argvars[1], &error);
+ int nbyte = 0;
+ bool error = false;
+ varnumber_T nchar = tv_get_number_chk(&argvars[1], &error);
if (!error) {
if (nchar > 0) {
- while (nchar > 0 && nbyte < slen) {
- nbyte += mb_cptr2len(p + nbyte);
+ while (nchar > 0 && (size_t)nbyte < slen) {
+ nbyte += MB_CPTR2LEN((const char_u *)p + nbyte);
nchar--;
}
} else {
nbyte = nchar;
}
}
+ int len = 0;
if (argvars[2].v_type != VAR_UNKNOWN) {
- charlen = get_tv_number(&argvars[2]);
- while (charlen > 0 && nbyte + len < slen) {
+ int charlen = tv_get_number(&argvars[2]);
+ while (charlen > 0 && nbyte + len < (int)slen) {
int off = nbyte + len;
if (off < 0) {
len += 1;
} else {
- len += mb_cptr2len(p + off);
+ len += (size_t)MB_CPTR2LEN((const char_u *)p + off);
}
charlen--;
}
@@ -16597,17 +16027,17 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) {
if (nbyte < 0) {
len += nbyte;
nbyte = 0;
- } else if (nbyte > slen) {
+ } else if ((size_t)nbyte > slen) {
nbyte = slen;
}
if (len < 0) {
len = 0;
- } else if (nbyte + len > slen) {
+ } else if (nbyte + len > (int)slen) {
len = slen - nbyte;
}
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = vim_strnsave(p + nbyte, len);
+ rettv->vval.v_string = (char_u *)xstrndup(p + nbyte, (size_t)len);
}
/*
@@ -16615,39 +16045,37 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) {
*/
static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *p;
- int n;
- int len;
- int slen;
- int error = FALSE;
+ bool error = false;
- p = get_tv_string(&argvars[0]);
- slen = (int)STRLEN(p);
+ const char *const p = tv_get_string(&argvars[0]);
+ const size_t slen = strlen(p);
- n = get_tv_number_chk(&argvars[1], &error);
- if (error)
+ varnumber_T n = tv_get_number_chk(&argvars[1], &error);
+ varnumber_T len;
+ if (error) {
len = 0;
- else if (argvars[2].v_type != VAR_UNKNOWN)
- len = get_tv_number(&argvars[2]);
- else
- len = slen - n; /* default len: all bytes that are available. */
+ } else if (argvars[2].v_type != VAR_UNKNOWN) {
+ len = tv_get_number(&argvars[2]);
+ } else {
+ len = slen - n; // Default len: all bytes that are available.
+ }
- /*
- * Only return the overlap between the specified part and the actual
- * string.
- */
+ // Only return the overlap between the specified part and the actual
+ // string.
if (n < 0) {
len += n;
n = 0;
- } else if (n > slen)
+ } else if (n > (varnumber_T)slen) {
n = slen;
- if (len < 0)
+ }
+ if (len < 0) {
len = 0;
- else if (n + len > slen)
+ } else if (n + len > (varnumber_T)slen) {
len = slen - n;
+ }
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = vim_strnsave(p + n, len);
+ rettv->vval.v_string = (char_u *)xmemdupz(p + n, (size_t)len);
}
/*
@@ -16655,45 +16083,46 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u buf[NUMBUFLEN];
- char_u *needle;
- char_u *haystack;
- char_u *rest;
- char_u *lastmatch = NULL;
- int haystack_len, end_idx;
-
- needle = get_tv_string_chk(&argvars[1]);
- haystack = get_tv_string_buf_chk(&argvars[0], buf);
+ char buf[NUMBUFLEN];
+ const char *const needle = tv_get_string_chk(&argvars[1]);
+ const char *const haystack = tv_get_string_buf_chk(&argvars[0], buf);
rettv->vval.v_number = -1;
- if (needle == NULL || haystack == NULL)
- return; /* type error; errmsg already given */
+ if (needle == NULL || haystack == NULL) {
+ return; // Type error; errmsg already given.
+ }
- haystack_len = (int)STRLEN(haystack);
+ const size_t haystack_len = STRLEN(haystack);
+ ptrdiff_t end_idx;
if (argvars[2].v_type != VAR_UNKNOWN) {
- /* Third argument: upper limit for index */
- end_idx = get_tv_number_chk(&argvars[2], NULL);
- if (end_idx < 0)
- return; /* can never find a match */
- } else
- end_idx = haystack_len;
+ // Third argument: upper limit for index.
+ end_idx = (ptrdiff_t)tv_get_number_chk(&argvars[2], NULL);
+ if (end_idx < 0) {
+ return; // Can never find a match.
+ }
+ } else {
+ end_idx = (ptrdiff_t)haystack_len;
+ }
+ const char *lastmatch = NULL;
if (*needle == NUL) {
- /* Empty string matches past the end. */
+ // Empty string matches past the end.
lastmatch = haystack + end_idx;
} else {
- for (rest = haystack; *rest != NUL; ++rest) {
- rest = (char_u *)strstr((char *)rest, (char *)needle);
- if (rest == NULL || rest > haystack + end_idx)
+ for (const char *rest = haystack; *rest != NUL; rest++) {
+ rest = strstr(rest, needle);
+ if (rest == NULL || rest > haystack + end_idx) {
break;
+ }
lastmatch = rest;
}
}
- if (lastmatch == NULL)
+ if (lastmatch == NULL) {
rettv->vval.v_number = -1;
- else
+ } else {
rettv->vval.v_number = (varnumber_T)(lastmatch - haystack);
+ }
}
/*
@@ -16702,7 +16131,7 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = transstr(get_tv_string(&argvars[0]));
+ rettv->vval.v_string = transstr((char_u *)tv_get_string(&argvars[0]));
}
/*
@@ -16710,18 +16139,22 @@ static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- int error = FALSE;
- int no = (int)get_tv_number_chk(&argvars[0], &error);
+ bool error = false;
+ int no = (int)tv_get_number_chk(&argvars[0], &error);
if (error) {
return;
}
+ if (no < 0 || no >= NSUBEXP) {
+ EMSGN(_("E935: invalid submatch number: %d"), no);
+ return;
+ }
int retList = 0;
if (argvars[1].v_type != VAR_UNKNOWN) {
- retList = get_tv_number_chk(&argvars[1], &error);
+ retList = tv_get_number_chk(&argvars[1], &error);
if (error) {
- return;
+ return;
}
}
@@ -16739,40 +16172,47 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u patbuf[NUMBUFLEN];
- char_u subbuf[NUMBUFLEN];
- char_u flagsbuf[NUMBUFLEN];
+ char patbuf[NUMBUFLEN];
+ char subbuf[NUMBUFLEN];
+ char flagsbuf[NUMBUFLEN];
+
+ const char *const str = tv_get_string_chk(&argvars[0]);
+ const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf);
+ const char *sub = NULL;
+ const char *const flg = tv_get_string_buf_chk(&argvars[3], flagsbuf);
- char_u *str = get_tv_string_chk(&argvars[0]);
- char_u *pat = get_tv_string_buf_chk(&argvars[1], patbuf);
- char_u *sub = get_tv_string_buf_chk(&argvars[2], subbuf);
- char_u *flg = get_tv_string_buf_chk(&argvars[3], flagsbuf);
+ typval_T *expr = NULL;
+ if (tv_is_func(argvars[2])) {
+ expr = &argvars[2];
+ } else {
+ sub = tv_get_string_buf_chk(&argvars[2], subbuf);
+ }
rettv->v_type = VAR_STRING;
- if (str == NULL || pat == NULL || sub == NULL || flg == NULL)
+ if (str == NULL || pat == NULL || (sub == NULL && expr == NULL)
+ || flg == NULL) {
rettv->vval.v_string = NULL;
- else
- rettv->vval.v_string = do_string_sub(str, pat, sub, flg);
+ } else {
+ rettv->vval.v_string = do_string_sub((char_u *)str, (char_u *)pat,
+ (char_u *)sub, expr, (char_u *)flg);
+ }
}
-/*
- * "synID(lnum, col, trans)" function
- */
+/// "synID(lnum, col, trans)" function
static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- int id = 0;
- long lnum;
- long col;
- int trans;
- int transerr = FALSE;
+ // -1 on type error (both)
+ const linenr_T lnum = tv_get_lnum(argvars);
+ const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1;
- lnum = get_tv_lnum(argvars); /* -1 on type error */
- col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */
- trans = get_tv_number_chk(&argvars[2], &transerr);
+ bool transerr = false;
+ const int trans = tv_get_number_chk(&argvars[2], &transerr);
+ int id = 0;
if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count
- && col >= 0 && col < (long)STRLEN(ml_get(lnum)))
- id = syn_get_id(curwin, lnum, (colnr_T)col, trans, NULL, FALSE);
+ && col >= 0 && (size_t)col < STRLEN(ml_get(lnum))) {
+ id = syn_get_id(curwin, lnum, col, trans, NULL, false);
+ }
rettv->vval.v_number = id;
}
@@ -16782,20 +16222,16 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *p = NULL;
- int id;
- char_u *what;
- char_u *mode;
- char_u modebuf[NUMBUFLEN];
+ const int id = (int)tv_get_number(&argvars[0]);
+ const char *const what = tv_get_string(&argvars[1]);
int modec;
-
- id = get_tv_number(&argvars[0]);
- what = get_tv_string(&argvars[1]);
if (argvars[2].v_type != VAR_UNKNOWN) {
- mode = get_tv_string_buf(&argvars[2], modebuf);
+ char modebuf[NUMBUFLEN];
+ const char *const mode = tv_get_string_buf(&argvars[2], modebuf);
modec = TOLOWER_ASC(mode[0]);
- if (modec != 'c' && modec != 'g')
- modec = 0; /* replace invalid with current */
+ if (modec != 'c' && modec != 'g') {
+ modec = 0; // Replace invalid with current.
+ }
} else if (ui_rgb_attached()) {
modec = 'g';
} else {
@@ -16803,54 +16239,56 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
+ const char *p = NULL;
switch (TOLOWER_ASC(what[0])) {
- case 'b':
- if (TOLOWER_ASC(what[1]) == 'g') /* bg[#] */
+ case 'b': {
+ if (TOLOWER_ASC(what[1]) == 'g') { // bg[#]
+ p = highlight_color(id, what, modec);
+ } else { // bold
+ p = highlight_has_attr(id, HL_BOLD, modec);
+ }
+ break;
+ }
+ case 'f': { // fg[#] or font
p = highlight_color(id, what, modec);
- else /* bold */
- p = highlight_has_attr(id, HL_BOLD, modec);
- break;
-
- case 'f': /* fg[#] or font */
- p = highlight_color(id, what, modec);
- break;
-
- case 'i':
- if (TOLOWER_ASC(what[1]) == 'n') /* inverse */
+ break;
+ }
+ case 'i': {
+ if (TOLOWER_ASC(what[1]) == 'n') { // inverse
+ p = highlight_has_attr(id, HL_INVERSE, modec);
+ } else { // italic
+ p = highlight_has_attr(id, HL_ITALIC, modec);
+ }
+ break;
+ }
+ case 'n': { // name
+ p = get_highlight_name(NULL, id - 1);
+ break;
+ }
+ case 'r': { // reverse
p = highlight_has_attr(id, HL_INVERSE, modec);
- else /* italic */
- p = highlight_has_attr(id, HL_ITALIC, modec);
- break;
-
- case 'n': /* name */
- p = get_highlight_name(NULL, id - 1);
- break;
-
- case 'r': /* reverse */
- p = highlight_has_attr(id, HL_INVERSE, modec);
- break;
-
- case 's':
- if (TOLOWER_ASC(what[1]) == 'p') /* sp[#] */
- p = highlight_color(id, what, modec);
- else /* standout */
- p = highlight_has_attr(id, HL_STANDOUT, modec);
- break;
-
- case 'u':
- if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c')
- /* underline */
- p = highlight_has_attr(id, HL_UNDERLINE, modec);
- else
- /* undercurl */
- p = highlight_has_attr(id, HL_UNDERCURL, modec);
- break;
+ break;
+ }
+ case 's': {
+ if (TOLOWER_ASC(what[1]) == 'p') { // sp[#]
+ p = highlight_color(id, what, modec);
+ } else { // standout
+ p = highlight_has_attr(id, HL_STANDOUT, modec);
+ }
+ break;
+ }
+ case 'u': {
+ if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c') { // underline
+ p = highlight_has_attr(id, HL_UNDERLINE, modec);
+ } else { // undercurl
+ p = highlight_has_attr(id, HL_UNDERCURL, modec);
+ }
+ break;
+ }
}
- if (p != NULL)
- p = vim_strsave(p);
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = p;
+ rettv->vval.v_string = (char_u *)(p == NULL ? p : xstrdup(p));
}
/*
@@ -16858,14 +16296,13 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- int id;
+ int id = tv_get_number(&argvars[0]);
- id = get_tv_number(&argvars[0]);
-
- if (id > 0)
+ if (id > 0) {
id = syn_get_final_id(id);
- else
+ } else {
id = 0;
+ }
rettv->vval.v_number = id;
}
@@ -16875,8 +16312,6 @@ static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- long lnum;
- long col;
int syntax_flags = 0;
int cchar;
int matchid = 0;
@@ -16885,15 +16320,16 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->v_type = VAR_LIST;
rettv->vval.v_list = NULL;
- lnum = get_tv_lnum(argvars); /* -1 on type error */
- col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */
+ // -1 on type error (both)
+ const linenr_T lnum = tv_get_lnum(argvars);
+ const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1;
memset(str, NUL, sizeof(str));
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0
- && col <= (long)STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) {
- (void)syn_get_id(curwin, lnum, col, FALSE, NULL, FALSE);
+ && (size_t)col <= STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) {
+ (void)syn_get_id(curwin, lnum, col, false, NULL, false);
syntax_flags = get_syntax_info(&matchid);
// get the conceal character
@@ -16911,10 +16347,10 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0);
+ tv_list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0);
// -1 to auto-determine strlen
- list_append_string(rettv->vval.v_list, str, -1);
- list_append_number(rettv->vval.v_list, matchid);
+ tv_list_append_string(rettv->vval.v_list, (const char *)str, -1);
+ tv_list_append_number(rettv->vval.v_list, matchid);
}
/*
@@ -16922,56 +16358,35 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- long lnum;
- long col;
-
rettv->v_type = VAR_LIST;
rettv->vval.v_list = NULL;
- lnum = get_tv_lnum(argvars); /* -1 on type error */
- col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */
+ // -1 on type error (both)
+ const linenr_T lnum = tv_get_lnum(argvars);
+ const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1;
if (lnum >= 1
&& lnum <= curbuf->b_ml.ml_line_count
&& col >= 0
- && col <= (long)STRLEN(ml_get(lnum))) {
- rettv_list_alloc(rettv);
- (void)syn_get_id(curwin, lnum, (colnr_T)col, FALSE, NULL, TRUE);
+ && (size_t)col <= STRLEN(ml_get(lnum))) {
+ tv_list_alloc_ret(rettv);
+ (void)syn_get_id(curwin, lnum, col, false, NULL, true);
int id;
int i = 0;
while ((id = syn_get_stack_item(i++)) >= 0) {
- list_append_number(rettv->vval.v_list, id);
+ tv_list_append_number(rettv->vval.v_list, id);
}
}
}
-static list_T* string_to_list(char_u *str, size_t len, bool keepempty)
+static list_T *string_to_list(const char *str, size_t len, const bool keepempty)
{
- list_T *list = list_alloc();
-
- // Copy each line to a list element using NL as the delimiter.
- for (size_t i = 0; i < len; i++) {
- char_u *start = str + i;
- size_t line_len = (char_u *) xmemscan(start, NL, len - i) - start;
- i += line_len;
-
- // Don't use a str function to copy res as it may contains NULs.
- char_u *s = xmemdupz(start, line_len);
- memchrsub(s, NUL, NL, line_len); // Replace NUL with NL to avoid truncation
-
- listitem_T *li = listitem_alloc();
- li->li_tv.v_type = VAR_STRING;
- li->li_tv.v_lock = 0;
- li->li_tv.vval.v_string = s;
- list_append(list, li);
- }
-
- // Optionally retain final newline, if present
- if (keepempty && str[len-1] == NL) {
- list_append_string(list, (char_u*)"", 0);
+ if (!keepempty && str[len - 1] == NL) {
+ len--;
}
-
+ list_T *const list = tv_list_alloc();
+ encode_list_write(list, str, len);
return list;
}
@@ -16986,16 +16401,20 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv,
}
// get input to the shell command (if any), and its length
- ssize_t input_len;
- char *input = (char *) save_tv_as_string(&argvars[1], &input_len, false);
+ ptrdiff_t input_len;
+ char *input = save_tv_as_string(&argvars[1], &input_len, false);
if (input_len < 0) {
assert(input == NULL);
return;
}
// get shell command to execute
- char **argv = tv_to_argv(&argvars[0], NULL, NULL);
+ bool executable = true;
+ char **argv = tv_to_argv(&argvars[0], NULL, &executable);
if (!argv) {
+ if (!executable) {
+ set_vim_var_nr(VV_SHELL_ERROR, (long)-1);
+ }
xfree(input);
return; // Already did emsg.
}
@@ -17012,7 +16431,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv,
if (res == NULL) {
if (retlist) {
// return an empty list when there's no output
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
} else {
rettv->vval.v_string = (char_u *) xstrdup("");
}
@@ -17022,9 +16441,9 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv,
if (retlist) {
int keepempty = 0;
if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) {
- keepempty = get_tv_number(&argvars[2]);
+ keepempty = tv_get_number(&argvars[2]);
}
- rettv->vval.v_list = string_to_list((char_u *) res, nread, keepempty != 0);
+ rettv->vval.v_list = string_to_list(res, nread, (bool)keepempty);
rettv->vval.v_list->lv_refcount++;
rettv->v_type = VAR_LIST;
@@ -17067,20 +16486,20 @@ static void f_systemlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- tabpage_T *tp;
win_T *wp = NULL;
- if (argvars[0].v_type == VAR_UNKNOWN)
+ if (argvars[0].v_type == VAR_UNKNOWN) {
wp = firstwin;
- else {
- tp = find_tabpage((int)get_tv_number(&argvars[0]));
- if (tp != NULL)
+ } else {
+ tabpage_T *const tp = find_tabpage((int)tv_get_number(&argvars[0]));
+ if (tp != NULL) {
wp = (tp == curtab) ? firstwin : tp->tp_firstwin;
+ }
}
if (wp != NULL) {
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
while (wp != NULL) {
- list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum);
+ tv_list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum);
wp = wp->w_next;
}
}
@@ -17093,19 +16512,20 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_tabpagenr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int nr = 1;
- char_u *arg;
if (argvars[0].v_type != VAR_UNKNOWN) {
- arg = get_tv_string_chk(&argvars[0]);
+ const char *const arg = tv_get_string_chk(&argvars[0]);
nr = 0;
if (arg != NULL) {
- if (STRCMP(arg, "$") == 0)
+ if (strcmp(arg, "$") == 0) {
nr = tabpage_index(NULL) - 1;
- else
+ } else {
EMSG2(_(e_invexpr2), arg);
+ }
}
- } else
+ } else {
nr = tabpage_index(curtab);
+ }
rettv->vval.v_number = nr;
}
@@ -17119,19 +16539,19 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar)
win_T *twin;
int nr = 1;
win_T *wp;
- char_u *arg;
twin = (tp == curtab) ? curwin : tp->tp_curwin;
if (argvar->v_type != VAR_UNKNOWN) {
- arg = get_tv_string_chk(argvar);
- if (arg == NULL)
- nr = 0; /* type error; errmsg already given */
- else if (STRCMP(arg, "$") == 0)
+ const char *const arg = tv_get_string_chk(argvar);
+ if (arg == NULL) {
+ nr = 0; // Type error; errmsg already given.
+ } else if (strcmp(arg, "$") == 0) {
twin = (tp == curtab) ? lastwin : tp->tp_lastwin;
- else if (STRCMP(arg, "#") == 0) {
+ } else if (strcmp(arg, "#") == 0) {
twin = (tp == curtab) ? prevwin : tp->tp_prevwin;
- if (twin == NULL)
+ if (twin == NULL) {
nr = 0;
+ }
} else {
EMSG2(_(e_invexpr2), arg);
nr = 0;
@@ -17157,13 +16577,12 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar)
static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int nr = 1;
- tabpage_T *tp;
-
- tp = find_tabpage((int)get_tv_number(&argvars[0]));
- if (tp == NULL)
+ tabpage_T *const tp = find_tabpage((int)tv_get_number(&argvars[0]));
+ if (tp == NULL) {
nr = 0;
- else
+ } else {
nr = get_winnr(tp, &argvars[1]);
+ }
rettv->vval.v_number = nr;
}
@@ -17173,16 +16592,16 @@ static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *fname;
+ char *fname;
tagname_T tn;
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
fname = xmalloc(MAXPATHL);
- int first = TRUE;
- while (get_tagfname(&tn, first, fname) == OK) {
- list_append_string(rettv->vval.v_list, fname, -1);
- first = FALSE;
+ bool first = true;
+ while (get_tagfname(&tn, first, (char_u *)fname) == OK) {
+ tv_list_append_string(rettv->vval.v_list, fname, -1);
+ first = false;
}
tagname_free(&tn);
@@ -17194,15 +16613,19 @@ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *tag_pattern;
-
- tag_pattern = get_tv_string(&argvars[0]);
+ const char *const tag_pattern = tv_get_string(&argvars[0]);
- rettv->vval.v_number = FALSE;
- if (*tag_pattern == NUL)
+ rettv->vval.v_number = false;
+ if (*tag_pattern == NUL) {
return;
+ }
- (void)get_tags(rettv_list_alloc(rettv), tag_pattern);
+ const char *fname = NULL;
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ fname = tv_get_string(&argvars[1]);
+ }
+ (void)get_tags(tv_list_alloc_ret(rettv), (char_u *)tag_pattern,
+ (char_u *)fname);
}
/*
@@ -17226,7 +16649,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- char *cmd;
+ const char *cmd;
bool executable = true;
char **argv = tv_to_argv(&argvars[0], &cmd, &executable);
if (!argv) {
@@ -17244,15 +16667,15 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
Callback on_stdout = CALLBACK_NONE, on_stderr = CALLBACK_NONE,
on_exit = CALLBACK_NONE;
dict_T *job_opts = NULL;
- char *cwd = ".";
+ const char *cwd = ".";
if (argvars[1].v_type == VAR_DICT) {
job_opts = argvars[1].vval.v_dict;
- char *new_cwd = (char *)get_dict_string(job_opts, "cwd", false);
- if (new_cwd && strlen(new_cwd) > 0) {
+ const char *const new_cwd = tv_dict_get_string(job_opts, "cwd", false);
+ if (new_cwd && *new_cwd != NUL) {
cwd = new_cwd;
// The new cwd must be a directory.
- if (!os_isdir((char_u *)cwd)) {
+ if (!os_isdir((const char_u *)cwd)) {
EMSG2(_(e_invarg2), "expected valid directory");
shell_free_argv(argv);
return;
@@ -17289,13 +16712,15 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// at this point the buffer has no terminal instance associated yet, so unset
// the 'swapfile' option to ensure no swap file will be created
curbuf->b_p_swf = false;
- (void)setfname(curbuf, (uint8_t *)buf, NULL, true);
+ (void)setfname(curbuf, (char_u *)buf, NULL, true);
// Save the job id and pid in b:terminal_job_{id,pid}
- Error err;
- dict_set_value(curbuf->b_vars, cstr_as_string("terminal_job_id"),
- INTEGER_OBJ(rettv->vval.v_number), false, false, &err);
- dict_set_value(curbuf->b_vars, cstr_as_string("terminal_job_pid"),
- INTEGER_OBJ(pid), false, false, &err);
+ Error err = ERROR_INIT;
+ dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"),
+ INTEGER_OBJ(rettv->vval.v_number), false, false, &err);
+ api_clear_error(&err);
+ dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"),
+ INTEGER_OBJ(pid), false, false, &err);
+ api_clear_error(&err);
Terminal *term = terminal_open(topts);
data->term = term;
@@ -17304,7 +16729,17 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
-static bool callback_from_typval(Callback *callback, typval_T *arg)
+// "test_garbagecollect_now()" function
+static void f_test_garbagecollect_now(typval_T *argvars,
+ typval_T *rettv, FunPtr fptr)
+{
+ // This is dangerous, any Lists and Dicts used internally may be freed
+ // while still in use.
+ garbage_collect(true);
+}
+
+bool callback_from_typval(Callback *const callback, typval_T *const arg)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL) {
callback->data.partial = arg->vval.v_partial;
@@ -17324,53 +16759,33 @@ static bool callback_from_typval(Callback *callback, typval_T *arg)
return true;
}
-
/// Unref/free callback
-static void callback_free(Callback *callback)
+void callback_free(Callback *const callback)
+ FUNC_ATTR_NONNULL_ALL
{
switch (callback->type) {
- case kCallbackFuncref:
+ case kCallbackFuncref: {
func_unref(callback->data.funcref);
xfree(callback->data.funcref);
break;
-
- case kCallbackPartial:
+ }
+ case kCallbackPartial: {
partial_unref(callback->data.partial);
break;
-
- case kCallbackNone:
+ }
+ case kCallbackNone: {
break;
-
- default:
+ }
+ default: {
abort();
+ }
}
callback->type = kCallbackNone;
}
-static bool callback_equal(Callback *cb1, Callback *cb2)
-{
- if (cb1->type != cb2->type) {
- return false;
- }
- switch (cb1->type) {
- case kCallbackFuncref:
- return STRCMP(cb1->data.funcref, cb2->data.funcref) == 0;
-
- case kCallbackPartial:
- // FIXME: this is inconsistent with tv_equal but is needed for precision
- // maybe change dictwatcheradd to return a watcher id instead?
- return cb1->data.partial == cb2->data.partial;
-
- case kCallbackNone:
- return true;
-
- default:
- abort();
- }
-}
-
-static bool callback_call(Callback *callback, int argcount_in,
- typval_T *argvars_in, typval_T *rettv)
+bool callback_call(Callback *const callback, const int argcount_in,
+ typval_T *const argvars_in, typval_T *const rettv)
+ FUNC_ATTR_NONNULL_ALL
{
partial_T *partial;
char_u *name;
@@ -17382,7 +16797,7 @@ static bool callback_call(Callback *callback, int argcount_in,
case kCallbackPartial:
partial = callback->data.partial;
- name = partial->pt_name;
+ name = partial_name(partial);
break;
case kCallbackNone:
@@ -17395,7 +16810,7 @@ static bool callback_call(Callback *callback, int argcount_in,
int dummy;
return call_func(name, (int)STRLEN(name), rettv, argcount_in, argvars_in,
- curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy,
+ NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy,
true, partial, NULL);
}
@@ -17422,11 +16837,82 @@ static bool set_ref_in_callback(Callback *callback, int copyID,
return false;
}
+static void add_timer_info(typval_T *rettv, timer_T *timer)
+{
+ list_T *list = rettv->vval.v_list;
+ dict_T *dict = tv_dict_alloc();
+
+ tv_list_append_dict(list, dict);
+ tv_dict_add_nr(dict, S_LEN("id"), timer->timer_id);
+ tv_dict_add_nr(dict, S_LEN("time"), timer->timeout);
+ tv_dict_add_nr(dict, S_LEN("paused"), timer->paused);
+
+ tv_dict_add_nr(dict, S_LEN("repeat"),
+ (timer->repeat_count < 0 ? -1 : timer->repeat_count));
+
+ dictitem_T *di = tv_dict_item_alloc("callback");
+ if (tv_dict_add(dict, di) == FAIL) {
+ xfree(di);
+ return;
+ }
+
+ if (timer->callback.type == kCallbackPartial) {
+ di->di_tv.v_type = VAR_PARTIAL;
+ di->di_tv.vval.v_partial = timer->callback.data.partial;
+ timer->callback.data.partial->pt_refcount++;
+ } else if (timer->callback.type == kCallbackFuncref) {
+ di->di_tv.v_type = VAR_FUNC;
+ di->di_tv.vval.v_string = vim_strsave(timer->callback.data.funcref);
+ }
+ di->di_tv.v_lock = 0;
+}
+
+static void add_timer_info_all(typval_T *rettv)
+{
+ timer_T *timer;
+ map_foreach_value(timers, timer, {
+ if (!timer->stopped) {
+ add_timer_info(rettv, timer);
+ }
+ })
+}
+
+/// "timer_info([timer])" function
+static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ tv_list_alloc_ret(rettv);
+ if (argvars[0].v_type != VAR_UNKNOWN) {
+ if (argvars[0].v_type != VAR_NUMBER) {
+ EMSG(_(e_number_exp));
+ return;
+ }
+ timer_T *timer = pmap_get(uint64_t)(timers, tv_get_number(&argvars[0]));
+ if (timer != NULL && !timer->stopped) {
+ add_timer_info(rettv, timer);
+ }
+ } else {
+ add_timer_info_all(rettv);
+ }
+}
+
+/// "timer_pause(timer, paused)" function
+static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr)
+{
+ if (argvars[0].v_type != VAR_NUMBER) {
+ EMSG(_(e_number_exp));
+ return;
+ }
+ int paused = (bool)tv_get_number(&argvars[1]);
+ timer_T *timer = pmap_get(uint64_t)(timers, tv_get_number(&argvars[0]));
+ if (timer != NULL) {
+ timer->paused = paused;
+ }
+}
/// "timer_start(timeout, callback, opts)" function
static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- long timeout = get_tv_number(&argvars[0]);
+ const long timeout = tv_get_number(&argvars[0]);
timer_T *timer;
int repeat = 1;
dict_T *dict;
@@ -17436,11 +16922,12 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[2].v_type != VAR_UNKNOWN) {
if (argvars[2].v_type != VAR_DICT
|| (dict = argvars[2].vval.v_dict) == NULL) {
- EMSG2(_(e_invarg2), get_tv_string(&argvars[2]));
+ EMSG2(_(e_invarg2), tv_get_string(&argvars[2]));
return;
}
- if (dict_find(dict, (char_u *)"repeat", -1) != NULL) {
- repeat = get_dict_number(dict, "repeat");
+ dictitem_T *const di = tv_dict_find(dict, S_LEN("repeat"));
+ if (di != NULL) {
+ repeat = tv_get_number(&di->di_tv);
if (repeat == 0) {
repeat = 1;
}
@@ -17455,6 +16942,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
timer = xmalloc(sizeof *timer);
timer->refcount = 1;
timer->stopped = false;
+ timer->paused = false;
timer->repeat_count = repeat;
timer->timeout = timeout;
timer->timer_id = last_timer_id++;
@@ -17464,8 +16952,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
timer->tw.events = multiqueue_new_child(main_loop.events);
// if main loop is blocked, don't queue up multiple events
timer->tw.blockable = true;
- time_watcher_start(&timer->tw, timer_due_cb, timeout,
- timeout * (repeat != 1));
+ time_watcher_start(&timer->tw, timer_due_cb, timeout, timeout);
pmap_put(uint64_t)(timers, timer->timer_id, timer);
rettv->vval.v_number = timer->timer_id;
@@ -17480,7 +16967,7 @@ static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- timer_T *timer = pmap_get(uint64_t)(timers, get_tv_number(&argvars[0]));
+ timer_T *timer = pmap_get(uint64_t)(timers, tv_get_number(&argvars[0]));
if (timer == NULL) {
return;
@@ -17489,28 +16976,32 @@ static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
timer_stop(timer);
}
+static void f_timer_stopall(typval_T *argvars, typval_T *unused, FunPtr fptr)
+{
+ timer_stop_all();
+}
+
// invoked on the main loop
static void timer_due_cb(TimeWatcher *tw, void *data)
{
timer_T *timer = (timer_T *)data;
- if (timer->stopped) {
+ if (timer->stopped || timer->paused) {
return;
}
+
timer->refcount++;
// if repeat was negative repeat forever
if (timer->repeat_count >= 0 && --timer->repeat_count == 0) {
timer_stop(timer);
}
- typval_T argv[2];
- init_tv(argv);
+ typval_T argv[2] = { TV_INITIAL_VALUE, TV_INITIAL_VALUE };
argv[0].v_type = VAR_NUMBER;
argv[0].vval.v_number = timer->timer_id;
- typval_T rettv;
+ typval_T rettv = TV_INITIAL_VALUE;
- init_tv(&rettv);
callback_call(&timer->callback, 1, argv, &rettv);
- clear_tv(&rettv);
+ tv_clear(&rettv);
if (!timer->stopped && timer->timeout == 0) {
// special case: timeout=0 means the callback will be
@@ -17550,7 +17041,7 @@ static void timer_decref(timer_T *timer)
}
}
-void timer_teardown(void)
+static void timer_stop_all(void)
{
timer_T *timer;
map_foreach_value(timers, timer, {
@@ -17558,35 +17049,19 @@ void timer_teardown(void)
})
}
+void timer_teardown(void)
+{
+ timer_stop_all();
+}
+
/*
* "tolower(string)" function
*/
static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *p = vim_strsave(get_tv_string(&argvars[0]));
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = p;
-
- while (*p != NUL) {
- int l;
-
- if (enc_utf8) {
- int c, lc;
-
- c = utf_ptr2char(p);
- lc = utf_tolower(c);
- l = utf_ptr2len(p);
- /* TODO: reallocate string when byte count changes. */
- if (utf_char2len(lc) == l)
- utf_char2bytes(lc, p);
- p += l;
- } else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
- p += l; /* skip multi-byte character */
- else {
- *p = TOLOWER_LOC(*p); /* note that tolower() can be a macro */
- ++p;
- }
- }
+ rettv->vval.v_string = (char_u *)strcase_save(tv_get_string(&argvars[0]),
+ false);
}
/*
@@ -17595,7 +17070,8 @@ static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = strup_save(get_tv_string(&argvars[0]));
+ rettv->vval.v_string = (char_u *)strcase_save(tv_get_string(&argvars[0]),
+ true);
}
/*
@@ -17603,77 +17079,71 @@ static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *in_str;
- char_u *fromstr;
- char_u *tostr;
- char_u *p;
- int inlen;
- int fromlen;
- int tolen;
- int idx;
- char_u *cpstr;
- int cplen;
- int first = TRUE;
- char_u buf[NUMBUFLEN];
- char_u buf2[NUMBUFLEN];
- garray_T ga;
+ char buf[NUMBUFLEN];
+ char buf2[NUMBUFLEN];
- in_str = get_tv_string(&argvars[0]);
- fromstr = get_tv_string_buf_chk(&argvars[1], buf);
- tostr = get_tv_string_buf_chk(&argvars[2], buf2);
+ const char *in_str = tv_get_string(&argvars[0]);
+ const char *fromstr = tv_get_string_buf_chk(&argvars[1], buf);
+ const char *tostr = tv_get_string_buf_chk(&argvars[2], buf2);
- /* Default return value: empty string. */
+ // Default return value: empty string.
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
- if (fromstr == NULL || tostr == NULL)
- return; /* type error; errmsg already given */
+ if (fromstr == NULL || tostr == NULL) {
+ return; // Type error; errmsg already given.
+ }
+ garray_T ga;
ga_init(&ga, (int)sizeof(char), 80);
- if (!has_mbyte)
- /* not multi-byte: fromstr and tostr must be the same length */
- if (STRLEN(fromstr) != STRLEN(tostr)) {
-error:
- EMSG2(_(e_invarg2), fromstr);
- ga_clear(&ga);
- return;
+ if (!has_mbyte) {
+ // Not multi-byte: fromstr and tostr must be the same length.
+ if (strlen(fromstr) != strlen(tostr)) {
+ goto error;
}
+ }
- /* fromstr and tostr have to contain the same number of chars */
+ // fromstr and tostr have to contain the same number of chars.
+ bool first = true;
while (*in_str != NUL) {
if (has_mbyte) {
- inlen = (*mb_ptr2len)(in_str);
- cpstr = in_str;
- cplen = inlen;
- idx = 0;
- for (p = fromstr; *p != NUL; p += fromlen) {
- fromlen = (*mb_ptr2len)(p);
+ const char *cpstr = in_str;
+ const int inlen = (*mb_ptr2len)((const char_u *)in_str);
+ int cplen = inlen;
+ int idx = 0;
+ int fromlen;
+ for (const char *p = fromstr; *p != NUL; p += fromlen) {
+ fromlen = (*mb_ptr2len)((const char_u *)p);
if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0) {
+ int tolen;
for (p = tostr; *p != NUL; p += tolen) {
- tolen = (*mb_ptr2len)(p);
+ tolen = (*mb_ptr2len)((const char_u *)p);
if (idx-- == 0) {
cplen = tolen;
- cpstr = p;
+ cpstr = (char *)p;
break;
}
}
- if (*p == NUL) /* tostr is shorter than fromstr */
+ if (*p == NUL) { // tostr is shorter than fromstr.
goto error;
+ }
break;
}
- ++idx;
+ idx++;
}
if (first && cpstr == in_str) {
- /* Check that fromstr and tostr have the same number of
- * (multi-byte) characters. Done only once when a character
- * of in_str doesn't appear in fromstr. */
- first = FALSE;
- for (p = tostr; *p != NUL; p += tolen) {
- tolen = (*mb_ptr2len)(p);
- --idx;
+ // Check that fromstr and tostr have the same number of
+ // (multi-byte) characters. Done only once when a character
+ // of in_str doesn't appear in fromstr.
+ first = false;
+ int tolen;
+ for (const char *p = tostr; *p != NUL; p += tolen) {
+ tolen = (*mb_ptr2len)((const char_u *)p);
+ idx--;
}
- if (idx != 0)
+ if (idx != 0) {
goto error;
+ }
}
ga_grow(&ga, cplen);
@@ -17682,13 +17152,14 @@ error:
in_str += inlen;
} else {
- /* When not using multi-byte chars we can do it faster. */
- p = vim_strchr(fromstr, *in_str);
- if (p != NULL)
+ // When not using multi-byte chars we can do it faster.
+ const char *const p = strchr(fromstr, *in_str);
+ if (p != NULL) {
ga_append(&ga, tostr[p - fromstr]);
- else
+ } else {
ga_append(&ga, *in_str);
- ++in_str;
+ }
+ in_str++;
}
}
@@ -17696,6 +17167,11 @@ error:
ga_append(&ga, NUL);
rettv->vval.v_string = ga.ga_data;
+ return;
+error:
+ EMSG2(_(e_invarg2), fromstr);
+ ga_clear(&ga);
+ return;
}
/*
@@ -17741,20 +17217,18 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
- {
- char_u *fname = get_tv_string(&argvars[0]);
+ const char *const fname = tv_get_string(&argvars[0]);
- if (*fname == NUL) {
- /* If there is no file name there will be no undo file. */
- rettv->vval.v_string = NULL;
- } else {
- char *ffname = FullName_save((char *)fname, false);
+ if (*fname == NUL) {
+ // If there is no file name there will be no undo file.
+ rettv->vval.v_string = NULL;
+ } else {
+ char *ffname = FullName_save(fname, false);
- if (ffname != NULL) {
- rettv->vval.v_string = (char_u *)u_get_undo_file_name(ffname, false);
- }
- xfree(ffname);
+ if (ffname != NULL) {
+ rettv->vval.v_string = (char_u *)u_get_undo_file_name(ffname, false);
}
+ xfree(ffname);
}
}
@@ -17763,22 +17237,22 @@ static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv_dict_alloc(rettv);
+ tv_dict_alloc_ret(rettv);
dict_T *dict = rettv->vval.v_dict;
list_T *list;
- dict_add_nr_str(dict, "synced", (long)curbuf->b_u_synced, NULL);
- dict_add_nr_str(dict, "seq_last", curbuf->b_u_seq_last, NULL);
- dict_add_nr_str(dict, "save_last",
- (long)curbuf->b_u_save_nr_last, NULL);
- dict_add_nr_str(dict, "seq_cur", curbuf->b_u_seq_cur, NULL);
- dict_add_nr_str(dict, "time_cur", (long)curbuf->b_u_time_cur, NULL);
- dict_add_nr_str(dict, "save_cur", (long)curbuf->b_u_save_nr_cur, NULL);
+ tv_dict_add_nr(dict, S_LEN("synced"), (varnumber_T)curbuf->b_u_synced);
+ tv_dict_add_nr(dict, S_LEN("seq_last"), (varnumber_T)curbuf->b_u_seq_last);
+ tv_dict_add_nr(dict, S_LEN("save_last"),
+ (varnumber_T)curbuf->b_u_save_nr_last);
+ tv_dict_add_nr(dict, S_LEN("seq_cur"), (varnumber_T)curbuf->b_u_seq_cur);
+ tv_dict_add_nr(dict, S_LEN("time_cur"), (varnumber_T)curbuf->b_u_time_cur);
+ tv_dict_add_nr(dict, S_LEN("save_cur"), (varnumber_T)curbuf->b_u_save_nr_cur);
- list = list_alloc();
+ list = tv_list_alloc();
u_eval_tree(curbuf->b_u_oldhead, list);
- dict_add_list(dict, "entries", list);
+ tv_dict_add_list(dict, S_LEN("entries"), list);
}
/*
@@ -17837,7 +17311,7 @@ static void f_wildmenumode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "win_findbuf()" function
static void f_win_findbuf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
win_findbuf(argvars, rettv->vval.v_list);
}
@@ -17856,7 +17330,7 @@ static void f_win_gotoid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "win_id2tabwin()" function
static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv_list_alloc(rettv);
+ tv_list_alloc_ret(rettv);
win_id2tabwin(argvars, rettv->vval.v_list);
}
@@ -17951,36 +17425,37 @@ static void f_winrestcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- dict_T *dict;
+ dict_T *dict;
if (argvars[0].v_type != VAR_DICT
- || (dict = argvars[0].vval.v_dict) == NULL)
- EMSG(_(e_invarg));
- else {
- if (dict_find(dict, (char_u *)"lnum", -1) != NULL) {
- curwin->w_cursor.lnum = get_dict_number(dict, "lnum");
+ || (dict = argvars[0].vval.v_dict) == NULL) {
+ emsgf(_(e_invarg));
+ } else {
+ dictitem_T *di;
+ if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) {
+ curwin->w_cursor.lnum = tv_get_number(&di->di_tv);
}
- if (dict_find(dict, (char_u *)"col", -1) != NULL) {
- curwin->w_cursor.col = get_dict_number(dict, "col");
+ if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) {
+ curwin->w_cursor.col = tv_get_number(&di->di_tv);
}
- if (dict_find(dict, (char_u *)"coladd", -1) != NULL) {
- curwin->w_cursor.coladd = get_dict_number(dict, "coladd");
+ if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) {
+ curwin->w_cursor.coladd = tv_get_number(&di->di_tv);
}
- if (dict_find(dict, (char_u *)"curswant", -1) != NULL) {
- curwin->w_curswant = get_dict_number(dict, "curswant");
+ if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) {
+ curwin->w_curswant = tv_get_number(&di->di_tv);
curwin->w_set_curswant = false;
}
- if (dict_find(dict, (char_u *)"topline", -1) != NULL) {
- set_topline(curwin, get_dict_number(dict, "topline"));
+ if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) {
+ set_topline(curwin, tv_get_number(&di->di_tv));
}
- if (dict_find(dict, (char_u *)"topfill", -1) != NULL) {
- curwin->w_topfill = get_dict_number(dict, "topfill");
+ if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) {
+ curwin->w_topfill = tv_get_number(&di->di_tv);
}
- if (dict_find(dict, (char_u *)"leftcol", -1) != NULL) {
- curwin->w_leftcol = get_dict_number(dict, "leftcol");
+ if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) {
+ curwin->w_leftcol = tv_get_number(&di->di_tv);
}
- if (dict_find(dict, (char_u *)"skipcol", -1) != NULL) {
- curwin->w_skipcol = get_dict_number(dict, "skipcol");
+ if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) {
+ curwin->w_skipcol = tv_get_number(&di->di_tv);
}
check_cursor();
@@ -18003,45 +17478,103 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
dict_T *dict;
- rettv_dict_alloc(rettv);
+ tv_dict_alloc_ret(rettv);
dict = rettv->vval.v_dict;
- dict_add_nr_str(dict, "lnum", (long)curwin->w_cursor.lnum, NULL);
- dict_add_nr_str(dict, "col", (long)curwin->w_cursor.col, NULL);
- dict_add_nr_str(dict, "coladd", (long)curwin->w_cursor.coladd, NULL);
+ tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)curwin->w_cursor.lnum);
+ tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)curwin->w_cursor.col);
+ tv_dict_add_nr(dict, S_LEN("coladd"), (varnumber_T)curwin->w_cursor.coladd);
update_curswant();
- dict_add_nr_str(dict, "curswant", (long)curwin->w_curswant, NULL);
+ tv_dict_add_nr(dict, S_LEN("curswant"), (varnumber_T)curwin->w_curswant);
- dict_add_nr_str(dict, "topline", (long)curwin->w_topline, NULL);
- dict_add_nr_str(dict, "topfill", (long)curwin->w_topfill, NULL);
- dict_add_nr_str(dict, "leftcol", (long)curwin->w_leftcol, NULL);
- dict_add_nr_str(dict, "skipcol", (long)curwin->w_skipcol, NULL);
+ tv_dict_add_nr(dict, S_LEN("topline"), (varnumber_T)curwin->w_topline);
+ tv_dict_add_nr(dict, S_LEN("topfill"), (varnumber_T)curwin->w_topfill);
+ tv_dict_add_nr(dict, S_LEN("leftcol"), (varnumber_T)curwin->w_leftcol);
+ tv_dict_add_nr(dict, S_LEN("skipcol"), (varnumber_T)curwin->w_skipcol);
}
/// Writes list of strings to file
-static bool write_list(FILE *fd, list_T *list, bool binary)
+///
+/// @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.
+static bool write_list(FileDescriptor *const fp, const list_T *const list,
+ const bool binary)
{
- int ret = true;
-
- for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) {
- for (char_u *s = get_tv_string(&li->li_tv); *s != NUL; ++s) {
- if (putc(*s == '\n' ? NUL : *s, fd) == EOF) {
- ret = false;
- break;
+ int error = 0;
+ for (const listitem_T *li = list->lv_first; li != NULL; li = li->li_next) {
+ const char *const s = tv_get_string_chk(&li->li_tv);
+ 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 || li->li_next != NULL) {
- if (putc('\n', fd) == EOF) {
- ret = false;
- break;
+ const ptrdiff_t written = file_write(fp, "\n", 1);
+ if (written < 0) {
+ error = (int)written;
+ goto write_list_error;
}
}
- if (ret == false) {
- EMSG(_(e_write));
- break;
+ }
+ if ((error = file_flush(fp)) != 0) {
+ goto write_list_error;
+ }
+ return true;
+write_list_error:
+ emsgf(_("E80: Error while writing: %s"), os_strerror(error));
+ return false;
+}
+
+/// Initializes a static list with 10 items.
+void init_static_list(staticList10_T *sl)
+{
+ list_T *l = &sl->sl_list;
+
+ memset(sl, 0, sizeof(staticList10_T));
+ l->lv_first = &sl->sl_items[0];
+ l->lv_last = &sl->sl_items[9];
+ l->lv_refcount = DO_NOT_FREE_CNT;
+ l->lv_lock = VAR_FIXED;
+ sl->sl_list.lv_len = 10;
+
+ for (int i = 0; i < 10; i++) {
+ listitem_T *li = &sl->sl_items[i];
+
+ if (i == 0) {
+ li->li_prev = NULL;
+ } else {
+ li->li_prev = li - 1;
+ }
+ if (i == 9) {
+ li->li_next = NULL;
+ } else {
+ li->li_next = li + 1;
}
}
- return ret;
}
/// Saves a typval_T as a string.
@@ -18053,7 +17586,7 @@ static bool write_list(FILE *fd, list_T *list, bool binary)
/// @param[in] endnl If true, the output will end in a newline (if a list).
/// @returns an allocated string if `tv` represents a VimL string, list, or
/// number; NULL otherwise.
-static char_u *save_tv_as_string(typval_T *tv, ssize_t *len, bool endnl)
+static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
if (tv->v_type == VAR_UNKNOWN) {
@@ -18061,34 +17594,33 @@ static char_u *save_tv_as_string(typval_T *tv, ssize_t *len, bool endnl)
return NULL;
}
- // For types other than list, let get_tv_string_buf_chk() get the value or
+ // For types other than list, let tv_get_string_buf_chk() get the value or
// print an error.
if (tv->v_type != VAR_LIST) {
- char_u *ret = get_tv_string_chk(tv);
- if (ret && (*len = STRLEN(ret))) {
- ret = vim_strsave(ret);
+ const char *ret = tv_get_string_chk(tv);
+ if (ret && (*len = strlen(ret))) {
+ return xmemdupz(ret, (size_t)(*len));
} else {
- ret = NULL;
*len = -1;
+ return NULL;
}
- return ret;
}
// Pre-calculate the resulting length.
*len = 0;
list_T *list = tv->vval.v_list;
for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) {
- *len += STRLEN(get_tv_string(&li->li_tv)) + 1;
+ *len += strlen(tv_get_string(&li->li_tv)) + 1;
}
if (*len == 0) {
return NULL;
}
- char_u *ret = xmalloc(*len + endnl);
- char_u *end = ret;
+ char *ret = xmalloc(*len + endnl);
+ char *end = ret;
for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) {
- for (char_u *s = get_tv_string(&li->li_tv); *s != NUL; s++) {
+ for (const char *s = tv_get_string(&li->li_tv); *s != NUL; s++) {
*end++ = (*s == '\n') ? NUL : *s;
}
if (endnl || li->li_next != NULL) {
@@ -18117,7 +17649,7 @@ static void f_winwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "wordcount()" function
static void f_wordcount(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv_dict_alloc(rettv);
+ tv_dict_alloc_ret(rettv);
cursor_pos_info(rettv->vval.v_dict);
}
@@ -18140,28 +17672,50 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
bool binary = false;
bool append = false;
+ bool do_fsync = !!p_fs;
if (argvars[2].v_type != VAR_UNKNOWN) {
- if (vim_strchr(get_tv_string(&argvars[2]), 'b')) {
- binary = true;
+ const char *const flags = tv_get_string_chk(&argvars[2]);
+ if (flags == NULL) {
+ return;
}
- if (vim_strchr(get_tv_string(&argvars[2]), 'a')) {
- append = true;
+ for (const char *p = flags; *p; p++) {
+ switch (*p) {
+ case 'b': { binary = true; break; }
+ case 'a': { append = true; break; }
+ case 's': { do_fsync = true; break; }
+ case 'S': { do_fsync = false; break; }
+ default: {
+ // Using %s, p and not %c, *p to preserve multibyte characters
+ emsgf(_("E5060: Unknown flag: %s"), p);
+ return;
+ }
+ }
}
}
- // Always open the file in binary mode, library functions have a mind of
- // their own about CR-LF conversion.
- char_u *fname = get_tv_string(&argvars[1]);
- FILE *fd;
- if (*fname == NUL || (fd = mch_fopen((char *)fname,
- append ? APPENDBIN : WRITEBIN)) == NULL) {
- EMSG2(_(e_notcreate), *fname == NUL ? (char_u *)_("<empty>") : fname);
- rettv->vval.v_number = -1;
+ char buf[NUMBUFLEN];
+ const char *const fname = tv_get_string_buf_chk(&argvars[1], buf);
+ if (fname == NULL) {
+ return;
+ }
+ FileDescriptor fp;
+ int error;
+ rettv->vval.v_number = -1;
+ if (*fname == NUL) {
+ EMSG(_("E482: Can't open file with an empty name"));
+ } else if ((error = file_open(&fp, fname,
+ ((append ? kFileAppend : kFileTruncate)
+ | kFileCreate), 0666)) != 0) {
+ emsgf(_("E482: Can't open file %s for writing: %s"),
+ fname, os_strerror(error));
} else {
- if (write_list(fd, argvars[0].vval.v_list, binary) == false) {
- rettv->vval.v_number = -1;
+ if (write_list(&fp, argvars[0].vval.v_list, binary)) {
+ rettv->vval.v_number = 0;
+ }
+ if ((error = file_close(&fp, do_fsync)) != 0) {
+ emsgf(_("E80: Error when closing file %s: %s"),
+ fname, os_strerror(error));
}
- fclose(fd);
}
}
/*
@@ -18169,82 +17723,96 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_xor(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL)
- ^ get_tv_number_chk(&argvars[1], NULL);
+ rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL)
+ ^ tv_get_number_chk(&argvars[1], NULL);
}
-/*
- * Translate a String variable into a position.
- * Returns NULL when there is an error.
- */
-static pos_T *
-var2fpos (
- typval_T *varp,
- int dollar_lnum, /* TRUE when $ is last line */
- int *fnum /* set to fnum for '0, 'A, etc. */
-)
+/// Translate a VimL object into a position
+///
+/// Accepts VAR_LIST and VAR_STRING objects. Does not give an error for invalid
+/// type.
+///
+/// @param[in] tv Object to translate.
+/// @param[in] dollar_lnum True when "$" is last line.
+/// @param[out] ret_fnum Set to fnum for marks.
+///
+/// @return Pointer to position or NULL in case of error (e.g. invalid type).
+pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum,
+ int *const ret_fnum)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- char_u *name;
static pos_T pos;
pos_T *pp;
- /* Argument can be [lnum, col, coladd]. */
- if (varp->v_type == VAR_LIST) {
+ // Argument can be [lnum, col, coladd].
+ if (tv->v_type == VAR_LIST) {
list_T *l;
int len;
- int error = FALSE;
- listitem_T *li;
+ bool error = false;
+ listitem_T *li;
- l = varp->vval.v_list;
- if (l == NULL)
+ l = tv->vval.v_list;
+ if (l == NULL) {
return NULL;
+ }
- /* Get the line number */
- pos.lnum = list_find_nr(l, 0L, &error);
- if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count)
- return NULL; /* invalid line number */
+ // Get the line number.
+ pos.lnum = tv_list_find_nr(l, 0L, &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 = list_find_nr(l, 1L, &error);
- if (error)
+ // Get the column number.
+ pos.col = tv_list_find_nr(l, 1L, &error);
+ if (error) {
return NULL;
+ }
len = (long)STRLEN(ml_get(pos.lnum));
- /* We accept "$" for the column number: last column. */
- li = list_find(l, 1L);
+ // We accept "$" for the column number: last column.
+ li = tv_list_find(l, 1L);
if (li != NULL && li->li_tv.v_type == VAR_STRING
&& li->li_tv.vval.v_string != NULL
- && STRCMP(li->li_tv.vval.v_string, "$") == 0)
+ && STRCMP(li->li_tv.vval.v_string, "$") == 0) {
pos.col = len + 1;
+ }
- /* Accept a position up to the NUL after the line. */
- if (pos.col == 0 || (int)pos.col > len + 1)
- return NULL; /* invalid column number */
- --pos.col;
+ // Accept a position up to the NUL after the line.
+ if (pos.col == 0 || (int)pos.col > len + 1) {
+ // Invalid column number.
+ return NULL;
+ }
+ pos.col--;
- /* Get the virtual offset. Defaults to zero. */
- pos.coladd = list_find_nr(l, 2L, &error);
- if (error)
+ // Get the virtual offset. Defaults to zero.
+ pos.coladd = tv_list_find_nr(l, 2L, &error);
+ if (error) {
pos.coladd = 0;
+ }
return &pos;
}
- name = get_tv_string_chk(varp);
- if (name == NULL)
+ const char *const name = tv_get_string_chk(tv);
+ if (name == NULL) {
return NULL;
- if (name[0] == '.') /* cursor */
+ }
+ if (name[0] == '.') { // Cursor.
return &curwin->w_cursor;
- if (name[0] == 'v' && name[1] == NUL) { /* Visual start */
- if (VIsual_active)
+ }
+ if (name[0] == 'v' && name[1] == NUL) { // Visual start.
+ if (VIsual_active) {
return &VIsual;
+ }
return &curwin->w_cursor;
}
- if (name[0] == '\'') { /* mark */
- pp = getmark_buf_fnum(curbuf, name[1], FALSE, fnum);
- if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0)
+ if (name[0] == '\'') { // Mark.
+ pp = getmark_buf_fnum(curbuf, (uint8_t)name[1], false, ret_fnum);
+ if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) {
return NULL;
+ }
return pp;
}
@@ -18297,32 +17865,37 @@ static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp)
return FAIL;
if (fnump != NULL) {
- n = list_find_nr(l, i++, NULL); /* fnum */
- if (n < 0)
+ n = tv_list_find_nr(l, i++, NULL); // fnum
+ if (n < 0) {
return FAIL;
- if (n == 0)
- n = curbuf->b_fnum; /* current buffer */
+ }
+ if (n == 0) {
+ n = curbuf->b_fnum; // Current buffer.
+ }
*fnump = n;
}
- n = list_find_nr(l, i++, NULL); /* lnum */
- if (n < 0)
+ n = tv_list_find_nr(l, i++, NULL); // lnum
+ if (n < 0) {
return FAIL;
+ }
posp->lnum = n;
- n = list_find_nr(l, i++, NULL); /* col */
- if (n < 0)
+ n = tv_list_find_nr(l, i++, NULL); // col
+ if (n < 0) {
return FAIL;
+ }
posp->col = n;
- n = list_find_nr(l, i, NULL); // off
- if (n < 0)
+ n = tv_list_find_nr(l, i, NULL); // off
+ if (n < 0) {
posp->coladd = 0;
- else
+ } else {
posp->coladd = n;
+ }
if (curswantp != NULL) {
- *curswantp = list_find_nr(l, i + 1, NULL); // curswant
+ *curswantp = tv_list_find_nr(l, i + 1, NULL); // curswant
}
return OK;
@@ -18333,15 +17906,16 @@ static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp)
* Advance "arg" to the first character after the name.
* Return 0 for error.
*/
-static int get_env_len(char_u **arg)
+static int get_env_len(const char_u **arg)
{
- char_u *p;
int len;
- for (p = *arg; vim_isIDc(*p); ++p)
- ;
- if (p == *arg) /* no name found */
+ const char_u *p;
+ for (p = *arg; vim_isIDc(*p); p++) {
+ }
+ if (p == *arg) { // No name found.
return 0;
+ }
len = (int)(p - *arg);
*arg = p;
@@ -18351,11 +17925,12 @@ static int get_env_len(char_u **arg)
// Get the length of the name of a function or internal variable.
// "arg" is advanced to the first non-white character after the name.
// Return 0 if something is wrong.
-static int get_id_len(char_u **arg) {
- char_u *p;
+static int get_id_len(const char **const arg)
+{
int len;
// Find the end of the name.
+ const char *p;
for (p = *arg; eval_isnamec(*p); p++) {
if (*p == ':') {
// "s:" is start of "s:var", but "n:" is not and can be used in
@@ -18372,7 +17947,7 @@ static int get_id_len(char_u **arg) {
}
len = (int)(p - *arg);
- *arg = skipwhite(p);
+ *arg = (const char *)skipwhite((const char_u *)p);
return len;
}
@@ -18386,18 +17961,18 @@ static int get_id_len(char_u **arg) {
* If the name contains 'magic' {}'s, expand them and return the
* expanded name in an allocated string via 'alias' - caller must free.
*/
-static int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose)
+static int get_name_len(const char **const arg,
+ char **alias,
+ int evaluate,
+ int verbose)
{
int len;
- char_u *p;
- char_u *expr_start;
- char_u *expr_end;
*alias = NULL; /* default to no alias */
- if ((*arg)[0] == K_SPECIAL && (*arg)[1] == KS_EXTRA
- && (*arg)[2] == (int)KE_SNR) {
- /* hard coded <SNR>, already translated */
+ if ((*arg)[0] == (char)K_SPECIAL && (*arg)[1] == (char)KS_EXTRA
+ && (*arg)[2] == (char)KE_SNR) {
+ // Hard coded <SNR>, already translated.
*arg += 3;
return get_id_len(arg) + 3;
}
@@ -18407,17 +17982,17 @@ static int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose)
*arg += len;
}
- /*
- * Find the end of the name; check for {} construction.
- */
- p = find_name_end(*arg, &expr_start, &expr_end,
- len > 0 ? 0 : FNE_CHECK_START);
+ // Find the end of the name; check for {} construction.
+ char_u *expr_start;
+ char_u *expr_end;
+ const char *p = (const char *)find_name_end((char_u *)(*arg),
+ (const char_u **)&expr_start,
+ (const char_u **)&expr_end,
+ len > 0 ? 0 : FNE_CHECK_START);
if (expr_start != NULL) {
- char_u *temp_string;
-
if (!evaluate) {
len += (int)(p - *arg);
- *arg = skipwhite(p);
+ *arg = (const char *)skipwhite((const char_u *)p);
return len;
}
@@ -18425,11 +18000,13 @@ static int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose)
* Include any <SID> etc in the expanded string:
* Thus the -len here.
*/
- temp_string = make_expanded_name(*arg - len, expr_start, expr_end, p);
- if (temp_string == NULL)
+ char_u *temp_string = make_expanded_name((char_u *)(*arg) - len, expr_start,
+ expr_end, (char_u *)p);
+ if (temp_string == NULL) {
return -1;
- *alias = temp_string;
- *arg = skipwhite(p);
+ }
+ *alias = (char *)temp_string;
+ *arg = (const char *)skipwhite((const char_u *)p);
return (int)STRLEN(temp_string);
}
@@ -18446,12 +18023,11 @@ static int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose)
// "flags" can have FNE_INCL_BR and FNE_CHECK_START.
// Return a pointer to just after the name. Equal to "arg" if there is no
// valid name.
-static char_u *find_name_end(char_u *arg, char_u **expr_start,
- char_u **expr_end, int flags)
+static const char_u *find_name_end(const char_u *arg, const char_u **expr_start,
+ const char_u **expr_end, int flags)
{
int mb_nest = 0;
int br_nest = 0;
- char_u *p;
int len;
if (expr_start != NULL) {
@@ -18464,6 +18040,7 @@ static char_u *find_name_end(char_u *arg, char_u **expr_start,
return arg;
}
+ const char_u *p;
for (p = arg; *p != NUL
&& (eval_isnamec(*p)
|| *p == '{'
@@ -18535,7 +18112,8 @@ static char_u *find_name_end(char_u *arg, char_u **expr_start,
* Returns a new allocated string, which the caller must free.
* Returns NULL for failure.
*/
-static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end)
+static char_u *make_expanded_name(const char_u *in_start, char_u *expr_start,
+ char_u *expr_end, char_u *in_end)
{
char_u c1;
char_u *retval = NULL;
@@ -18564,7 +18142,9 @@ static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, char_u *
*expr_end = '}';
if (retval != NULL) {
- temp_result = find_name_end(retval, &expr_start, &expr_end, 0);
+ temp_result = (char_u *)find_name_end(retval,
+ (const char_u **)&expr_start,
+ (const char_u **)&expr_end, 0);
if (expr_start != NULL) {
/* Further expansion! */
temp_result = make_expanded_name(retval, expr_start,
@@ -18598,7 +18178,7 @@ static int eval_isnamec1(int c)
/*
* Get number v: variable value.
*/
-long get_vim_var_nr(int idx) FUNC_ATTR_PURE
+varnumber_T get_vim_var_nr(int idx) FUNC_ATTR_PURE
{
return vimvars[idx].vv_nr;
}
@@ -18608,7 +18188,7 @@ long get_vim_var_nr(int idx) FUNC_ATTR_PURE
*/
char_u *get_vim_var_str(int idx) FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET
{
- return get_tv_string(&vimvars[idx].vv_tv);
+ return (char_u *)tv_get_string(&vimvars[idx].vv_tv);
}
/*
@@ -18661,7 +18241,7 @@ void set_vcount(long count, long count1, int set_prevcount)
/// @param[in] val Value to set to.
void set_vim_var_nr(const VimVarIndex idx, const varnumber_T val)
{
- clear_tv(&vimvars[idx].vv_tv);
+ tv_clear(&vimvars[idx].vv_tv);
vimvars[idx].vv_type = VAR_NUMBER;
vimvars[idx].vv_nr = val;
}
@@ -18672,7 +18252,7 @@ void set_vim_var_nr(const VimVarIndex idx, const varnumber_T val)
/// @param[in] val Value to set to.
void set_vim_var_special(const VimVarIndex idx, const SpecialVarValue val)
{
- clear_tv(&vimvars[idx].vv_tv);
+ tv_clear(&vimvars[idx].vv_tv);
vimvars[idx].vv_type = VAR_SPECIAL;
vimvars[idx].vv_special = val;
}
@@ -18686,7 +18266,7 @@ void set_vim_var_special(const VimVarIndex idx, const SpecialVarValue val)
void set_vim_var_string(const VimVarIndex idx, const char *const val,
const ptrdiff_t len)
{
- clear_tv(&vimvars[idx].vv_di.di_tv);
+ tv_clear(&vimvars[idx].vv_di.di_tv);
vimvars[idx].vv_type = VAR_STRING;
if (val == NULL) {
vimvars[idx].vv_str = NULL;
@@ -18703,7 +18283,7 @@ void set_vim_var_string(const VimVarIndex idx, const char *const val,
/// @param[in,out] val Value to set to. Reference count will be incremented.
void set_vim_var_list(const VimVarIndex idx, list_T *const val)
{
- clear_tv(&vimvars[idx].vv_di.di_tv);
+ tv_clear(&vimvars[idx].vv_di.di_tv);
vimvars[idx].vv_type = VAR_LIST;
vimvars[idx].vv_list = val;
if (val != NULL) {
@@ -18718,14 +18298,14 @@ void set_vim_var_list(const VimVarIndex idx, list_T *const val)
/// Also keys of the dictionary will be made read-only.
void set_vim_var_dict(const VimVarIndex idx, dict_T *const val)
{
- clear_tv(&vimvars[idx].vv_di.di_tv);
+ tv_clear(&vimvars[idx].vv_di.di_tv);
vimvars[idx].vv_type = VAR_DICT;
vimvars[idx].vv_dict = val;
if (val != NULL) {
val->dv_refcount++;
// Set readonly
- dict_set_keys_readonly(val);
+ tv_dict_set_keys_readonly(val);
}
}
@@ -18843,9 +18423,8 @@ char_u *set_cmdarg(exarg_T *eap, char_u *oldarg)
* Get the value of internal variable "name".
* Return OK or FAIL.
*/
-static int
-get_var_tv (
- char_u *name,
+static int get_var_tv(
+ const char *name,
int len, // length of "name"
typval_T *rettv, // NULL when only checking existence
dictitem_T **dip, // non-NULL when typval's dict item is needed
@@ -18855,55 +18434,52 @@ get_var_tv (
{
int ret = OK;
typval_T *tv = NULL;
- typval_T atv;
dictitem_T *v;
- int cc;
-
- /* truncate the name, so that we can use strcmp() */
- cc = name[len];
- name[len] = NUL;
- /*
- * Check for "b:changedtick".
- */
- if (STRCMP(name, "b:changedtick") == 0) {
- atv.v_type = VAR_NUMBER;
- atv.vval.v_number = curbuf->b_changedtick;
- tv = &atv;
- }
- /*
- * Check for user-defined variables.
- */
- else {
- v = find_var(name, NULL, no_autoload);
- if (v != NULL) {
- tv = &v->di_tv;
- if (dip != NULL) {
- *dip = v;
- }
+ v = find_var(name, (size_t)len, NULL, no_autoload);
+ if (v != NULL) {
+ tv = &v->di_tv;
+ if (dip != NULL) {
+ *dip = v;
}
}
if (tv == NULL) {
- if (rettv != NULL && verbose)
- EMSG2(_(e_undefvar), name);
+ if (rettv != NULL && verbose) {
+ emsgf(_("E121: Undefined variable: %.*s"), len, name);
+ }
ret = FAIL;
- } else if (rettv != NULL)
- copy_tv(tv, rettv);
-
- name[len] = cc;
+ } else if (rettv != NULL) {
+ tv_copy(tv, rettv);
+ }
return ret;
}
-/*
- * Handle expr[expr], expr[expr:expr] subscript and .name lookup.
- * Also handle function call with Funcref variable: func(expr)
- * Can all be combined: dict.func(expr)[idx]['func'](expr)
- */
-static int
-handle_subscript (
- char_u **arg,
+/// Check if variable "name[len]" is a local variable or an argument.
+/// If so, "*eval_lavars_used" is set to TRUE.
+static void check_vars(const char *name, size_t len)
+{
+ if (eval_lavars_used == NULL) {
+ return;
+ }
+
+ const char *varname;
+ hashtab_T *ht = find_var_ht(name, len, &varname);
+
+ if (ht == get_funccal_local_ht() || ht == get_funccal_args_ht()) {
+ if (find_var(name, len, NULL, true) != NULL) {
+ *eval_lavars_used = true;
+ }
+ }
+}
+
+/// Handle expr[expr], expr[expr:expr] subscript and .name lookup.
+/// Also handle function call with Funcref variable: func(expr)
+/// Can all be combined: dict.func(expr)[idx]['func'](expr)
+static int
+handle_subscript(
+ const char **const arg,
typval_T *rettv,
int evaluate, /* do more than finding the end */
int verbose /* give error messages */
@@ -18918,8 +18494,7 @@ handle_subscript (
while (ret == OK
&& (**arg == '['
|| (**arg == '.' && rettv->v_type == VAR_DICT)
- || (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC
- || rettv->v_type == VAR_PARTIAL)))
+ || (**arg == '(' && (!evaluate || tv_is_func(*rettv))))
&& !ascii_iswhite(*(*arg - 1))) {
if (**arg == '(') {
partial_T *pt = NULL;
@@ -18931,59 +18506,59 @@ handle_subscript (
// Invoke the function. Recursive!
if (functv.v_type == VAR_PARTIAL) {
pt = functv.vval.v_partial;
- s = pt->pt_name;
+ s = partial_name(pt);
} else {
s = functv.vval.v_string;
}
} else {
s = (char_u *)"";
}
- ret = get_func_tv(s, (int)STRLEN(s), rettv, arg,
+ ret = get_func_tv(s, (int)STRLEN(s), rettv, (char_u **)arg,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&len, evaluate, pt, selfdict);
- /* Clear the funcref afterwards, so that deleting it while
- * evaluating the arguments is possible (see test55). */
- if (evaluate)
- clear_tv(&functv);
+ // Clear the funcref afterwards, so that deleting it while
+ // evaluating the arguments is possible (see test55).
+ if (evaluate) {
+ tv_clear(&functv);
+ }
/* Stop the expression evaluation when immediately aborting on
* error, or when an interrupt occurred or an exception was thrown
* but not caught. */
if (aborting()) {
- if (ret == OK)
- clear_tv(rettv);
+ if (ret == OK) {
+ tv_clear(rettv);
+ }
ret = FAIL;
}
- dict_unref(selfdict);
+ tv_dict_unref(selfdict);
selfdict = NULL;
- } else { /* **arg == '[' || **arg == '.' */
- dict_unref(selfdict);
+ } else { // **arg == '[' || **arg == '.'
+ tv_dict_unref(selfdict);
if (rettv->v_type == VAR_DICT) {
selfdict = rettv->vval.v_dict;
if (selfdict != NULL)
++selfdict->dv_refcount;
} else
selfdict = NULL;
- if (eval_index(arg, rettv, evaluate, verbose) == FAIL) {
- clear_tv(rettv);
+ if (eval_index((char_u **)arg, rettv, evaluate, verbose) == FAIL) {
+ tv_clear(rettv);
ret = FAIL;
}
}
}
// Turn "dict.Func" into a partial for "Func" bound to "dict".
- if (selfdict != NULL
- && (rettv->v_type == VAR_FUNC
- || rettv->v_type == VAR_PARTIAL)) {
+ if (selfdict != NULL && tv_is_func(*rettv)) {
set_selfdict(rettv, selfdict);
}
- dict_unref(selfdict);
+ tv_dict_unref(selfdict);
return ret;
}
-static void set_selfdict(typval_T *rettv, dict_T *selfdict)
+void set_selfdict(typval_T *rettv, dict_T *selfdict)
{
// Don't do this when "dict.Func" is already a partial that was bound
// explicitly (pt_auto is false).
@@ -18991,19 +18566,23 @@ static void set_selfdict(typval_T *rettv, dict_T *selfdict)
&& rettv->vval.v_partial->pt_dict != NULL) {
return;
}
- char_u *fname = rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING
- ? rettv->vval.v_string
- : rettv->vval.v_partial->pt_name;
+ char_u *fname;
char_u *tofree = NULL;
ufunc_T *fp;
char_u fname_buf[FLEN_FIXED + 1];
int error;
- // Translate "s:func" to the stored function name.
- fname = fname_trans_sid(fname, fname_buf, &tofree, &error);
-
- fp = find_func(fname);
- xfree(tofree);
+ if (rettv->v_type == VAR_PARTIAL && rettv->vval.v_partial->pt_func != NULL) {
+ fp = rettv->vval.v_partial->pt_func;
+ } else {
+ fname = rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING
+ ? rettv->vval.v_string
+ : rettv->vval.v_partial->pt_name;
+ // Translate "s:func" to the stored function name.
+ fname = fname_trans_sid(fname, fname_buf, &tofree, &error);
+ fp = find_func(fname);
+ xfree(tofree);
+ }
// Turn "dict.Func" into a partial for "Func" with "dict".
if (fp != NULL && (fp->uf_flags & FC_DICT)) {
@@ -19024,8 +18603,13 @@ static void set_selfdict(typval_T *rettv, dict_T *selfdict)
// Partial: copy the function name, use selfdict and copy
// args. Can't take over name or args, the partial might
// be referenced elsewhere.
- pt->pt_name = vim_strsave(ret_pt->pt_name);
- func_ref(pt->pt_name);
+ if (ret_pt->pt_name != NULL) {
+ pt->pt_name = vim_strsave(ret_pt->pt_name);
+ func_ref(pt->pt_name);
+ } else {
+ pt->pt_func = ret_pt->pt_func;
+ func_ptr_ref(pt->pt_func);
+ }
if (ret_pt->pt_argc > 0) {
size_t arg_size = sizeof(typval_T) * ret_pt->pt_argc;
pt->pt_argv = (typval_T *)xmalloc(arg_size);
@@ -19035,7 +18619,7 @@ static void set_selfdict(typval_T *rettv, dict_T *selfdict)
} else {
pt->pt_argc = ret_pt->pt_argc;
for (i = 0; i < pt->pt_argc; i++) {
- copy_tv(&ret_pt->pt_argv[i], &pt->pt_argv[i]);
+ tv_copy(&ret_pt->pt_argv[i], &pt->pt_argv[i]);
}
}
}
@@ -19047,546 +18631,90 @@ static void set_selfdict(typval_T *rettv, dict_T *selfdict)
}
}
-/*
- * Free the memory for a variable type-value.
- */
-void free_tv(typval_T *varp)
+// Find variable "name" in the list of variables.
+// Return a pointer to it if found, NULL if not found.
+// Careful: "a:0" variables don't have a name.
+// When "htp" is not NULL we are writing to the variable, set "htp" to the
+// hashtab_T used.
+static dictitem_T *find_var(const char *const name, const size_t name_len,
+ hashtab_T **htp, int no_autoload)
{
- if (varp != NULL) {
- switch (varp->v_type) {
- case VAR_FUNC:
- func_unref(varp->vval.v_string);
- // FALLTHROUGH
- case VAR_STRING:
- xfree(varp->vval.v_string);
- break;
- case VAR_PARTIAL:
- partial_unref(varp->vval.v_partial);
- break;
- case VAR_LIST:
- list_unref(varp->vval.v_list);
- break;
- case VAR_DICT:
- dict_unref(varp->vval.v_dict);
- break;
- case VAR_SPECIAL:
- case VAR_NUMBER:
- case VAR_FLOAT:
- case VAR_UNKNOWN:
- break;
- }
- xfree(varp);
+ const char *varname;
+ hashtab_T *const ht = find_var_ht(name, name_len, &varname);
+ if (htp != NULL) {
+ *htp = ht;
}
-}
-
-#define TYPVAL_ENCODE_ALLOW_SPECIALS false
-
-#define TYPVAL_ENCODE_CONV_NIL(tv) \
- do { \
- tv->vval.v_special = kSpecialVarFalse; \
- tv->v_lock = VAR_UNLOCKED; \
- } while (0)
-
-#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
- TYPVAL_ENCODE_CONV_NIL(tv)
-
-#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
- do { \
- (void)num; \
- tv->vval.v_number = 0; \
- tv->v_lock = VAR_UNLOCKED; \
- } while (0)
-
-#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num)
-
-#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
- do { \
- tv->vval.v_float = 0; \
- tv->v_lock = VAR_UNLOCKED; \
- } while (0)
-
-#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
- do { \
- xfree(buf); \
- tv->vval.v_string = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
- } while (0)
-
-#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len)
-
-#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type)
-
-static inline int _nothing_conv_func_start(typval_T *const tv,
- char_u *const fun)
- FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1)
-{
- tv->v_lock = VAR_UNLOCKED;
- if (tv->v_type == VAR_PARTIAL) {
- partial_T *const pt_ = tv->vval.v_partial;
- if (pt_ != NULL && pt_->pt_refcount > 1) {
- pt_->pt_refcount--;
- tv->vval.v_partial = NULL;
- return OK;
- }
- } else {
- func_unref(fun);
- if (fun != empty_string) {
- xfree(fun);
- }
- tv->vval.v_string = NULL;
+ if (ht == NULL) {
+ return NULL;
}
- return NOTDONE;
-}
-#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
- do { \
- if (_nothing_conv_func_start(tv, fun) != NOTDONE) { \
- return OK; \
- } \
- } while (0)
-
-#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len)
-#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len)
-
-static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID)
- FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
-{
- if (tv->v_type == VAR_PARTIAL) {
- partial_T *const pt = tv->vval.v_partial;
- if (pt == NULL) {
- return;
- }
- // Dictionary should already be freed by the time.
- // If it was not freed then it is a part of the reference cycle.
- assert(pt->pt_dict == NULL || pt->pt_dict->dv_copyID == copyID);
- pt->pt_dict = NULL;
- // As well as all arguments.
- pt->pt_argc = 0;
- assert(pt->pt_refcount <= 1);
- partial_unref(pt);
- tv->vval.v_partial = NULL;
- assert(tv->v_lock == VAR_UNLOCKED);
- }
-}
-#define TYPVAL_ENCODE_CONV_FUNC_END(tv) _nothing_conv_func_end(tv, copyID)
-
-#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
- do { \
- list_unref(tv->vval.v_list); \
- tv->vval.v_list = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
- } while (0)
-
-#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
- do { \
- assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \
- dict_unref((dict_T *)dict); \
- *((dict_T **)&dict) = NULL; \
- if (tv != NULL) { \
- ((typval_T *)tv)->v_lock = VAR_UNLOCKED; \
- } \
- } while (0)
-
-static inline int _nothing_conv_real_list_after_start(
- typval_T *const tv, MPConvStackVal *const mpsv)
- FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- assert(tv != NULL);
- tv->v_lock = VAR_UNLOCKED;
- if (tv->vval.v_list->lv_refcount > 1) {
- tv->vval.v_list->lv_refcount--;
- tv->vval.v_list = NULL;
- mpsv->data.l.li = NULL;
- return OK;
+ dictitem_T *const ret = find_var_in_ht(ht, *name,
+ varname,
+ name_len - (size_t)(varname - name),
+ no_autoload || htp != NULL);
+ if (ret != NULL) {
+ return ret;
}
- return NOTDONE;
-}
-#define TYPVAL_ENCODE_CONV_LIST_START(tv, len)
-
-#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) \
- do { \
- if (_nothing_conv_real_list_after_start(tv, &mpsv) != NOTDONE) { \
- goto typval_encode_stop_converting_one_item; \
- } \
- } while (0)
-#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv)
-
-static inline void _nothing_conv_list_end(typval_T *const tv)
- FUNC_ATTR_ALWAYS_INLINE
-{
- if (tv == NULL) {
- return;
- }
- assert(tv->v_type == VAR_LIST);
- list_T *const list = tv->vval.v_list;
- list_unref(list);
- tv->vval.v_list = NULL;
+ // Search in parent scope for lambda
+ return find_var_in_scoped_ht(name, name_len, no_autoload || htp != NULL);
}
-#define TYPVAL_ENCODE_CONV_LIST_END(tv) _nothing_conv_list_end(tv)
-static inline int _nothing_conv_real_dict_after_start(
- typval_T *const tv, dict_T **const dictp, const void *const nodictvar,
- MPConvStackVal *const mpsv)
- FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- if (tv != NULL) {
- tv->v_lock = VAR_UNLOCKED;
- }
- if ((const void *)dictp != nodictvar && (*dictp)->dv_refcount > 1) {
- (*dictp)->dv_refcount--;
- *dictp = NULL;
- mpsv->data.d.todo = 0;
- return OK;
- }
- return NOTDONE;
-}
-#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len)
-
-#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) \
- do { \
- if (_nothing_conv_real_dict_after_start( \
- tv, (dict_T **)&dict, (void *)&TYPVAL_ENCODE_NODICT_VAR, \
- &mpsv) != NOTDONE) { \
- goto typval_encode_stop_converting_one_item; \
- } \
- } while (0)
-
-#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(tv, dict)
-#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict)
-#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict)
-
-static inline void _nothing_conv_dict_end(typval_T *const tv,
- dict_T **const dictp,
- const void *const nodictvar)
- FUNC_ATTR_ALWAYS_INLINE
-{
- if ((const void *)dictp != nodictvar) {
- dict_unref(*dictp);
- *dictp = NULL;
- }
-}
-#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \
- _nothing_conv_dict_end(tv, (dict_T **)&dict, \
- (void *)&TYPVAL_ENCODE_NODICT_VAR)
-
-#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type)
-
-#define TYPVAL_ENCODE_SCOPE static
-#define TYPVAL_ENCODE_NAME nothing
-#define TYPVAL_ENCODE_FIRST_ARG_TYPE const void *const
-#define TYPVAL_ENCODE_FIRST_ARG_NAME ignored
-#include "nvim/eval/typval_encode.c.h"
-#undef TYPVAL_ENCODE_SCOPE
-#undef TYPVAL_ENCODE_NAME
-#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
-#undef TYPVAL_ENCODE_FIRST_ARG_NAME
-
-#undef TYPVAL_ENCODE_ALLOW_SPECIALS
-#undef TYPVAL_ENCODE_CONV_NIL
-#undef TYPVAL_ENCODE_CONV_BOOL
-#undef TYPVAL_ENCODE_CONV_NUMBER
-#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
-#undef TYPVAL_ENCODE_CONV_FLOAT
-#undef TYPVAL_ENCODE_CONV_STRING
-#undef TYPVAL_ENCODE_CONV_STR_STRING
-#undef TYPVAL_ENCODE_CONV_EXT_STRING
-#undef TYPVAL_ENCODE_CONV_FUNC_START
-#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
-#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
-#undef TYPVAL_ENCODE_CONV_FUNC_END
-#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
-#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
-#undef TYPVAL_ENCODE_CONV_LIST_START
-#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
-#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
-#undef TYPVAL_ENCODE_CONV_LIST_END
-#undef TYPVAL_ENCODE_CONV_DICT_START
-#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
-#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
-#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
-#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
-#undef TYPVAL_ENCODE_CONV_DICT_END
-#undef TYPVAL_ENCODE_CONV_RECURSE
-
-/// Free memory for a variable value and set the value to NULL or 0
+/// Find variable in hashtab
///
-/// @param[in,out] varp Value to free.
-void clear_tv(typval_T *varp)
-{
- if (varp != NULL && varp->v_type != VAR_UNKNOWN) {
- const int evn_ret = encode_vim_to_nothing(varp, varp, "clear_tv argument");
- (void)evn_ret;
- assert(evn_ret == OK);
- }
-}
-
-/*
- * Set the value of a variable to NULL without freeing items.
- */
-static void init_tv(typval_T *varp)
-{
- if (varp != NULL)
- memset(varp, 0, sizeof(typval_T));
-}
-
-/*
- * Get the number value of a variable.
- * If it is a String variable, uses vim_str2nr().
- * For incompatible types, return 0.
- * get_tv_number_chk() is similar to get_tv_number(), but informs the
- * caller of incompatible types: it sets *denote to TRUE if "denote"
- * is not NULL or returns -1 otherwise.
- */
-long get_tv_number(typval_T *varp)
-{
- int error = FALSE;
-
- return get_tv_number_chk(varp, &error); /* return 0L on error */
-}
-
-long get_tv_number_chk(typval_T *varp, int *denote)
-{
- long n = 0L;
-
- switch (varp->v_type) {
- case VAR_NUMBER:
- return (long)(varp->vval.v_number);
- case VAR_FLOAT:
- EMSG(_("E805: Using a Float as a Number"));
- break;
- case VAR_FUNC:
- case VAR_PARTIAL:
- EMSG(_("E703: Using a Funcref as a Number"));
- break;
- case VAR_STRING:
- if (varp->vval.v_string != NULL) {
- vim_str2nr(varp->vval.v_string, NULL, NULL,
- STR2NR_ALL, &n, NULL, 0);
- }
- return n;
- case VAR_LIST:
- EMSG(_("E745: Using a List as a Number"));
- break;
- case VAR_DICT:
- EMSG(_("E728: Using a Dictionary as a Number"));
- break;
- case VAR_SPECIAL:
- switch (varp->vval.v_special) {
- case kSpecialVarTrue: {
- return 1;
- }
- case kSpecialVarFalse:
- case kSpecialVarNull: {
- return 0;
- }
- }
- break;
- case VAR_UNKNOWN:
- EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)");
- break;
- }
- if (denote == NULL) {
- // useful for values that must be unsigned
- n = -1;
- } else {
- *denote = true;
- }
- return n;
-}
-
-static float_T get_tv_float(typval_T *varp)
-{
- switch (varp->v_type) {
- case VAR_NUMBER:
- return (float_T)(varp->vval.v_number);
- case VAR_FLOAT:
- return varp->vval.v_float;
- break;
- case VAR_FUNC:
- case VAR_PARTIAL:
- EMSG(_("E891: Using a Funcref as a Float"));
- break;
- case VAR_STRING:
- EMSG(_("E892: Using a String as a Float"));
- break;
- case VAR_LIST:
- EMSG(_("E893: Using a List as a Float"));
- break;
- case VAR_DICT:
- EMSG(_("E894: Using a Dictionary as a Float"));
- break;
- default:
- EMSG2(_(e_intern2), "get_tv_float()");
- break;
- }
- return 0;
-}
-
-/*
- * Get the lnum from the first argument.
- * Also accepts ".", "$", etc., but that only works for the current buffer.
- * Returns -1 on error.
- */
-static linenr_T get_tv_lnum(typval_T *argvars)
-{
- typval_T rettv;
- linenr_T lnum;
-
- lnum = get_tv_number_chk(&argvars[0], NULL);
- if (lnum == 0) { /* no valid number, try using line() */
- rettv.v_type = VAR_NUMBER;
- f_line(argvars, &rettv, NULL);
- lnum = rettv.vval.v_number;
- clear_tv(&rettv);
- }
- return lnum;
-}
-
-/*
- * Get the lnum from the first argument.
- * Also accepts "$", then "buf" is used.
- * Returns 0 on error.
- */
-static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf)
-{
- if (argvars[0].v_type == VAR_STRING
- && argvars[0].vval.v_string != NULL
- && argvars[0].vval.v_string[0] == '$'
- && buf != NULL)
- return buf->b_ml.ml_line_count;
- return get_tv_number_chk(&argvars[0], NULL);
-}
-
-/*
- * Get the string value of a variable.
- * If it is a Number variable, the number is converted into a string.
- * get_tv_string() uses a single, static buffer. YOU CAN ONLY USE IT ONCE!
- * get_tv_string_buf() uses a given buffer.
- * If the String variable has never been set, return an empty string.
- * Never returns NULL;
- * get_tv_string_chk() and get_tv_string_buf_chk() are similar, but return
- * NULL on error.
- */
-static char_u *get_tv_string(const typval_T *varp)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
-{
- static char_u mybuf[NUMBUFLEN];
-
- return get_tv_string_buf(varp, mybuf);
-}
-
-static char_u *get_tv_string_buf(const typval_T *varp, char_u *buf)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
-{
- char_u *res = get_tv_string_buf_chk(varp, buf);
-
- return res != NULL ? res : (char_u *)"";
-}
-
-/// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE!
-char_u *get_tv_string_chk(const typval_T *varp)
- FUNC_ATTR_NONNULL_ALL
-{
- static char_u mybuf[NUMBUFLEN];
-
- return get_tv_string_buf_chk(varp, mybuf);
-}
-
-static char_u *get_tv_string_buf_chk(const typval_T *varp, char_u *buf)
- FUNC_ATTR_NONNULL_ALL
-{
- switch (varp->v_type) {
- case VAR_NUMBER:
- sprintf((char *)buf, "%" PRId64, (int64_t)varp->vval.v_number);
- return buf;
- case VAR_FUNC:
- case VAR_PARTIAL:
- EMSG(_("E729: using Funcref as a String"));
- break;
- case VAR_LIST:
- EMSG(_("E730: using List as a String"));
- break;
- case VAR_DICT:
- EMSG(_("E731: using Dictionary as a String"));
- break;
- case VAR_FLOAT:
- EMSG(_(e_float_as_string));
- break;
- case VAR_STRING:
- if (varp->vval.v_string != NULL)
- return varp->vval.v_string;
- return (char_u *)"";
- case VAR_SPECIAL:
- STRCPY(buf, encode_special_var_names[varp->vval.v_special]);
- return buf;
- case VAR_UNKNOWN:
- EMSG(_("E908: using an invalid value as a String"));
- break;
- }
- return NULL;
-}
-
-/*
- * Find variable "name" in the list of variables.
- * Return a pointer to it if found, NULL if not found.
- * Careful: "a:0" variables don't have a name.
- * When "htp" is not NULL we are writing to the variable, set "htp" to the
- * hashtab_T used.
- */
-static dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload)
-{
- char_u *varname;
- hashtab_T *ht;
-
- ht = find_var_ht(name, &varname);
- if (htp != NULL)
- *htp = ht;
- if (ht == NULL)
- return NULL;
- return find_var_in_ht(ht, *name, varname, no_autoload || htp != NULL);
-}
-
-/// Find variable "varname" in hashtab "ht" with name "htname".
-/// Returns NULL if not found.
-static dictitem_T *find_var_in_ht(hashtab_T *ht, int htname,
- char_u *varname, bool no_autoload)
+/// @param[in] ht Hashtab to find variable in.
+/// @param[in] htname Hashtab name (first character).
+/// @param[in] varname Variable name.
+/// @param[in] varname_len Variable name length.
+/// @param[in] no_autoload If true then autoload scripts will not be sourced
+/// if autoload variable was not found.
+///
+/// @return pointer to the dictionary item with the found variable or NULL if it
+/// was not found.
+static dictitem_T *find_var_in_ht(hashtab_T *const ht,
+ int htname,
+ const char *const varname,
+ const size_t varname_len,
+ int no_autoload)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
hashitem_T *hi;
- if (*varname == NUL) {
- /* Must be something like "s:", otherwise "ht" would be NULL. */
+ if (varname_len == 0) {
+ // Must be something like "s:", otherwise "ht" would be NULL.
switch (htname) {
- case 's': return &SCRIPT_SV(current_SID)->sv_var;
- case 'g': return &globvars_var;
- case 'v': return &vimvars_var;
- case 'b': return &curbuf->b_bufvar;
- case 'w': return &curwin->w_winvar;
- case 't': return &curtab->tp_winvar;
- case 'l': return current_funccal == NULL
- ? NULL : &current_funccal->l_vars_var;
- case 'a': return current_funccal == NULL
- ? NULL : &current_funccal->l_avars_var;
+ case 's': return (dictitem_T *)&SCRIPT_SV(current_SID)->sv_var;
+ case 'g': return (dictitem_T *)&globvars_var;
+ case 'v': return (dictitem_T *)&vimvars_var;
+ case 'b': return (dictitem_T *)&curbuf->b_bufvar;
+ case 'w': return (dictitem_T *)&curwin->w_winvar;
+ case 't': return (dictitem_T *)&curtab->tp_winvar;
+ case 'l': return (current_funccal == NULL
+ ? NULL : (dictitem_T *)&current_funccal->l_vars_var);
+ case 'a': return (current_funccal == NULL
+ ? NULL : (dictitem_T *)&current_funccal->l_avars_var);
}
return NULL;
}
- hi = hash_find(ht, varname);
+ hi = hash_find_len(ht, varname, varname_len);
if (HASHITEM_EMPTY(hi)) {
- /* For global variables we may try auto-loading the script. If it
- * worked find the variable again. Don't auto-load a script if it was
- * loaded already, otherwise it would be loaded every time when
- * checking if a function name is a Funcref variable. */
+ // For global variables we may try auto-loading the script. If it
+ // worked find the variable again. Don't auto-load a script if it was
+ // loaded already, otherwise it would be loaded every time when
+ // checking if a function name is a Funcref variable.
if (ht == &globvarht && !no_autoload) {
- /* Note: script_autoload() may make "hi" invalid. It must either
- * be obtained again or not used. */
- if (!script_autoload(varname, FALSE) || aborting())
+ // Note: script_autoload() may make "hi" invalid. It must either
+ // be obtained again or not used.
+ if (!script_autoload(varname, varname_len, false) || aborting()) {
return NULL;
- hi = hash_find(ht, varname);
+ }
+ hi = hash_find_len(ht, varname, varname_len);
}
- if (HASHITEM_EMPTY(hi))
+ if (HASHITEM_EMPTY(hi)) {
return NULL;
+ }
}
- return HI2DI(hi);
+ return TV_DICT_HI2DI(hi);
}
// Get function call environment based on backtrace debug level
@@ -19608,17 +18736,45 @@ static funccall_T *get_funccal(void)
return funccal;
}
-// Find the dict and hashtable used for a variable name. Set "varname" to the
-// start of name without ':'.
-static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d)
+/// Return the hashtable used for argument in the current funccal.
+/// Return NULL if there is no current funccal.
+static hashtab_T *get_funccal_args_ht(void)
{
- hashitem_T *hi;
+ if (current_funccal == NULL) {
+ return NULL;
+ }
+ return &get_funccal()->l_avars.dv_hashtab;
+}
+
+/// Return the hashtable used for local variables in the current funccal.
+/// Return NULL if there is no current funccal.
+static hashtab_T *get_funccal_local_ht(void)
+{
+ if (current_funccal == NULL) {
+ return NULL;
+ }
+ return &get_funccal()->l_vars.dv_hashtab;
+}
+
+/// Find the dict and hashtable used for a variable
+///
+/// @param[in] name Variable name, possibly with scope prefix.
+/// @param[in] name_len Variable name length.
+/// @param[out] varname Will be set to the start of the name without scope
+/// prefix.
+/// @param[out] d Scope dictionary.
+///
+/// @return Scope hashtab, NULL if name is not valid.
+static hashtab_T *find_var_ht_dict(const char *name, const size_t name_len,
+ const char **varname, dict_T **d)
+{
+ hashitem_T *hi;
*d = NULL;
- if (name[0] == NUL) {
+ if (name_len == 0) {
return NULL;
}
- if (name[1] != ':') {
+ if (name_len == 1 || (name_len >= 2 && name[1] != ':')) {
// name has implicit scope
if (name[0] == ':' || name[0] == AUTOLOAD_CHAR) {
// The name must not start with a colon or #.
@@ -19627,7 +18783,7 @@ static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d)
*varname = name;
// "version" is "v:version" in all scopes
- hi = hash_find(&compat_hashtab, name);
+ hi = hash_find_len(&compat_hashtab, name, name_len);
if (!HASHITEM_EMPTY(hi)) {
return &compat_hashtab;
}
@@ -19643,26 +18799,27 @@ static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d)
*varname = name + 2;
if (*name == 'g') { // global variable
*d = &globvardict;
- } else if (vim_strchr(name + 2, ':') != NULL
- || vim_strchr(name + 2, AUTOLOAD_CHAR) != NULL) {
+ } else if (name_len > 2
+ && (memchr(name + 2, ':', name_len - 2) != NULL
+ || memchr(name + 2, AUTOLOAD_CHAR, name_len - 2) != NULL)) {
// There must be no ':' or '#' in the rest of the name if g: was not used
return NULL;
}
- if (*name == 'b') { // buffer variable
+ if (*name == 'b') { // buffer variable
*d = curbuf->b_vars;
- } else if (*name == 'w') { // window variable
+ } else if (*name == 'w') { // window variable
*d = curwin->w_vars;
- } else if (*name == 't') { // tab page variable
+ } else if (*name == 't') { // tab page variable
*d = curtab->tp_vars;
- } else if (*name == 'v') { // v: variable
+ } else if (*name == 'v') { // v: variable
*d = &vimvardict;
} else if (*name == 'a' && current_funccal != NULL) { // function argument
*d = &get_funccal()->l_avars;
} else if (*name == 'l' && current_funccal != NULL) { // local variable
*d = &get_funccal()->l_vars;
- } else if (*name == 's' // script variable
- && current_SID > 0 && current_SID <= ga_scripts.ga_len) {
+ } else if (*name == 's' // script variable
+ && current_SID > 0 && current_SID <= ga_scripts.ga_len) {
*d = &SCRIPT_SV(current_SID)->sv_dict;
}
@@ -19670,28 +18827,35 @@ end:
return *d ? &(*d)->dv_hashtab : NULL;
}
-// Find the hashtab used for a variable name.
-// Return NULL if the name is not valid.
-// Set "varname" to the start of name without ':'.
-static hashtab_T *find_var_ht(uint8_t *name, uint8_t **varname)
+/// Find the hashtable used for a variable
+///
+/// @param[in] name Variable name, possibly with scope prefix.
+/// @param[in] name_len Variable name length.
+/// @param[out] varname Will be set to the start of the name without scope
+/// prefix.
+///
+/// @return Scope hashtab, NULL if name is not valid.
+static hashtab_T *find_var_ht(const char *name, const size_t name_len,
+ const char **varname)
{
dict_T *d;
- return find_var_ht_dict(name, varname, &d);
+ return find_var_ht_dict(name, name_len, varname, &d);
}
/*
* Get the string value of a (global/local) variable.
- * Note: see get_tv_string() for how long the pointer remains valid.
+ * Note: see tv_get_string() for how long the pointer remains valid.
* Returns NULL when it doesn't exist.
*/
-char_u *get_var_value(char_u *name)
+char_u *get_var_value(const char *const name)
{
dictitem_T *v;
- v = find_var(name, NULL, FALSE);
- if (v == NULL)
+ v = find_var(name, strlen(name), NULL, false);
+ if (v == NULL) {
return NULL;
- return get_tv_string(&v->di_tv);
+ }
+ return (char_u *)tv_get_string(&v->di_tv);
}
/*
@@ -19728,10 +18892,10 @@ void new_script_vars(scid_T id)
* Initialize dictionary "dict" as a scope and set variable "dict_var" to
* point to it.
*/
-void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope)
+void init_var_dict(dict_T *dict, ScopeDictDictItem *dict_var, int scope)
{
hash_init(&dict->dv_hashtab);
- dict->dv_lock = 0;
+ dict->dv_lock = VAR_UNLOCKED;
dict->dv_scope = scope;
dict->dv_refcount = DO_NOT_FREE_CNT;
dict->dv_copyID = 0;
@@ -19751,7 +18915,7 @@ void unref_var_dict(dict_T *dict)
/* Now the dict needs to be freed if no one else is using it, go back to
* normal reference counting. */
dict->dv_refcount -= DO_NOT_FREE_CNT - 1;
- dict_unref(dict);
+ tv_dict_unref(dict);
}
/*
@@ -19782,9 +18946,9 @@ static void vars_clear_ext(hashtab_T *ht, int free_val)
// Free the variable. Don't remove it from the hashtab,
// ht_array might change then. hash_clear() takes care of it
// later.
- v = HI2DI(hi);
+ v = TV_DICT_HI2DI(hi);
if (free_val) {
- clear_tv(&v->di_tv);
+ tv_clear(&v->di_tv);
}
if (v->di_flags & DI_FLAGS_ALLOC) {
xfree(v);
@@ -19801,38 +18965,37 @@ static void vars_clear_ext(hashtab_T *ht, int free_val)
*/
static void delete_var(hashtab_T *ht, hashitem_T *hi)
{
- dictitem_T *di = HI2DI(hi);
+ dictitem_T *di = TV_DICT_HI2DI(hi);
hash_remove(ht, hi);
- clear_tv(&di->di_tv);
+ tv_clear(&di->di_tv);
xfree(di);
}
/*
* List the value of one internal variable.
*/
-static void list_one_var(dictitem_T *v, char_u *prefix, int *first)
+static void list_one_var(dictitem_T *v, const char *prefix, int *first)
{
- char_u *s = (char_u *) encode_tv2echo(&v->di_tv, NULL);
- list_one_var_a(prefix, v->di_key, v->di_tv.v_type,
- s == NULL ? (char_u *)"" : s, first);
+ char *const s = encode_tv2echo(&v->di_tv, NULL);
+ list_one_var_a(prefix, (const char *)v->di_key, STRLEN(v->di_key),
+ v->di_tv.v_type, (s == NULL ? "" : s), first);
xfree(s);
}
-static void
-list_one_var_a (
- char_u *prefix,
- char_u *name,
- int type,
- char_u *string,
- int *first /* when TRUE clear rest of screen and set to FALSE */
-)
+/// @param[in] name_len Length of the name. May be -1, in this case strlen()
+/// will be used.
+/// @param[in,out] first When true clear rest of screen and set to false.
+static void list_one_var_a(const char *prefix, const char *name,
+ const ptrdiff_t name_len, const int type,
+ const char *string, int *first)
{
- /* don't use msg() or msg_attr() to avoid overwriting "v:statusmsg" */
+ // don't use msg() or msg_attr() to avoid overwriting "v:statusmsg"
msg_start();
msg_puts(prefix);
- if (name != NULL) /* "a:" vars don't have a name stored */
- msg_puts(name);
+ if (name != NULL) { // "a:" vars don't have a name stored
+ msg_puts_attr_len(name, name_len, 0);
+ }
msg_putchar(' ');
msg_advance(22);
if (type == VAR_NUMBER) {
@@ -19850,10 +19013,10 @@ list_one_var_a (
} else
msg_putchar(' ');
- msg_outtrans(string);
+ msg_outtrans((char_u *)string);
if (type == VAR_FUNC || type == VAR_PARTIAL) {
- msg_puts((char_u *)"()");
+ msg_puts("()");
}
if (*first) {
msg_clr_eos();
@@ -19861,46 +19024,47 @@ list_one_var_a (
}
}
-/*
- * Set variable "name" to value in "tv".
- * If the variable already exists, the value is updated.
- * Otherwise the variable is created.
- */
-static void
-set_var (
- char_u *name,
- typval_T *tv,
- int copy /* make copy of value in "tv" */
-)
+/// Set variable to the given value
+///
+/// If the variable already exists, the value is updated. Otherwise the variable
+/// is created.
+///
+/// @param[in] name Variable name to set.
+/// @param[in] name_len Length of the variable name.
+/// @param tv Variable value.
+/// @param[in] copy True if value in tv is to be copied.
+static void set_var(const char *name, const size_t name_len, typval_T *const tv,
+ const bool copy)
+ FUNC_ATTR_NONNULL_ALL
{
dictitem_T *v;
- char_u *varname;
hashtab_T *ht;
- typval_T oldtv;
dict_T *dict;
- ht = find_var_ht_dict(name, &varname, &dict);
- bool watched = is_watched(dict);
-
- if (watched) {
- init_tv(&oldtv);
- }
+ const char *varname;
+ ht = find_var_ht_dict(name, name_len, &varname, &dict);
+ const bool watched = tv_dict_is_watched(dict);
if (ht == NULL || *varname == NUL) {
EMSG2(_(e_illvar), name);
return;
}
- v = find_var_in_ht(ht, 0, varname, TRUE);
+ v = find_var_in_ht(ht, 0, varname, name_len - (size_t)(varname - name), true);
- if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
- && var_check_func_name(name, v == NULL)) {
+ // Search in parent scope which is possible to reference from lambda
+ if (v == NULL) {
+ v = find_var_in_scoped_ht((const char *)name, name_len, true);
+ }
+
+ if (tv_is_func(*tv) && !var_check_func_name(name, v == NULL)) {
return;
}
+ typval_T oldtv = TV_INITIAL_VALUE;
if (v != NULL) {
// existing variable, need to clear the value
- if (var_check_ro(v->di_flags, name, false)
- || tv_check_lock(v->di_tv.v_lock, name, false)) {
+ if (var_check_ro(v->di_flags, name, name_len)
+ || tv_check_lock(v->di_tv.v_lock, name, name_len)) {
return;
}
@@ -19909,19 +19073,19 @@ set_var (
if (ht == &vimvarht) {
if (v->di_tv.v_type == VAR_STRING) {
xfree(v->di_tv.vval.v_string);
- if (copy || tv->v_type != VAR_STRING)
- v->di_tv.vval.v_string = vim_strsave(get_tv_string(tv));
- else {
+ if (copy || tv->v_type != VAR_STRING) {
+ v->di_tv.vval.v_string = (char_u *)xstrdup(tv_get_string(tv));
+ } else {
// Take over the string to avoid an extra alloc/free.
v->di_tv.vval.v_string = tv->vval.v_string;
tv->vval.v_string = NULL;
}
return;
} else if (v->di_tv.v_type == VAR_NUMBER) {
- v->di_tv.vval.v_number = get_tv_number(tv);
- if (STRCMP(varname, "searchforward") == 0)
+ v->di_tv.vval.v_number = tv_get_number(tv);
+ if (strcmp(varname, "searchforward") == 0) {
set_search_direction(v->di_tv.vval.v_number ? '/' : '?');
- else if (STRCMP(varname, "hlsearch") == 0) {
+ } else if (strcmp(varname, "hlsearch") == 0) {
no_hlsearch = !v->di_tv.vval.v_number;
redraw_all_later(SOME_VALID);
}
@@ -19932,23 +19096,24 @@ set_var (
}
if (watched) {
- copy_tv(&v->di_tv, &oldtv);
+ tv_copy(&v->di_tv, &oldtv);
}
- clear_tv(&v->di_tv);
- } else { /* add a new variable */
- /* Can't add "v:" variable. */
+ tv_clear(&v->di_tv);
+ } else { // Add a new variable.
+ // Can't add "v:" variable.
if (ht == &vimvarht) {
- EMSG2(_(e_illvar), name);
+ emsgf(_(e_illvar), name);
return;
}
- /* Make sure the variable name is valid. */
- if (!valid_varname(varname))
+ // Make sure the variable name is valid.
+ if (!valid_varname(varname)) {
return;
+ }
- v = xmalloc(sizeof(dictitem_T) + STRLEN(varname));
+ v = xmalloc(sizeof(dictitem_T) + strlen(varname));
STRCPY(v->di_key, varname);
- if (hash_add(ht, DI2HIKEY(v)) == FAIL) {
+ if (tv_dict_add(dict, v) == FAIL) {
xfree(v);
return;
}
@@ -19956,167 +19121,153 @@ set_var (
}
if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) {
- copy_tv(tv, &v->di_tv);
+ tv_copy(tv, &v->di_tv);
} else {
v->di_tv = *tv;
v->di_tv.v_lock = 0;
- init_tv(tv);
+ tv_init(tv);
}
if (watched) {
if (oldtv.v_type == VAR_UNKNOWN) {
- dictwatcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL);
+ tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL);
} else {
- dictwatcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv);
- clear_tv(&oldtv);
+ tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv);
+ tv_clear(&oldtv);
}
}
}
-// Return true if di_flags "flags" indicates variable "name" is read-only.
-// Also give an error message.
-static bool var_check_ro(int flags, char_u *name, bool use_gettext)
+/// Check whether variable is read-only (DI_FLAGS_RO, DI_FLAGS_RO_SBX)
+///
+/// Also gives an error message.
+///
+/// @param[in] flags di_flags attribute value.
+/// @param[in] name Variable name, for use in error message.
+/// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate
+/// variable name and compute the length. Use #TV_CSTRING
+/// to compute the length with strlen() without
+/// translating.
+///
+/// Both #TV_… values are used for optimization purposes:
+/// variable name with its length is needed only in case
+/// of error, when no error occurs computing them is
+/// a waste of CPU resources. This especially applies to
+/// gettext.
+///
+/// @return True if variable is read-only: either always or in sandbox when
+/// sandbox is enabled, false otherwise.
+bool var_check_ro(const int flags, const char *name,
+ size_t name_len)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
+ const char *error_message = NULL;
if (flags & DI_FLAGS_RO) {
- EMSG2(_(e_readonlyvar), use_gettext ? (char_u *)_(name) : name);
- return true;
+ error_message = N_(e_readonlyvar);
+ } else if ((flags & DI_FLAGS_RO_SBX) && sandbox) {
+ error_message = N_("E794: Cannot set variable in the sandbox: \"%.*s\"");
}
- if ((flags & DI_FLAGS_RO_SBX) && sandbox) {
- EMSG2(_(e_readonlysbx), use_gettext ? (char_u *)_(name) : name);
- return true;
+
+ if (error_message == NULL) {
+ return false;
}
- return false;
+ if (name_len == TV_TRANSLATE) {
+ name = _(name);
+ name_len = strlen(name);
+ } else if (name_len == TV_CSTRING) {
+ name_len = strlen(name);
+ }
+
+ emsgf(_(error_message), (int)name_len, name);
+
+ return true;
}
-// Return true if di_flags "flags" indicates variable "name" is fixed.
-// Also give an error message.
-static bool var_check_fixed(int flags, char_u *name, bool use_gettext)
+/// Check whether variable is fixed (DI_FLAGS_FIX)
+///
+/// Also gives an error message.
+///
+/// @param[in] flags di_flags attribute value.
+/// @param[in] name Variable name, for use in error message.
+/// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate
+/// variable name and compute the length. Use #TV_CSTRING
+/// to compute the length with strlen() without
+/// translating.
+///
+/// Both #TV_… values are used for optimization purposes:
+/// variable name with its length is needed only in case
+/// of error, when no error occurs computing them is
+/// a waste of CPU resources. This especially applies to
+/// gettext.
+///
+/// @return True if variable is fixed, false otherwise.
+static bool var_check_fixed(const int flags, const char *name,
+ size_t name_len)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
if (flags & DI_FLAGS_FIX) {
- EMSG2(_("E795: Cannot delete variable %s"),
- use_gettext ? (char_u *)_(name) : name);
+ if (name_len == TV_TRANSLATE) {
+ name = _(name);
+ name_len = strlen(name);
+ } else if (name_len == TV_CSTRING) {
+ name_len = strlen(name);
+ }
+ emsgf(_("E795: Cannot delete variable %.*s"), (int)name_len, name);
return true;
}
return false;
}
-/*
- * Check if a funcref is assigned to a valid variable name.
- * Return TRUE and give an error if not.
- */
-static int
-var_check_func_name (
- char_u *name, /* points to start of variable name */
- int new_var /* TRUE when creating the variable */
-)
+// TODO(ZyX-I): move to eval/expressions
+
+/// Check if name is a valid name to assign funcref to
+///
+/// @param[in] name Possible function/funcref name.
+/// @param[in] new_var True if it is a name for a variable.
+///
+/// @return false in case of error, true in case of success. Also gives an
+/// error message if appropriate.
+bool var_check_func_name(const char *const name, const bool new_var)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
// Allow for w: b: s: and t:.
if (!(vim_strchr((char_u *)"wbst", name[0]) != NULL && name[1] == ':')
&& !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') ? name[2]
: name[0])) {
EMSG2(_("E704: Funcref variable name must start with a capital: %s"), name);
- return TRUE;
+ return false;
}
- /* Don't allow hiding a function. When "v" is not NULL we might be
- * assigning another function to the same var, the type is checked
- * below. */
- if (new_var && function_exists(name)) {
+ // Don't allow hiding a function. When "v" is not NULL we might be
+ // assigning another function to the same var, the type is checked
+ // below.
+ if (new_var && function_exists((const char *)name, false)) {
EMSG2(_("E705: Variable name conflicts with existing function: %s"),
- name);
- return TRUE;
+ name);
+ return false;
}
- return FALSE;
+ return true;
}
-/*
- * Check if a variable name is valid.
- * Return FALSE and give an error if not.
- */
-static int valid_varname(char_u *varname)
-{
- char_u *p;
+// TODO(ZyX-I): move to eval/expressions
- for (p = varname; *p != NUL; ++p)
- if (!eval_isnamec1(*p) && (p == varname || !ascii_isdigit(*p))
+/// Check if a variable name is valid
+///
+/// @param[in] varname Variable name to check.
+///
+/// @return false when variable name is not valid, true when it is. Also gives
+/// an error message if appropriate.
+bool valid_varname(const char *varname)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ for (const char *p = varname; *p != NUL; p++) {
+ if (!eval_isnamec1((int)(uint8_t)(*p))
+ && (p == varname || !ascii_isdigit(*p))
&& *p != AUTOLOAD_CHAR) {
- EMSG2(_(e_illvar), varname);
- return FALSE;
+ emsgf(_(e_illvar), varname);
+ return false;
}
- return TRUE;
-}
-
-// Return true if typeval "tv" is set to be locked (immutable).
-// Also give an error message, using "name" or _("name") when use_gettext is
-// true.
-static bool tv_check_lock(int lock, char_u *name, bool use_gettext)
-{
- if (lock & VAR_LOCKED) {
- EMSG2(_("E741: Value is locked: %s"),
- name == NULL
- ? (char_u *)_("Unknown")
- : use_gettext ? (char_u *)_(name)
- : name);
- return true;
- }
- if (lock & VAR_FIXED) {
- EMSG2(_("E742: Cannot change value of %s"),
- name == NULL
- ? (char_u *)_("Unknown")
- : use_gettext ? (char_u *)_(name)
- : name);
- return true;
- }
- return false;
-}
-
-/*
- * Copy the values from typval_T "from" to typval_T "to".
- * When needed allocates string or increases reference count.
- * Does not make a copy of a list or dict but copies the reference!
- * It is OK for "from" and "to" to point to the same item. This is used to
- * make a copy later.
- */
-void copy_tv(typval_T *from, typval_T *to)
-{
- to->v_type = from->v_type;
- to->v_lock = 0;
- memmove(&to->vval, &from->vval, sizeof(to->vval));
- switch (from->v_type) {
- case VAR_NUMBER:
- case VAR_FLOAT:
- case VAR_SPECIAL:
- break;
- case VAR_STRING:
- case VAR_FUNC:
- if (from->vval.v_string != NULL) {
- to->vval.v_string = vim_strsave(from->vval.v_string);
- if (from->v_type == VAR_FUNC) {
- func_ref(to->vval.v_string);
- }
- }
- break;
- case VAR_PARTIAL:
- if (from->vval.v_partial == NULL) {
- to->vval.v_partial = NULL;
- } else {
- to->vval.v_partial = from->vval.v_partial;
- (to->vval.v_partial->pt_refcount)++;
- }
- break;
- case VAR_LIST:
- if (from->vval.v_list != NULL) {
- to->vval.v_list->lv_refcount++;
- }
- break;
- case VAR_DICT:
- if (from->vval.v_dict != NULL) {
- to->vval.v_dict->dv_refcount++;
- }
- break;
- case VAR_UNKNOWN:
- EMSG2(_(e_intern2), "copy_tv(UNKNOWN)");
- break;
}
+ return true;
}
/// Make a copy of an item
@@ -20134,7 +19285,7 @@ void copy_tv(typval_T *from, typval_T *to)
/// list[1]`) var_item_copy with zero copyID will emit
/// a copy with (`copy[0] isnot copy[1]`), with non-zero it
/// will emit a copy with (`copy[0] is copy[1]`) like in the
-/// original list. Not use when deep is false.
+/// original list. Not used when deep is false.
int var_item_copy(const vimconv_T *const conv,
typval_T *const from,
typval_T *const to,
@@ -20157,11 +19308,12 @@ int var_item_copy(const vimconv_T *const conv,
case VAR_FUNC:
case VAR_PARTIAL:
case VAR_SPECIAL:
- copy_tv(from, to);
+ tv_copy(from, to);
break;
case VAR_STRING:
- if (conv == NULL || conv->vc_type == CONV_NONE) {
- copy_tv(from, to);
+ if (conv == NULL || conv->vc_type == CONV_NONE
+ || from->vval.v_string == NULL) {
+ tv_copy(from, to);
} else {
to->v_type = VAR_STRING;
to->v_lock = 0;
@@ -20183,10 +19335,11 @@ int var_item_copy(const vimconv_T *const conv,
to->vval.v_list = from->vval.v_list->lv_copylist;
++to->vval.v_list->lv_refcount;
} else {
- to->vval.v_list = list_copy(conv, from->vval.v_list, deep, copyID);
+ to->vval.v_list = tv_list_copy(conv, from->vval.v_list, deep, copyID);
}
- if (to->vval.v_list == NULL)
+ if (to->vval.v_list == NULL && from->vval.v_list != NULL) {
ret = FAIL;
+ }
break;
case VAR_DICT:
to->v_type = VAR_DICT;
@@ -20198,10 +19351,11 @@ int var_item_copy(const vimconv_T *const conv,
to->vval.v_dict = from->vval.v_dict->dv_copydict;
++to->vval.v_dict->dv_refcount;
} else {
- to->vval.v_dict = dict_copy(conv, from->vval.v_dict, deep, copyID);
+ to->vval.v_dict = tv_dict_copy(conv, from->vval.v_dict, deep, copyID);
}
- if (to->vval.v_dict == NULL)
+ if (to->vval.v_dict == NULL && from->vval.v_dict != NULL) {
ret = FAIL;
+ }
break;
case VAR_UNKNOWN:
EMSG2(_(e_intern2), "var_item_copy(UNKNOWN)");
@@ -20220,7 +19374,6 @@ void ex_echo(exarg_T *eap)
{
char_u *arg = eap->arg;
typval_T rettv;
- char_u *p;
bool needclr = true;
bool atstart = true;
@@ -20231,19 +19384,20 @@ void ex_echo(exarg_T *eap)
* still need to be cleared. E.g., "echo 22,44". */
need_clr_eos = needclr;
- p = arg;
- if (eval1(&arg, &rettv, !eap->skip) == FAIL) {
- /*
- * Report the invalid expression unless the expression evaluation
- * has been cancelled due to an aborting error, an interrupt, or an
- * exception.
- */
- if (!aborting())
- EMSG2(_(e_invexpr2), p);
- need_clr_eos = FALSE;
- break;
+ {
+ char_u *p = arg;
+ if (eval1(&arg, &rettv, !eap->skip) == FAIL) {
+ // Report the invalid expression unless the expression evaluation
+ // has been cancelled due to an aborting error, an interrupt, or an
+ // exception.
+ if (!aborting()) {
+ EMSG2(_(e_invexpr2), p);
+ }
+ need_clr_eos = false;
+ break;
+ }
+ need_clr_eos = false;
}
- need_clr_eos = FALSE;
if (!eap->skip) {
if (atstart) {
@@ -20257,9 +19411,11 @@ void ex_echo(exarg_T *eap)
msg_sb_eol();
msg_start();
}
- } else if (eap->cmdidx == CMD_echo)
- msg_puts_attr((char_u *)" ", echo_attr);
- char_u *tofree = p = (char_u *) encode_tv2echo(&rettv, NULL);
+ } else if (eap->cmdidx == CMD_echo) {
+ msg_puts_attr(" ", echo_attr);
+ }
+ char *tofree = encode_tv2echo(&rettv, NULL);
+ const char *p = tofree;
if (p != NULL) {
for (; *p != NUL && !got_int; ++p) {
if (*p == '\n' || *p == '\r' || *p == TAB) {
@@ -20268,21 +19424,22 @@ void ex_echo(exarg_T *eap)
msg_clr_eos();
needclr = false;
}
- msg_putchar_attr(*p, echo_attr);
+ msg_putchar_attr((uint8_t)(*p), echo_attr);
} else {
if (has_mbyte) {
- int i = (*mb_ptr2len)(p);
+ int i = (*mb_ptr2len)((const char_u *)p);
- (void)msg_outtrans_len_attr(p, i, echo_attr);
+ (void)msg_outtrans_len_attr((char_u *)p, i, echo_attr);
p += i - 1;
- } else
- (void)msg_outtrans_len_attr(p, 1, echo_attr);
+ } else {
+ (void)msg_outtrans_len_attr((char_u *)p, 1, echo_attr);
+ }
}
}
}
xfree(tofree);
}
- clear_tv(&rettv);
+ tv_clear(&rettv);
arg = skipwhite(arg);
}
eap->nextcmd = check_nextcmd(arg);
@@ -20326,7 +19483,6 @@ void ex_execute(exarg_T *eap)
int ret = OK;
char_u *p;
garray_T ga;
- int len;
int save_did_emsg;
ga_init(&ga, 1, 80);
@@ -20348,16 +19504,17 @@ void ex_execute(exarg_T *eap)
}
if (!eap->skip) {
- p = get_tv_string(&rettv);
- len = (int)STRLEN(p);
+ const char *const argstr = tv_get_string(&rettv);
+ const size_t len = strlen(argstr);
ga_grow(&ga, len + 2);
- if (!GA_EMPTY(&ga))
+ if (!GA_EMPTY(&ga)) {
((char_u *)(ga.ga_data))[ga.ga_len++] = ' ';
- STRCPY((char_u *)(ga.ga_data) + ga.ga_len, p);
+ }
+ memcpy((char_u *)(ga.ga_data) + ga.ga_len, argstr, len + 1);
ga.ga_len += len;
}
- clear_tv(&rettv);
+ tv_clear(&rettv);
arg = skipwhite(arg);
}
@@ -20390,9 +19547,9 @@ void ex_execute(exarg_T *eap)
* Returns NULL when no option name found. Otherwise pointer to the char
* after the option name.
*/
-static char_u *find_option_end(char_u **arg, int *opt_flags)
+static const char *find_option_end(const char **const arg, int *const opt_flags)
{
- char_u *p = *arg;
+ const char *p = *arg;
++p;
if (*p == 'g' && p[1] == ':') {
@@ -20401,18 +19558,22 @@ static char_u *find_option_end(char_u **arg, int *opt_flags)
} else if (*p == 'l' && p[1] == ':') {
*opt_flags = OPT_LOCAL;
p += 2;
- } else
+ } else {
*opt_flags = 0;
+ }
- if (!ASCII_ISALPHA(*p))
+ if (!ASCII_ISALPHA(*p)) {
return NULL;
+ }
*arg = p;
- if (p[0] == 't' && p[1] == '_' && p[2] != NUL && p[3] != NUL)
- p += 4; /* termcap option */
- else
- while (ASCII_ISALPHA(*p))
- ++p;
+ if (p[0] == 't' && p[1] == '_' && p[2] != NUL && p[3] != NUL) {
+ p += 4; // t_xx/termcap option
+ } else {
+ while (ASCII_ISALPHA(*p)) {
+ p++;
+ }
+ }
return p;
}
@@ -20431,10 +19592,10 @@ void ex_function(exarg_T *eap)
char_u *line_arg = NULL;
garray_T newargs;
garray_T newlines;
- int varargs = FALSE;
- int mustend = FALSE;
+ int varargs = false;
int flags = 0;
ufunc_T *fp;
+ bool overwrite = false;
int indent;
int nesting;
char_u *skip_until = NULL;
@@ -20457,8 +19618,9 @@ void ex_function(exarg_T *eap)
if (!HASHITEM_EMPTY(hi)) {
--todo;
fp = HI2UF(hi);
- if (!isdigit(*fp->uf_name))
- list_func_head(fp, FALSE);
+ if (!func_name_refcount(fp->uf_name)) {
+ list_func_head(fp, false);
+ }
}
}
}
@@ -20524,8 +19686,9 @@ void ex_function(exarg_T *eap)
* interrupt, or an exception.
*/
if (!aborting()) {
- if (!eap->skip && fudi.fd_newkey != NULL)
+ if (fudi.fd_newkey != NULL) {
EMSG2(_(e_dictkey), fudi.fd_newkey);
+ }
xfree(fudi.fd_newkey);
return;
} else
@@ -20567,7 +19730,7 @@ void ex_function(exarg_T *eap)
}
if (!got_int) {
msg_putchar('\n');
- msg_puts((char_u *)" endfunction");
+ msg_puts(" endfunction");
}
} else
emsg_funcname(N_("E123: Undefined function: %s"), name);
@@ -20600,9 +19763,7 @@ void ex_function(exarg_T *eap)
arg = name;
else
arg = fudi.fd_newkey;
- if (arg != NULL && (fudi.fd_di == NULL
- || (fudi.fd_di->di_tv.v_type != VAR_FUNC
- && fudi.fd_di->di_tv.v_type != VAR_PARTIAL))) {
+ if (arg != NULL && (fudi.fd_di == NULL || !tv_is_func(fudi.fd_di->di_tv))) {
int j = (*arg == K_SPECIAL) ? 3 : 0;
while (arg[j] != NUL && (j == 0 ? eval_isnamec1(arg[j])
: eval_isnamec(arg[j])))
@@ -20615,59 +19776,11 @@ void ex_function(exarg_T *eap)
EMSG(_("E862: Cannot use g: here"));
}
- /*
- * Isolate the arguments: "arg1, arg2, ...)"
- */
- while (*p != ')') {
- if (p[0] == '.' && p[1] == '.' && p[2] == '.') {
- varargs = TRUE;
- p += 3;
- mustend = TRUE;
- } else {
- arg = p;
- while (ASCII_ISALNUM(*p) || *p == '_')
- ++p;
- if (arg == p || isdigit(*arg)
- || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0)
- || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0)) {
- if (!eap->skip)
- EMSG2(_("E125: Illegal argument: %s"), arg);
- break;
- }
- ga_grow(&newargs, 1);
- c = *p;
- *p = NUL;
- arg = vim_strsave(arg);
-
- /* Check for duplicate argument name. */
- for (int i = 0; i < newargs.ga_len; ++i)
- if (STRCMP(((char_u **)(newargs.ga_data))[i], arg) == 0) {
- EMSG2(_("E853: Duplicate argument name: %s"), arg);
- xfree(arg);
- goto erret;
- }
-
- ((char_u **)(newargs.ga_data))[newargs.ga_len] = arg;
- *p = c;
- newargs.ga_len++;
- if (*p == ',')
- ++p;
- else
- mustend = TRUE;
- }
- p = skipwhite(p);
- if (mustend && *p != ')') {
- if (!eap->skip)
- EMSG2(_(e_invarg2), eap->arg);
- break;
- }
- }
- if (*p != ')') {
- goto erret;
+ if (get_function_args(&p, ')', &newargs, &varargs, eap->skip) == FAIL) {
+ goto errret_2;
}
- ++p; // skip the ')'
- /* find extra arguments "range", "dict" and "abort" */
+ // find extra arguments "range", "dict", "abort" and "closure"
for (;; ) {
p = skipwhite(p);
if (STRNCMP(p, "range", 5) == 0) {
@@ -20679,16 +19792,28 @@ void ex_function(exarg_T *eap)
} else if (STRNCMP(p, "abort", 5) == 0) {
flags |= FC_ABORT;
p += 5;
- } else
+ } else if (STRNCMP(p, "closure", 7) == 0) {
+ flags |= FC_CLOSURE;
+ p += 7;
+ if (current_funccal == NULL) {
+ emsg_funcname(N_
+ ("E932: Closure function should not be at top level: %s"),
+ name == NULL ? (char_u *)"" : name);
+ goto erret;
+ }
+ } else {
break;
+ }
}
/* When there is a line break use what follows for the function body.
* Makes 'exe "func Test()\n...\nendfunc"' work. */
- if (*p == '\n')
+ const char *const end = (const char *)p + STRLEN(p);
+ if (*p == '\n') {
line_arg = p + 1;
- else if (*p != NUL && *p != '"' && !eap->skip && !did_emsg)
- EMSG(_(e_trailing));
+ } else if (*p != NUL && *p != '"' && !eap->skip && !did_emsg) {
+ emsgf(_(e_trailing));
+ }
/*
* Read the body of the function, until ":endfunction" is found.
@@ -20762,8 +19887,30 @@ void ex_function(exarg_T *eap)
/* Check for "endfunction". */
if (checkforcmd(&p, "endfunction", 4) && nesting-- == 0) {
- if (line_arg == NULL)
+ if (*p == '!') {
+ p++;
+ }
+ const char *const comment_start = strchr((const char *)p, '"');
+ const char *const endfunc_end = (comment_start
+ ? strchr(comment_start, '\n')
+ : strpbrk((const char *)p, "\n|"));
+ p = (endfunc_end
+ ? (char_u *)endfunc_end
+ : p + STRLEN(p));
+ if (*p == '|') {
+ emsgf(_(e_trailing2), p);
+ if (line_arg == NULL) {
+ xfree(theline);
+ }
+ goto erret;
+ }
+ if (line_arg == NULL) {
xfree(theline);
+ } else {
+ if ((const char *)p < end) {
+ eap->nextcmd = p + 1;
+ }
+ }
break;
}
@@ -20782,7 +19929,7 @@ void ex_function(exarg_T *eap)
if (*p == '!') {
p = skipwhite(p + 1);
}
- p += eval_fname_script(p);
+ p += eval_fname_script((const char *)p);
xfree(trans_function_name(&p, true, 0, NULL, NULL));
if (*skipwhite(p) == '(') {
nesting++;
@@ -20790,9 +19937,15 @@ void ex_function(exarg_T *eap)
}
}
- /* Check for ":append" or ":insert". */
+ // Check for ":append", ":change", ":insert".
p = skip_range(p, NULL);
if ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p'))
+ || (p[0] == 'c'
+ && (!ASCII_ISALPHA(p[1])
+ || (p[1] == 'h' && (!ASCII_ISALPHA(p[2])
+ || (p[2] == 'a'
+ && (STRNCMP(&p[3], "nge", 3) != 0
+ || !ASCII_ISALPHA(p[6])))))))
|| (p[0] == 'i'
&& (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
&& (!ASCII_ISALPHA(p[2])
@@ -20857,7 +20010,7 @@ void ex_function(exarg_T *eap)
* If there are no errors, add the function
*/
if (fudi.fd_dict == NULL) {
- v = find_var(name, &ht, FALSE);
+ v = find_var((const char *)name, STRLEN(name), &ht, false);
if (v != NULL && v->di_tv.v_type == VAR_FUNC) {
emsg_funcname(N_("E707: Function name conflicts with variable: %s"),
name);
@@ -20870,16 +20023,25 @@ void ex_function(exarg_T *eap)
emsg_funcname(e_funcexts, name);
goto erret;
}
- if (fp->uf_refcount > 1 || fp->uf_calls > 0) {
+ if (fp->uf_calls > 0) {
emsg_funcname(N_("E127: Cannot redefine function %s: It is in use"),
name);
goto erret;
}
- /* redefine existing function */
- ga_clear_strings(&(fp->uf_args));
- ga_clear_strings(&(fp->uf_lines));
- xfree(name);
- name = NULL;
+ if (fp->uf_refcount > 1) {
+ // This function is referenced somewhere, don't redefine it but
+ // create a new one.
+ (fp->uf_refcount)--;
+ fp->uf_flags |= FC_REMOVED;
+ fp = NULL;
+ overwrite = true;
+ } else {
+ // redefine existing function
+ ga_clear_strings(&(fp->uf_args));
+ ga_clear_strings(&(fp->uf_lines));
+ xfree(name);
+ name = NULL;
+ }
}
} else {
char numbuf[20];
@@ -20890,11 +20052,13 @@ void ex_function(exarg_T *eap)
goto erret;
}
if (fudi.fd_di == NULL) {
- if (tv_check_lock(fudi.fd_dict->dv_lock, eap->arg, false)) {
+ if (tv_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg,
+ TV_CSTRING)) {
// Can't add a function to a locked dictionary
goto erret;
}
- } else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, false)) {
+ } else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg,
+ TV_CSTRING)) {
// Can't change an existing function if it is locked
goto erret;
}
@@ -20914,7 +20078,7 @@ void ex_function(exarg_T *eap)
/* Check that the autoload name matches the script name. */
int j = FAIL;
if (sourcing_name != NULL) {
- scriptname = autoload_name(name);
+ scriptname = (char_u *)autoload_name((const char *)name, STRLEN(name));
p = vim_strchr(scriptname, '/');
plen = (int)STRLEN(p);
slen = (int)STRLEN(sourcing_name);
@@ -20931,20 +20095,21 @@ void ex_function(exarg_T *eap)
}
}
- fp = xmalloc(sizeof(ufunc_T) + STRLEN(name));
+ fp = xcalloc(1, sizeof(ufunc_T) + STRLEN(name));
if (fudi.fd_dict != NULL) {
if (fudi.fd_di == NULL) {
- /* add new dict entry */
- fudi.fd_di = dictitem_alloc(fudi.fd_newkey);
- if (dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) {
+ // Add new dict entry
+ fudi.fd_di = tv_dict_item_alloc((const char *)fudi.fd_newkey);
+ if (tv_dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) {
xfree(fudi.fd_di);
xfree(fp);
goto erret;
}
- } else
- /* overwrite existing dict entry */
- clear_tv(&fudi.fd_di->di_tv);
+ } else {
+ // Overwrite existing dict entry.
+ tv_clear(&fudi.fd_di->di_tv);
+ }
fudi.fd_di->di_tv.v_type = VAR_FUNC;
fudi.fd_di->di_tv.v_lock = 0;
fudi.fd_di->di_tv.vval.v_string = vim_strsave(name);
@@ -20955,14 +20120,22 @@ void ex_function(exarg_T *eap)
/* insert the new function in the function list */
STRCPY(fp->uf_name, name);
- if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) {
- xfree(fp);
- goto erret;
+ if (overwrite) {
+ hi = hash_find(&func_hashtab, name);
+ hi->hi_key = UF2HIKEY(fp);
+ } else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) {
+ xfree(fp);
+ goto erret;
}
+ fp->uf_refcount = 1;
}
- fp->uf_refcount = 1;
fp->uf_args = newargs;
fp->uf_lines = newlines;
+ if ((flags & FC_CLOSURE) != 0) {
+ register_closure(fp);
+ } else {
+ fp->uf_scoped = NULL;
+ }
fp->uf_tml_count = NULL;
fp->uf_tml_total = NULL;
fp->uf_tml_self = NULL;
@@ -20977,6 +20150,7 @@ void ex_function(exarg_T *eap)
erret:
ga_clear_strings(&newargs);
+errret_2:
ga_clear_strings(&newlines);
ret_free:
xfree(skip_until);
@@ -20986,30 +20160,29 @@ ret_free:
need_wait_return |= saved_wait_return;
}
-/*
- * Get a function name, translating "<SID>" and "<SNR>".
- * Also handles a Funcref in a List or Dictionary.
- * Returns the function name in allocated memory, or NULL for failure.
- * flags:
- * TFN_INT: internal function name OK
- * TFN_QUIET: be quiet
- * TFN_NO_AUTOLOAD: do not use script autoloading
- * Advances "pp" to just after the function name (if no error).
- */
+/// Get a function name, translating "<SID>" and "<SNR>".
+/// Also handles a Funcref in a List or Dictionary.
+/// flags:
+/// TFN_INT: internal function name OK
+/// TFN_QUIET: be quiet
+/// TFN_NO_AUTOLOAD: do not use script autoloading
+/// TFN_NO_DEREF: do not dereference a Funcref
+/// Advances "pp" to just after the function name (if no error).
+///
+/// @return the function name in allocated memory, or NULL for failure.
static char_u *
-trans_function_name (
+trans_function_name(
char_u **pp,
- int skip, /* only find the end, don't evaluate */
+ int skip, // only find the end, don't evaluate
int flags,
funcdict_T *fdp, // return: info about dictionary used
partial_T **partial // return: partial of a FuncRef
)
{
char_u *name = NULL;
- char_u *start;
- char_u *end;
+ const char_u *start;
+ const char_u *end;
int lead;
- char_u sid_buf[20];
int len;
lval_T lv;
@@ -21022,19 +20195,20 @@ trans_function_name (
if ((*pp)[0] == K_SPECIAL && (*pp)[1] == KS_EXTRA
&& (*pp)[2] == (int)KE_SNR) {
*pp += 3;
- len = get_id_len(pp) + 3;
- return vim_strnsave(start, len);
+ len = get_id_len((const char **)pp) + 3;
+ return (char_u *)xmemdupz(start, len);
}
/* A name starting with "<SID>" or "<SNR>" is local to a script. But
* don't skip over "s:", get_lval() needs it for "s:dict.func". */
- lead = eval_fname_script(start);
- if (lead > 2)
+ lead = eval_fname_script((const char *)start);
+ if (lead > 2) {
start += lead;
+ }
- /* Note that TFN_ flags use the same values as GLV_ flags. */
- end = get_lval(start, NULL, &lv, FALSE, skip, flags,
- lead > 2 ? 0 : FNE_CHECK_START);
+ // Note that TFN_ flags use the same values as GLV_ flags.
+ end = get_lval((char_u *)start, NULL, &lv, false, skip, flags,
+ lead > 2 ? 0 : FNE_CHECK_START);
if (end == start) {
if (!skip)
EMSG(_("E129: Function name required"));
@@ -21047,10 +20221,12 @@ trans_function_name (
* interrupt, or an exception.
*/
if (!aborting()) {
- if (end != NULL)
- EMSG2(_(e_invarg2), start);
- } else
- *pp = find_name_end(start, NULL, NULL, FNE_INCL_BR);
+ if (end != NULL) {
+ emsgf(_(e_invarg2), start);
+ }
+ } else {
+ *pp = (char_u *)find_name_end(start, NULL, NULL, FNE_INCL_BR);
+ }
goto theend;
}
@@ -21063,11 +20239,11 @@ trans_function_name (
}
if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) {
name = vim_strsave(lv.ll_tv->vval.v_string);
- *pp = end;
+ *pp = (char_u *)end;
} else if (lv.ll_tv->v_type == VAR_PARTIAL
&& lv.ll_tv->vval.v_partial != NULL) {
- name = vim_strsave(lv.ll_tv->vval.v_partial->pt_name);
- *pp = end;
+ name = vim_strsave(partial_name(lv.ll_tv->vval.v_partial));
+ *pp = (char_u *)end;
if (partial != NULL) {
*partial = lv.ll_tv->vval.v_partial;
}
@@ -21077,7 +20253,7 @@ trans_function_name (
|| fdp->fd_newkey == NULL)) {
EMSG(_(e_funcref));
} else {
- *pp = end;
+ *pp = (char_u *)end;
}
name = NULL;
}
@@ -21085,29 +20261,30 @@ trans_function_name (
}
if (lv.ll_name == NULL) {
- /* Error found, but continue after the function name. */
- *pp = end;
+ // Error found, but continue after the function name.
+ *pp = (char_u *)end;
goto theend;
}
/* Check if the name is a Funcref. If so, use the value. */
if (lv.ll_exp_name != NULL) {
- len = (int)STRLEN(lv.ll_exp_name);
+ len = (int)strlen(lv.ll_exp_name);
name = deref_func_name(lv.ll_exp_name, &len, partial,
flags & TFN_NO_AUTOLOAD);
- if (name == lv.ll_exp_name) {
+ if ((const char *)name == lv.ll_exp_name) {
name = NULL;
}
- } else {
+ } else if (!(flags & TFN_NO_DEREF)) {
len = (int)(end - *pp);
- name = deref_func_name(*pp, &len, partial, flags & TFN_NO_AUTOLOAD);
+ name = deref_func_name((const char *)(*pp), &len, partial,
+ flags & TFN_NO_AUTOLOAD);
if (name == *pp) {
name = NULL;
}
}
if (name != NULL) {
name = vim_strsave(name);
- *pp = end;
+ *pp = (char_u *)end;
if (strncmp((char *)name, "<SNR>", 5) == 0) {
// Change "<SNR>" to the byte sequence.
name[0] = K_SPECIAL;
@@ -21119,12 +20296,13 @@ trans_function_name (
}
if (lv.ll_exp_name != NULL) {
- len = (int)STRLEN(lv.ll_exp_name);
+ len = (int)strlen(lv.ll_exp_name);
if (lead <= 2 && lv.ll_name == lv.ll_exp_name
- && STRNCMP(lv.ll_name, "s:", 2) == 0) {
- /* When there was "s:" already or the name expanded to get a
- * leading "s:" then remove it. */
+ && lv.ll_name_len >= 2 && memcmp(lv.ll_name, "s:", 2) == 0) {
+ // When there was "s:" already or the name expanded to get a
+ // leading "s:" then remove it.
lv.ll_name += 2;
+ lv.ll_name_len -= 2;
len -= 2;
lead = 2;
}
@@ -21132,37 +20310,41 @@ trans_function_name (
// Skip over "s:" and "g:".
if (lead == 2 || (lv.ll_name[0] == 'g' && lv.ll_name[1] == ':')) {
lv.ll_name += 2;
+ lv.ll_name_len -= 2;
}
- len = (int)(end - lv.ll_name);
+ len = (int)((const char *)end - lv.ll_name);
}
- /*
- * Copy the function name to allocated memory.
- * Accept <SID>name() inside a script, translate into <SNR>123_name().
- * Accept <SNR>123_name() outside a script.
- */
- if (skip)
- lead = 0; /* do nothing */
- else if (lead > 0) {
+ size_t sid_buf_len = 0;
+ char sid_buf[20];
+
+ // Copy the function name to allocated memory.
+ // Accept <SID>name() inside a script, translate into <SNR>123_name().
+ // Accept <SNR>123_name() outside a script.
+ if (skip) {
+ lead = 0; // do nothing
+ } else if (lead > 0) {
lead = 3;
if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name))
- || eval_fname_sid(*pp)) {
- /* It's "s:" or "<SID>" */
+ || eval_fname_sid((const char *)(*pp))) {
+ // It's "s:" or "<SID>".
if (current_SID <= 0) {
EMSG(_(e_usingsid));
goto theend;
}
- sprintf((char *)sid_buf, "%" PRId64 "_", (int64_t)current_SID);
- lead += (int)STRLEN(sid_buf);
+ sid_buf_len = snprintf(sid_buf, sizeof(sid_buf),
+ "%" PRIdSCID "_", current_SID);
+ lead += sid_buf_len;
}
- } else if (!(flags & TFN_INT) && builtin_function(lv.ll_name, len)) {
+ } else if (!(flags & TFN_INT)
+ && builtin_function(lv.ll_name, lv.ll_name_len)) {
EMSG2(_("E128: Function name must start with a capital or \"s:\": %s"),
start);
goto theend;
}
- if (!skip && !(flags & TFN_QUIET)) {
- char_u *cp = vim_strchr(lv.ll_name, ':');
+ if (!skip && !(flags & TFN_QUIET) && !(flags & TFN_NO_DEREF)) {
+ char_u *cp = xmemrchr(lv.ll_name, ':', lv.ll_name_len);
if (cp != NULL && cp < end) {
EMSG2(_("E884: Function name cannot contain a colon: %s"), start);
@@ -21175,12 +20357,13 @@ trans_function_name (
name[0] = K_SPECIAL;
name[1] = KS_EXTRA;
name[2] = (int)KE_SNR;
- if (lead > 3) /* If it's "<SID>" */
- STRCPY(name + 3, sid_buf);
+ if (sid_buf_len > 0) { // If it's "<SID>"
+ memcpy(name + 3, sid_buf, sid_buf_len);
+ }
}
- memmove(name + lead, lv.ll_name, (size_t)len);
+ memmove(name + lead, lv.ll_name, len);
name[lead + len] = NUL;
- *pp = end;
+ *pp = (char_u *)end;
theend:
clear_lval(&lv);
@@ -21192,12 +20375,13 @@ theend:
* Return 2 if "p" starts with "s:".
* Return 0 otherwise.
*/
-static int eval_fname_script(char_u *p)
+static int eval_fname_script(const char *const p)
{
- // Use mb_stricmp() because in Turkish comparing the "I" may not work with
+ // Use mb_strnicmp() because in Turkish comparing the "I" may not work with
// the standard library function.
- if (p[0] == '<' && (mb_strnicmp(p + 1, (char_u *)"SID>", 4) == 0
- || mb_strnicmp(p + 1, (char_u *)"SNR>", 4) == 0)) {
+ if (p[0] == '<'
+ && (mb_strnicmp((char_u *)p + 1, (char_u *)"SID>", 4) == 0
+ || mb_strnicmp((char_u *)p + 1, (char_u *)"SNR>", 4) == 0)) {
return 5;
}
if (p[0] == 's' && p[1] == ':') {
@@ -21206,13 +20390,19 @@ static int eval_fname_script(char_u *p)
return 0;
}
-/*
- * Return TRUE if "p" starts with "<SID>" or "s:".
- * Only works if eval_fname_script() returned non-zero for "p"!
- */
-static int eval_fname_sid(char_u *p)
+/// Check whether function name starts with <SID> or s:
+///
+/// @warning Only works for names previously checked by eval_fname_script(), if
+/// it returned non-zero.
+///
+/// @param[in] name Name to check.
+///
+/// @return true if it starts with <SID> or s:, false otherwise.
+static inline bool eval_fname_sid(const char *const name)
+ FUNC_ATTR_PURE FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT
+ FUNC_ATTR_NONNULL_ALL
{
- return *p == 's' || TOUPPER_ASC(p[2]) == 'I';
+ return *name == 's' || TOUPPER_ASC(name[2]) == 'I';
}
/*
@@ -21226,38 +20416,45 @@ static void list_func_head(ufunc_T *fp, int indent)
MSG_PUTS("function ");
if (fp->uf_name[0] == K_SPECIAL) {
MSG_PUTS_ATTR("<SNR>", hl_attr(HLF_8));
- msg_puts(fp->uf_name + 3);
- } else
- msg_puts(fp->uf_name);
+ msg_puts((const char *)fp->uf_name + 3);
+ } else {
+ msg_puts((const char *)fp->uf_name);
+ }
msg_putchar('(');
int j;
- for (j = 0; j < fp->uf_args.ga_len; ++j) {
- if (j)
- MSG_PUTS(", ");
- msg_puts(FUNCARG(fp, j));
+ for (j = 0; j < fp->uf_args.ga_len; j++) {
+ if (j) {
+ msg_puts(", ");
+ }
+ msg_puts((const char *)FUNCARG(fp, j));
}
if (fp->uf_varargs) {
- if (j)
- MSG_PUTS(", ");
- MSG_PUTS("...");
+ if (j) {
+ msg_puts(", ");
+ }
+ msg_puts("...");
}
msg_putchar(')');
- if (fp->uf_flags & FC_ABORT)
- MSG_PUTS(" abort");
- if (fp->uf_flags & FC_RANGE)
- MSG_PUTS(" range");
- if (fp->uf_flags & FC_DICT)
- MSG_PUTS(" dict");
+ if (fp->uf_flags & FC_ABORT) {
+ msg_puts(" abort");
+ }
+ if (fp->uf_flags & FC_RANGE) {
+ msg_puts(" range");
+ }
+ if (fp->uf_flags & FC_DICT) {
+ msg_puts(" dict");
+ }
+ if (fp->uf_flags & FC_CLOSURE) {
+ msg_puts(" closure");
+ }
msg_clr_eos();
if (p_verbose > 0)
last_set_msg(fp->uf_script_ID);
}
-/*
- * Find a function by name, return pointer to it in ufuncs.
- * Return NULL for unknown function.
- */
-static ufunc_T *find_func(char_u *name)
+/// Find a function by name, return pointer to it in ufuncs.
+/// @return NULL for unknown function.
+static ufunc_T *find_func(const char_u *name)
{
hashitem_T *hi;
@@ -21271,61 +20468,118 @@ static ufunc_T *find_func(char_u *name)
void free_all_functions(void)
{
hashitem_T *hi;
+ ufunc_T *fp;
+ uint64_t skipped = 0;
+ uint64_t todo = 1;
+ uint64_t used;
+
+ // First clear what the functions contain. Since this may lower the
+ // reference count of a function, it may also free a function and change
+ // the hash table. Restart if that happens.
+ while (todo > 0) {
+ todo = func_hashtab.ht_used;
+ for (hi = func_hashtab.ht_array; todo > 0; hi++) {
+ if (!HASHITEM_EMPTY(hi)) {
+ // Only free functions that are not refcounted, those are
+ // supposed to be freed when no longer referenced.
+ fp = HI2UF(hi);
+ if (func_name_refcount(fp->uf_name)) {
+ skipped++;
+ } else {
+ used = func_hashtab.ht_used;
+ func_clear(fp, true);
+ if (used != func_hashtab.ht_used) {
+ skipped = 0;
+ break;
+ }
+ }
+ todo--;
+ }
+ }
+ }
- /* Need to start all over every time, because func_free() may change the
- * hash table. */
- while (func_hashtab.ht_used > 0)
- for (hi = func_hashtab.ht_array;; ++hi)
+ // Now actually free the functions. Need to start all over every time,
+ // because func_free() may change the hash table.
+ skipped = 0;
+ while (func_hashtab.ht_used > skipped) {
+ todo = func_hashtab.ht_used;
+ for (hi = func_hashtab.ht_array; todo > 0; hi++) {
if (!HASHITEM_EMPTY(hi)) {
- func_free(HI2UF(hi));
- break;
+ todo--;
+ // Only free functions that are not refcounted, those are
+ // supposed to be freed when no longer referenced.
+ fp = HI2UF(hi);
+ if (func_name_refcount(fp->uf_name)) {
+ skipped++;
+ } else {
+ func_free(fp);
+ skipped = 0;
+ break;
+ }
}
+ }
+ }
+ if (skipped == 0) {
+ hash_clear(&func_hashtab);
+ }
}
#endif
-int translated_function_exists(char_u *name)
+bool translated_function_exists(const char *name)
{
if (builtin_function(name, -1)) {
return find_internal_func((char *)name) != NULL;
}
- return find_func(name) != NULL;
+ return find_func((const char_u *)name) != NULL;
}
-/*
- * Return TRUE if a function "name" exists.
- */
-static int function_exists(char_u *name)
+/// Check whether function with the given name exists
+///
+/// @param[in] name Function name.
+/// @param[in] no_deref Whether to dereference a Funcref.
+///
+/// @return True if it exists, false otherwise.
+static bool function_exists(const char *const name, bool no_deref)
{
- char_u *nm = name;
- char_u *p;
- int n = FALSE;
+ const char_u *nm = (const char_u *)name;
+ bool n = false;
+ int flag = TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD;
- p = trans_function_name(&nm, false, TFN_INT|TFN_QUIET|TFN_NO_AUTOLOAD,
- NULL, NULL);
+ if (no_deref) {
+ flag |= TFN_NO_DEREF;
+ }
+ char *const p = (char *)trans_function_name((char_u **)&nm, false, flag, NULL,
+ NULL);
nm = skipwhite(nm);
/* Only accept "funcname", "funcname ", "funcname (..." and
* "funcname(...", not "funcname!...". */
- if (p != NULL && (*nm == NUL || *nm == '('))
+ if (p != NULL && (*nm == NUL || *nm == '(')) {
n = translated_function_exists(p);
+ }
xfree(p);
return n;
}
-/// Return TRUE if "name" looks like a builtin function name: starts with a
+/// Checks if a builtin function with the given name exists.
+///
+/// @param[in] name name of the builtin function to check.
+/// @param[in] len length of "name", or -1 for NUL terminated.
+///
+/// @return true if "name" looks like a builtin function name: starts with a
/// lower case letter and doesn't contain AUTOLOAD_CHAR.
-/// "len" is the length of "name", or -1 for NUL terminated.
-static bool builtin_function(char_u *name, int len)
+static bool builtin_function(const char *name, int len)
{
if (!ASCII_ISLOWER(name[0])) {
- return FALSE;
+ return false;
}
- char_u *p = vim_strchr(name, AUTOLOAD_CHAR);
+ const char *p = (len == -1
+ ? strchr(name, AUTOLOAD_CHAR)
+ : memchr(name, AUTOLOAD_CHAR, (size_t)len));
- return p == NULL
- || (len > 0 && p > name + len);
+ return p == NULL;
}
/*
@@ -21491,44 +20745,45 @@ static int prof_self_cmp(const void *s1, const void *s2)
}
-/*
- * If "name" has a package name try autoloading the script for it.
- * Return TRUE if a package was loaded.
- */
-static int
-script_autoload (
- char_u *name,
- int reload /* load script again when already loaded */
-)
+/// 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.
+static bool script_autoload(const char *const name, const size_t name_len,
+ const bool reload)
{
- char_u *p;
- char_u *scriptname, *tofree;
- int ret = FALSE;
- int i;
-
- /* If there is no '#' after name[0] there is no package name. */
- p = vim_strchr(name, AUTOLOAD_CHAR);
- if (p == NULL || p == name)
- return FALSE;
+ // 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;
+ }
- tofree = scriptname = autoload_name(name);
+ 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. */
- for (i = 0; i < ga_loaded.ga_len; ++i)
- if (STRCMP(((char_u **)ga_loaded.ga_data)[i] + 9, scriptname + 9) == 0)
+ // 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 (!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_u *, &ga_loaded, scriptname);
+ GA_APPEND(char *, &ga_loaded, scriptname);
tofree = NULL;
}
// Try loading the package from $VIMRUNTIME/autoload/<name>.vim
- if (source_runtime(scriptname, 0) == OK) {
+ if (source_runtime((char_u *)scriptname, 0) == OK) {
ret = true;
}
}
@@ -21537,21 +20792,29 @@ script_autoload (
return ret;
}
-/*
- * Return the autoload script name for a function or variable name.
- */
-static char_u *autoload_name(char_u *name)
+/// Return the autoload script name for a function or variable name
+///
+/// @param[in] name Variable/function name.
+/// @param[in] name_len Name length.
+///
+/// @return [allocated] autoload script name.
+static 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_u *scriptname = xmalloc(STRLEN(name) + 14);
- STRCPY(scriptname, "autoload/");
- STRCAT(scriptname, name);
- *vim_strrchr(scriptname, AUTOLOAD_CHAR) = NUL;
- STRCAT(scriptname, ".vim");
-
- char_u *p;
- while ((p = vim_strchr(scriptname, AUTOLOAD_CHAR)) != NULL)
- *p = '/';
+ // 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;
}
@@ -21579,8 +20842,10 @@ char_u *get_user_func_name(expand_T *xp, int idx)
++hi;
fp = HI2UF(hi);
- if (fp->uf_flags & FC_DICT)
- return (char_u *)""; /* don't show dict functions */
+ if ((fp->uf_flags & FC_DICT)
+ || STRNCMP(fp->uf_name, "<lambda>", 8) == 0) {
+ return (char_u *)""; // don't show dict and lambda functions
+ }
if (STRLEN(fp->uf_name) + 4 >= IOSIZE)
return fp->uf_name; /* prevents overflow */
@@ -21611,9 +20876,18 @@ static void cat_func_name(char_u *buf, ufunc_T *fp)
STRCPY(buf, fp->uf_name);
}
-/*
- * ":delfunction {name}"
- */
+/// There are two kinds of function names:
+/// 1. ordinary names, function defined with :function
+/// 2. numbered functions and lambdas
+/// For the first we only count the name stored in func_hashtab as a reference,
+/// using function() does not count as a reference, because the function is
+/// looked up by name.
+static bool func_name_refcount(char_u *name)
+{
+ return isdigit(*name) || *name == '<';
+}
+
+/// ":delfunction {name}"
void ex_delfunction(exarg_T *eap)
{
ufunc_T *fp = NULL;
@@ -21660,102 +20934,172 @@ void ex_delfunction(exarg_T *eap)
}
if (fudi.fd_dict != NULL) {
- /* Delete the dict item that refers to the function, it will
- * invoke func_unref() and possibly delete the function. */
- dictitem_remove(fudi.fd_dict, fudi.fd_di);
- } else
- func_free(fp);
+ // Delete the dict item that refers to the function, it will
+ // invoke func_unref() and possibly delete the function.
+ tv_dict_item_remove(fudi.fd_dict, fudi.fd_di);
+ } else {
+ // A normal function (not a numbered function or lambda) has a
+ // refcount of 1 for the entry in the hashtable. When deleting
+ // it and the refcount is more than one, it should be kept.
+ // A numbered function or lambda should be kept if the refcount is
+ // one or more.
+ if (fp->uf_refcount > (func_name_refcount(fp->uf_name) ? 0 : 1)) {
+ // Function is still referenced somewhere. Don't free it but
+ // do remove it from the hashtable.
+ if (func_remove(fp)) {
+ fp->uf_refcount--;
+ }
+ fp->uf_flags |= FC_DELETED;
+ } else {
+ func_clear_free(fp, false);
+ }
+ }
}
}
-/*
- * Free a function and remove it from the list of functions.
- */
-static void func_free(ufunc_T *fp)
+/// Remove the function from the function hashtable. If the function was
+/// deleted while it still has references this was already done.
+///
+/// @return true if the entry was deleted, false if it wasn't found.
+static bool func_remove(ufunc_T *fp)
{
- hashitem_T *hi;
+ hashitem_T *hi = hash_find(&func_hashtab, UF2HIKEY(fp));
+
+ if (!HASHITEM_EMPTY(hi)) {
+ hash_remove(&func_hashtab, hi);
+ return true;
+ }
+
+ return false;
+}
- /* clear this function */
+/// Free all things that a function contains. Does not free the function
+/// itself, use func_free() for that.
+///
+/// param[in] force When true, we are exiting.
+static void func_clear(ufunc_T *fp, bool force)
+{
+ if (fp->uf_cleared) {
+ return;
+ }
+ fp->uf_cleared = true;
+
+ // clear this function
ga_clear_strings(&(fp->uf_args));
ga_clear_strings(&(fp->uf_lines));
xfree(fp->uf_tml_count);
xfree(fp->uf_tml_total);
xfree(fp->uf_tml_self);
+ funccal_unref(fp->uf_scoped, fp, force);
+}
- /* remove the function from the function hashtable */
- hi = hash_find(&func_hashtab, UF2HIKEY(fp));
- if (HASHITEM_EMPTY(hi))
- EMSG2(_(e_intern2), "func_free()");
- else
- hash_remove(&func_hashtab, hi);
-
+/// Free a function and remove it from the list of functions. Does not free
+/// what a function contains, call func_clear() first.
+///
+/// param[in] fp The function to free.
+static void func_free(ufunc_T *fp)
+{
+ // only remove it when not done already, otherwise we would remove a newer
+ // version of the function
+ if ((fp->uf_flags & (FC_DELETED | FC_REMOVED)) == 0) {
+ func_remove(fp);
+ }
xfree(fp);
}
+/// Free all things that a function contains and free the function itself.
+///
+/// param[in] force When true, we are exiting.
+static void func_clear_free(ufunc_T *fp, bool force)
+{
+ func_clear(fp, force);
+ func_free(fp);
+}
+
/*
* Unreference a Function: decrement the reference count and free it when it
- * becomes zero. Only for numbered functions.
+ * becomes zero.
*/
void func_unref(char_u *name)
{
- ufunc_T *fp;
+ ufunc_T *fp = NULL;
- if (name != NULL && isdigit(*name)) {
- fp = find_func(name);
- if (fp == NULL) {
+ if (name == NULL || !func_name_refcount(name)) {
+ return;
+ }
+
+ fp = find_func(name);
+ if (fp == NULL && isdigit(*name)) {
#ifdef EXITFREE
- if (!entered_free_all_mem) {
- EMSG2(_(e_intern2), "func_unref()");
- }
+ if (!entered_free_all_mem) {
+ EMSG2(_(e_intern2), "func_unref()");
+ abort();
+ }
#else
EMSG2(_(e_intern2), "func_unref()");
+ abort();
#endif
- } else {
- user_func_unref(fp);
- }
}
+ func_ptr_unref(fp);
}
-static void user_func_unref(ufunc_T *fp)
+/// Unreference a Function: decrement the reference count and free it when it
+/// becomes zero.
+/// Unreference user function, freeing it if needed
+///
+/// Decrements the reference count and frees when it becomes zero.
+///
+/// @param fp Function to unreference.
+void func_ptr_unref(ufunc_T *fp)
{
- if (--fp->uf_refcount <= 0) {
- // Only delete it when it's not being used. Otherwise it's done
+ if (fp != NULL && --fp->uf_refcount <= 0) {
+ // Only delete it when it's not being used. Otherwise it's done
// when "uf_calls" becomes zero.
if (fp->uf_calls == 0) {
- func_free(fp);
+ func_clear_free(fp, false);
}
}
}
-/*
- * Count a reference to a Function.
- */
+/// Count a reference to a Function.
void func_ref(char_u *name)
{
ufunc_T *fp;
- if (name != NULL && isdigit(*name)) {
- fp = find_func(name);
- if (fp == NULL)
- EMSG2(_(e_intern2), "func_ref()");
- else
- ++fp->uf_refcount;
+ if (name == NULL || !func_name_refcount(name)) {
+ return;
+ }
+ fp = find_func(name);
+ if (fp != NULL) {
+ (fp->uf_refcount)++;
+ } else if (isdigit(*name)) {
+ // Only give an error for a numbered function.
+ // Fail silently, when named or lambda function isn't found.
+ EMSG2(_(e_intern2), "func_ref()");
}
}
-/*
- * Call a user function.
- */
-static void
-call_user_func (
- ufunc_T *fp, /* pointer to function */
- int argcount, /* nr of args */
- typval_T *argvars, /* arguments */
- typval_T *rettv, /* return value */
- linenr_T firstline, /* first line of range */
- linenr_T lastline, /* last line of range */
- dict_T *selfdict /* Dictionary for "self" */
-)
+/// Count a reference to a Function.
+void func_ptr_ref(ufunc_T *fp)
+{
+ if (fp != NULL) {
+ (fp->uf_refcount)++;
+ }
+}
+
+/// Call a user function
+///
+/// @param fp Function to call.
+/// @param[in] argcount Number of arguments.
+/// @param argvars Arguments.
+/// @param[out] rettv Return value.
+/// @param[in] firstline First line of range.
+/// @param[in] lastline Last line of range.
+/// @param selfdict Dictionary for "self" for dictionary functions.
+void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
+ typval_T *rettv, linenr_T firstline, linenr_T lastline,
+ dict_T *selfdict)
+ FUNC_ATTR_NONNULL_ARG(1, 3, 4)
{
char_u *save_sourcing_name;
linenr_T save_sourcing_lnum;
@@ -21766,6 +21110,7 @@ call_user_func (
dictitem_T *v;
int fixvar_idx = 0; /* index in fixvar[] */
int ai;
+ bool islambda = false;
char_u numbuf[NUMBUFLEN];
char_u *name;
proftime_T wait_start;
@@ -21803,25 +21148,32 @@ call_user_func (
fc->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, (linenr_T)0);
fc->dbg_tick = debug_tick;
- /*
- * Note about using fc->fixvar[]: This is an array of FIXVAR_CNT variables
- * with names up to VAR_SHORT_LEN long. This avoids having to alloc/free
- * each argument variable and saves a lot of time.
- */
- /*
- * Init l: variables.
- */
+ // Set up fields for closure.
+ fc->fc_refcount = 0;
+ fc->fc_copyID = 0;
+ ga_init(&fc->fc_funcs, sizeof(ufunc_T *), 1);
+ func_ptr_ref(fp);
+
+ if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0) {
+ islambda = true;
+ }
+
+ // Note about using fc->fixvar[]: This is an array of FIXVAR_CNT variables
+ // with names up to VAR_SHORT_LEN long. This avoids having to alloc/free
+ // each argument variable and saves a lot of time.
+ //
+ // Init l: variables.
init_var_dict(&fc->l_vars, &fc->l_vars_var, VAR_DEF_SCOPE);
if (selfdict != NULL) {
- /* Set l:self to "selfdict". Use "name" to avoid a warning from
- * some compiler that checks the destination size. */
- v = &fc->fixvar[fixvar_idx++].var;
+ // Set l:self to "selfdict". Use "name" to avoid a warning from
+ // some compiler that checks the destination size.
+ v = (dictitem_T *)&fc->fixvar[fixvar_idx++];
#ifndef __clang_analyzer__
name = v->di_key;
STRCPY(name, "self");
#endif
v->di_flags = DI_FLAGS_RO + DI_FLAGS_FIX;
- hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v));
+ tv_dict_add(&fc->l_vars, v);
v->di_tv.v_type = VAR_DICT;
v->di_tv.v_lock = 0;
v->di_tv.vval.v_dict = selfdict;
@@ -21834,17 +21186,17 @@ call_user_func (
* Set a:000 to a list with room for the "..." arguments.
*/
init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE);
- add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "0",
- (varnumber_T)(argcount - fp->uf_args.ga_len));
- /* Use "name" to avoid a warning from some compiler that checks the
- * destination size. */
- v = &fc->fixvar[fixvar_idx++].var;
+ add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], "0",
+ (varnumber_T)(argcount - fp->uf_args.ga_len));
+ // Use "name" to avoid a warning from some compiler that checks the
+ // destination size.
+ v = (dictitem_T *)&fc->fixvar[fixvar_idx++];
#ifndef __clang_analyzer__
name = v->di_key;
STRCPY(name, "000");
#endif
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v));
+ tv_dict_add(&fc->l_avars, v);
v->di_tv.v_type = VAR_LIST;
v->di_tv.v_lock = VAR_FIXED;
v->di_tv.vval.v_list = &fc->l_varlist;
@@ -21852,42 +21204,53 @@ call_user_func (
fc->l_varlist.lv_refcount = DO_NOT_FREE_CNT;
fc->l_varlist.lv_lock = VAR_FIXED;
- /*
- * Set a:firstline to "firstline" and a:lastline to "lastline".
- * Set a:name to named arguments.
- * Set a:N to the "..." arguments.
- */
- add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "firstline",
- (varnumber_T)firstline);
- add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "lastline",
- (varnumber_T)lastline);
- for (int i = 0; i < argcount; ++i) {
+ // Set a:firstline to "firstline" and a:lastline to "lastline".
+ // Set a:name to named arguments.
+ // Set a:N to the "..." arguments.
+ add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++],
+ "firstline", (varnumber_T)firstline);
+ add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++],
+ "lastline", (varnumber_T)lastline);
+ for (int i = 0; i < argcount; i++) {
+ bool addlocal = false;
+
ai = i - fp->uf_args.ga_len;
- if (ai < 0)
- /* named argument a:name */
+ if (ai < 0) {
+ // named argument a:name
name = FUNCARG(fp, i);
- else {
- /* "..." argument a:1, a:2, etc. */
- sprintf((char *)numbuf, "%d", ai + 1);
+ if (islambda) {
+ addlocal = true;
+ }
+ } else {
+ // "..." argument a:1, a:2, etc.
+ snprintf((char *)numbuf, sizeof(numbuf), "%d", ai + 1);
name = numbuf;
}
if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN) {
- v = &fc->fixvar[fixvar_idx++].var;
+ v = (dictitem_T *)&fc->fixvar[fixvar_idx++];
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
} else {
v = xmalloc(sizeof(dictitem_T) + STRLEN(name));
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
}
STRCPY(v->di_key, name);
- hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v));
- /* Note: the values are copied directly to avoid alloc/free.
- * "argvars" must have VAR_FIXED for v_lock. */
+ // Note: the values are copied directly to avoid alloc/free.
+ // "argvars" must have VAR_FIXED for v_lock.
v->di_tv = argvars[i];
v->di_tv.v_lock = VAR_FIXED;
+ if (addlocal) {
+ // Named arguments can be accessed without the "a:" prefix in lambda
+ // expressions. Add to the l: dict.
+ tv_copy(&v->di_tv, &v->di_tv);
+ tv_dict_add(&fc->l_vars, v);
+ } else {
+ tv_dict_add(&fc->l_avars, v);
+ }
+
if (ai >= 0 && ai < MAX_FUNC_ARGS) {
- list_append(&fc->l_varlist, &fc->l_listitems[ai]);
+ tv_list_append(&fc->l_varlist, &fc->l_listitems[ai]);
fc->l_listitems[ai].li_tv = argvars[i];
fc->l_listitems[ai].li_tv.v_lock = VAR_FIXED;
}
@@ -21925,24 +21288,24 @@ call_user_func (
smsg(_("calling %s"), sourcing_name);
if (p_verbose >= 14) {
- char_u buf[MSG_BUF_LEN];
-
- msg_puts((char_u *)"(");
- for (int i = 0; i < argcount; ++i) {
+ msg_puts("(");
+ for (int i = 0; i < argcount; i++) {
if (i > 0) {
- msg_puts((char_u *)", ");
+ msg_puts(", ");
}
if (argvars[i].v_type == VAR_NUMBER) {
msg_outnum((long)argvars[i].vval.v_number);
} else {
// Do not want errors such as E724 here.
emsg_off++;
- char_u *s = (char_u *) encode_tv2string(&argvars[i], NULL);
- char_u *tofree = s;
+ char *tofree = encode_tv2string(&argvars[i], NULL);
emsg_off--;
- if (s != NULL) {
- if (vim_strsize(s) > MSG_BUF_CLEN) {
- trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN);
+ if (tofree != NULL) {
+ char *s = tofree;
+ char buf[MSG_BUF_LEN];
+ if (vim_strsize((char_u *)s) > MSG_BUF_CLEN) {
+ trunc_string((char_u *)s, (char_u *)buf, MSG_BUF_CLEN,
+ sizeof(buf));
s = buf;
}
msg_puts(s);
@@ -21950,9 +21313,9 @@ call_user_func (
}
}
}
- msg_puts((char_u *)")");
+ msg_puts(")");
}
- msg_puts((char_u *)"\n"); /* don't overwrite this either */
+ msg_puts("\n"); // don't overwrite this either
verbose_leave_scroll();
--no_wait_return;
@@ -21995,14 +21358,14 @@ call_user_func (
// when the function was aborted because of an error, return -1
if ((did_emsg
&& (fp->uf_flags & FC_ABORT)) || rettv->v_type == VAR_UNKNOWN) {
- clear_tv(rettv);
+ tv_clear(rettv);
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = -1;
}
if (func_or_func_caller_profiling) {
call_start = profile_end(call_start);
- call_start = profile_sub_wait(wait_start, call_start);
+ call_start = profile_sub_wait(wait_start, call_start); // -V614
fp->uf_tm_total = profile_add(fp->uf_tm_total, call_start);
fp->uf_tm_self = profile_self(fp->uf_tm_self, call_start,
fp->uf_tm_children);
@@ -22043,7 +21406,7 @@ call_user_func (
xfree(tofree);
}
}
- msg_puts((char_u *)"\n"); /* don't overwrite this either */
+ msg_puts("\n"); // don't overwrite this either
verbose_leave_scroll();
--no_wait_return;
@@ -22061,7 +21424,7 @@ call_user_func (
verbose_enter_scroll();
smsg(_("continuing in %s"), sourcing_name);
- msg_puts((char_u *)"\n"); /* don't overwrite this either */
+ msg_puts("\n"); // don't overwrite this either
verbose_leave_scroll();
--no_wait_return;
@@ -22071,41 +21434,35 @@ call_user_func (
current_funccal = fc->caller;
--depth;
- /* If the a:000 list and the l: and a: dicts are not referenced we can
- * free the funccall_T and what's in it. */
+ // If the a:000 list and the l: and a: dicts are not referenced and there
+ // is no closure using it, we can free the funccall_T and what's in it.
if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
&& fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
- && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT) {
- free_funccal(fc, FALSE);
+ && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT
+ && fc->fc_refcount <= 0) {
+ free_funccal(fc, false);
} else {
- hashitem_T *hi;
- listitem_T *li;
- int todo;
-
- /* "fc" is still in use. This can happen when returning "a:000" or
- * assigning "l:" to a global variable.
- * Link "fc" in the list for garbage collection later. */
+ // "fc" is still in use. This can happen when returning "a:000",
+ // assigning "l:" to a global variable or defining a closure.
+ // Link "fc" in the list for garbage collection later.
fc->caller = previous_funccal;
previous_funccal = fc;
- /* Make a copy of the a: variables, since we didn't do that above. */
- todo = (int)fc->l_avars.dv_hashtab.ht_used;
- for (hi = fc->l_avars.dv_hashtab.ht_array; todo > 0; ++hi) {
- if (!HASHITEM_EMPTY(hi)) {
- --todo;
- v = HI2DI(hi);
- copy_tv(&v->di_tv, &v->di_tv);
- }
- }
+ // Make a copy of the a: variables, since we didn't do that above.
+ TV_DICT_ITER(&fc->l_avars, di, {
+ tv_copy(&di->di_tv, &di->di_tv);
+ });
- /* Make a copy of the a:000 items, since we didn't do that above. */
- for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next)
- copy_tv(&li->li_tv, &li->li_tv);
+ // Make a copy of the a:000 items, since we didn't do that above.
+ for (listitem_T *li = fc->l_varlist.lv_first; li != NULL;
+ li = li->li_next) {
+ tv_copy(&li->li_tv, &li->li_tv);
+ }
}
- if (--fp->uf_calls <= 0 && isdigit(*fp->uf_name) && fp->uf_refcount <= 0) {
+ if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) {
// Function was unreferenced while being used, free it now.
- func_free(fp);
+ func_clear_free(fp, false);
}
// restore search patterns and redo buffer
if (did_save_redo) {
@@ -22114,15 +21471,46 @@ call_user_func (
restore_search_patterns();
}
-/*
- * Return TRUE if items in "fc" do not have "copyID". That means they are not
- * referenced from anywhere that is in use.
- */
+/// Unreference "fc": decrement the reference count and free it when it
+/// becomes zero. "fp" is detached from "fc".
+///
+/// @param[in] force When true, we are exiting.
+static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force)
+{
+ funccall_T **pfc;
+ int i;
+
+ if (fc == NULL) {
+ return;
+ }
+
+ if (--fc->fc_refcount <= 0 && (force || (
+ fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
+ && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
+ && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT))) {
+ for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) {
+ if (fc == *pfc) {
+ *pfc = fc->caller;
+ free_funccal(fc, true);
+ return;
+ }
+ }
+ }
+ for (i = 0; i < fc->fc_funcs.ga_len; i++) {
+ if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp) {
+ ((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL;
+ }
+ }
+}
+
+/// @return true if items in "fc" do not have "copyID". That means they are not
+/// referenced from anywhere that is in use.
static int can_free_funccal(funccall_T *fc, int copyID)
{
return fc->l_varlist.lv_copyID != copyID
&& fc->l_vars.dv_copyID != copyID
- && fc->l_avars.dv_copyID != copyID;
+ && fc->l_avars.dv_copyID != copyID
+ && fc->fc_copyID != copyID;
}
/*
@@ -22136,18 +21524,34 @@ free_funccal (
{
listitem_T *li;
- /* The a: variables typevals may not have been allocated, only free the
- * allocated variables. */
+ for (int i = 0; i < fc->fc_funcs.ga_len; i++) {
+ ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i];
+
+ // When garbage collecting a funccall_T may be freed before the
+ // function that references it, clear its uf_scoped field.
+ // The function may have been redefined and point to another
+ // funccal_T, don't clear it then.
+ if (fp != NULL && fp->uf_scoped == fc) {
+ fp->uf_scoped = NULL;
+ }
+ }
+ ga_clear(&fc->fc_funcs);
+
+ // The a: variables typevals may not have been allocated, only free the
+ // allocated variables.
vars_clear_ext(&fc->l_avars.dv_hashtab, free_val);
/* free all l: variables */
vars_clear(&fc->l_vars.dv_hashtab);
- /* Free the a:000 variables if they were allocated. */
- if (free_val)
- for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next)
- clear_tv(&li->li_tv);
+ // Free the a:000 variables if they were allocated.
+ if (free_val) {
+ for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) {
+ tv_clear(&li->li_tv);
+ }
+ }
+ func_ptr_unref(fc->func);
xfree(fc);
}
@@ -22160,7 +21564,7 @@ static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr)
STRCPY(v->di_key, name);
#endif
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- hash_add(&dp->dv_hashtab, DI2HIKEY(v));
+ tv_dict_add(dp, v);
v->di_tv.v_type = VAR_NUMBER;
v->di_tv.v_lock = VAR_FIXED;
v->di_tv.vval.v_number = nr;
@@ -22186,10 +21590,11 @@ void ex_return(exarg_T *eap)
eap->nextcmd = NULL;
if ((*arg != NUL && *arg != '|' && *arg != '\n')
&& eval0(arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) {
- if (!eap->skip)
- returning = do_return(eap, FALSE, TRUE, &rettv);
- else
- clear_tv(&rettv);
+ if (!eap->skip) {
+ returning = do_return(eap, false, true, &rettv);
+ } else {
+ tv_clear(&rettv);
+ }
}
/* It's safer to return also on error. */
else if (!eap->skip) {
@@ -22276,7 +21681,7 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv)
* a return immediately after reanimation, the value is already
* there. */
if (!reanimate && rettv != NULL) {
- clear_tv(current_funccal->rettv);
+ tv_clear(current_funccal->rettv);
*current_funccal->rettv = *(typval_T *)rettv;
if (!is_cmd)
xfree(rettv);
@@ -22287,14 +21692,6 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv)
}
/*
- * Free the variable with a pending return value.
- */
-void discard_pending_return(void *rettv)
-{
- free_tv((typval_T *)rettv);
-}
-
-/*
* Generate a return command for producing the value of "rettv". The result
* is an allocated string. Used by report_pending() for verbose messages.
*/
@@ -22311,7 +21708,7 @@ char_u *get_return_cmd(void *rettv)
}
STRCPY(IObuff, ":return ");
- STRNCPY(IObuff + 8, s, IOSIZE - 8);
+ STRLCPY(IObuff + 8, s, IOSIZE - 8);
if (STRLEN(s) + 8 >= IOSIZE)
STRCPY(IObuff + IOSIZE - 4, "...");
xfree(tofree);
@@ -22466,6 +21863,72 @@ static var_flavour_T var_flavour(char_u *varname)
}
}
+/// Search hashitem in parent scope.
+hashitem_T *find_hi_in_scoped_ht(const char *name, hashtab_T **pht)
+{
+ if (current_funccal == NULL || current_funccal->func->uf_scoped == NULL) {
+ return NULL;
+ }
+
+ funccall_T *old_current_funccal = current_funccal;
+ hashitem_T *hi = NULL;
+ const size_t namelen = strlen(name);
+ const char *varname;
+
+ // Search in parent scope which is possible to reference from lambda
+ current_funccal = current_funccal->func->uf_scoped;
+ while (current_funccal != NULL) {
+ hashtab_T *ht = find_var_ht(name, namelen, &varname);
+ if (ht != NULL && *varname != NUL) {
+ hi = hash_find_len(ht, varname, namelen - (varname - name));
+ if (!HASHITEM_EMPTY(hi)) {
+ *pht = ht;
+ break;
+ }
+ }
+ if (current_funccal == current_funccal->func->uf_scoped) {
+ break;
+ }
+ current_funccal = current_funccal->func->uf_scoped;
+ }
+ current_funccal = old_current_funccal;
+
+ return hi;
+}
+
+/// Search variable in parent scope.
+dictitem_T *find_var_in_scoped_ht(const char *name, const size_t namelen,
+ int no_autoload)
+{
+ if (current_funccal == NULL || current_funccal->func->uf_scoped == NULL) {
+ return NULL;
+ }
+
+ dictitem_T *v = NULL;
+ funccall_T *old_current_funccal = current_funccal;
+ const char *varname;
+
+ // Search in parent scope which is possible to reference from lambda
+ current_funccal = current_funccal->func->uf_scoped;
+ while (current_funccal) {
+ hashtab_T *ht = find_var_ht(name, namelen, &varname);
+ if (ht != NULL && *varname != NUL) {
+ v = find_var_in_ht(ht, *name, varname,
+ namelen - (size_t)(varname - name), no_autoload);
+ if (v != NULL) {
+ break;
+ }
+ }
+ if (current_funccal == current_funccal->func->uf_scoped) {
+ break;
+ }
+ current_funccal = current_funccal->func->uf_scoped;
+ }
+ current_funccal = old_current_funccal;
+
+ return v;
+}
+
/// Iterate over global variables
///
/// @warning No modifications to global variable dictionary must be performed
@@ -22489,7 +21952,7 @@ const void *var_shada_iter(const void *const iter, const char **const name,
hi = globvarht.ht_array;
while ((size_t) (hi - hifirst) < hinum
&& (HASHITEM_EMPTY(hi)
- || var_flavour(HI2DI(hi)->di_key) != VAR_FLAVOUR_SHADA)) {
+ || var_flavour(hi->hi_key) != VAR_FLAVOUR_SHADA)) {
hi++;
}
if ((size_t) (hi - hifirst) == hinum) {
@@ -22498,11 +21961,10 @@ const void *var_shada_iter(const void *const iter, const char **const name,
} else {
hi = (const hashitem_T *) iter;
}
- *name = (char *) HI2DI(hi)->di_key;
- copy_tv(&(HI2DI(hi)->di_tv), rettv);
- while ((size_t) (++hi - hifirst) < hinum) {
- if (!HASHITEM_EMPTY(hi)
- && var_flavour(HI2DI(hi)->di_key) == VAR_FLAVOUR_SHADA) {
+ *name = (char *)TV_DICT_HI2DI(hi)->di_key;
+ tv_copy(&TV_DICT_HI2DI(hi)->di_tv, rettv);
+ while ((size_t)(++hi - hifirst) < hinum) {
+ if (!HASHITEM_EMPTY(hi) && var_flavour(hi->hi_key) == VAR_FLAVOUR_SHADA) {
return hi;
}
}
@@ -22513,62 +21975,55 @@ void var_set_global(const char *const name, typval_T vartv)
{
funccall_T *const saved_current_funccal = current_funccal;
current_funccal = NULL;
- set_var((char_u *) name, &vartv, false);
+ set_var(name, strlen(name), &vartv, false);
current_funccal = saved_current_funccal;
}
int store_session_globals(FILE *fd)
{
- hashitem_T *hi;
- dictitem_T *this_var;
- int todo;
- char_u *p, *t;
-
- todo = (int)globvarht.ht_used;
- for (hi = globvarht.ht_array; todo > 0; ++hi) {
- if (!HASHITEM_EMPTY(hi)) {
- --todo;
- this_var = HI2DI(hi);
- if ((this_var->di_tv.v_type == VAR_NUMBER
- || this_var->di_tv.v_type == VAR_STRING)
- && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) {
- /* Escape special characters with a backslash. Turn a LF and
- * CR into \n and \r. */
- p = vim_strsave_escaped(get_tv_string(&this_var->di_tv),
- (char_u *)"\\\"\n\r");
- for (t = p; *t != NUL; ++t)
- if (*t == '\n')
- *t = 'n';
- else if (*t == '\r')
- *t = 'r';
- if ((fprintf(fd, "let %s = %c%s%c",
- this_var->di_key,
- (this_var->di_tv.v_type == VAR_STRING) ? '"'
- : ' ',
- p,
- (this_var->di_tv.v_type == VAR_STRING) ? '"'
- : ' ') < 0)
- || put_eol(fd) == FAIL) {
- xfree(p);
- return FAIL;
+ 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(this_var->di_key) == VAR_FLAVOUR_SESSION) {
+ // Escape special characters with a backslash. Turn a LF and
+ // CR into \n and \r.
+ char_u *const p = vim_strsave_escaped(
+ (const char_u *)tv_get_string(&this_var->di_tv),
+ (const char_u *)"\\\"\n\r");
+ for (char_u *t = p; *t != NUL; t++) {
+ if (*t == '\n') {
+ *t = 'n';
+ } else if (*t == '\r') {
+ *t = 'r';
}
+ }
+ if ((fprintf(fd, "let %s = %c%s%c",
+ this_var->di_key,
+ ((this_var->di_tv.v_type == VAR_STRING) ? '"'
+ : ' '),
+ p,
+ ((this_var->di_tv.v_type == VAR_STRING) ? '"'
+ : ' ')) < 0)
+ || put_eol(fd) == FAIL) {
xfree(p);
- } else if (this_var->di_tv.v_type == VAR_FLOAT
- && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) {
- float_T f = this_var->di_tv.vval.v_float;
- int sign = ' ';
-
- if (f < 0) {
- f = -f;
- sign = '-';
- }
- if ((fprintf(fd, "let %s = %c%f",
- this_var->di_key, sign, f) < 0)
- || put_eol(fd) == FAIL)
- return FAIL;
+ return FAIL;
+ }
+ xfree(p);
+ } else if (this_var->di_tv.v_type == VAR_FLOAT
+ && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) {
+ float_T f = this_var->di_tv.vval.v_float;
+ int sign = ' ';
+
+ if (f < 0) {
+ f = -f;
+ sign = '-';
+ }
+ if ((fprintf(fd, "let %s = %c%f", this_var->di_key, sign, f) < 0)
+ || put_eol(fd) == FAIL) {
+ return FAIL;
}
}
- }
+ });
return OK;
}
@@ -22588,51 +22043,6 @@ void last_set_msg(scid_T scriptID)
}
}
-/*
- * List v:oldfiles in a nice way.
- */
-void ex_oldfiles(exarg_T *eap)
-{
- list_T *l = get_vim_var_list(VV_OLDFILES);
- listitem_T *li;
- long nr = 0;
-
- if (l == NULL)
- msg((char_u *)_("No old files"));
- else {
- msg_start();
- msg_scroll = TRUE;
- for (li = l->lv_first; li != NULL && !got_int; li = li->li_next) {
- msg_outnum(++nr);
- MSG_PUTS(": ");
- msg_outtrans(get_tv_string(&li->li_tv));
- msg_putchar('\n');
- ui_flush(); /* output one line at a time */
- os_breakcheck();
- }
- /* Assume "got_int" was set to truncate the listing. */
- got_int = FALSE;
-
- // File selection prompt on ":browse oldfiles"
- if (cmdmod.browse) {
- quit_more = false;
- nr = prompt_for_number(false);
- msg_starthere();
- if (nr > 0 && nr <= l->lv_len) {
- char_u *p = list_find_str(l, nr);
- if (p == NULL) {
- return;
- }
- p = expand_env_save(p);
- eap->arg = p;
- eap->cmdidx = CMD_edit;
- do_exedit(eap, NULL);
- xfree(p);
- }
- }
- }
-}
-
// reset v:option_new, v:option_old and v:option_type
void reset_v_option_vars(void)
{
@@ -22865,7 +22275,7 @@ repeat:
sub = vim_strnsave(s, (int)(p - s));
str = vim_strnsave(*fnamep, *fnamelen);
*usedlen = (size_t)(p + 1 - src);
- s = do_string_sub(str, pat, sub, flags);
+ s = do_string_sub(str, pat, sub, NULL, flags);
*fnamep = s;
*fnamelen = STRLEN(s);
xfree(*bufp);
@@ -22901,12 +22311,12 @@ repeat:
return valid;
}
-/*
- * Perform a substitution on "str" with pattern "pat" and substitute "sub".
- * "flags" can be "g" to do a global substitute.
- * Returns an allocated string, NULL for error.
- */
-char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags)
+/// Perform a substitution on "str" with pattern "pat" and substitute "sub".
+/// When "sub" is NULL "expr" is used, must be a VAR_FUNC or VAR_PARTIAL.
+/// "flags" can be "g" to do a global substitute.
+/// Returns an allocated string, NULL for error.
+char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub,
+ typval_T *expr, char_u *flags)
{
int sublen;
regmatch_T regmatch;
@@ -22944,23 +22354,21 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags)
zero_width = regmatch.startp[0];
}
- /*
- * Get some space for a temporary buffer to do the substitution
- * into. It will contain:
- * - The text up to where the match is.
- * - The substituted text.
- * - The text after the match.
- */
- sublen = vim_regsub(&regmatch, sub, tail, FALSE, TRUE, FALSE);
+ // Get some space for a temporary buffer to do the substitution
+ // into. It will contain:
+ // - The text up to where the match is.
+ // - The substituted text.
+ // - The text after the match.
+ sublen = vim_regsub(&regmatch, sub, expr, tail, false, true, false);
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);
- /* add the substituted text */
- (void)vim_regsub(&regmatch, sub, (char_u *)ga.ga_data
- + ga.ga_len + i, TRUE, TRUE, FALSE);
+ // add the substituted text
+ (void)vim_regsub(&regmatch, sub, expr, (char_u *)ga.ga_data
+ + ga.ga_len + i, true, true, false);
ga.ga_len += i + sublen - 1;
tail = regmatch.endp[0];
if (*tail == NUL)
@@ -22977,11 +22385,12 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags)
char_u *ret = vim_strsave(ga.ga_data == NULL ? str : (char_u *)ga.ga_data);
ga_clear(&ga);
- if (p_cpo == empty_option)
+ if (p_cpo == empty_option) {
p_cpo = save_cpo;
- else
- /* Darn, evaluating {sub} expression changed the value. */
+ } else {
+ // Darn, evaluating {sub} expression or {expr} changed the value.
free_string_option(save_cpo);
+ }
return ret;
}
@@ -22993,7 +22402,7 @@ static inline TerminalJobData *common_job_init(char **argv,
bool pty,
bool rpc,
bool detach,
- char *cwd)
+ const char *cwd)
{
TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData));
data->stopped = false;
@@ -23027,9 +22436,9 @@ static inline TerminalJobData *common_job_init(char **argv,
static inline bool common_job_callbacks(dict_T *vopts, Callback *on_stdout,
Callback *on_stderr, Callback *on_exit)
{
- if (get_dict_callback(vopts, "on_stdout", on_stdout)
- && get_dict_callback(vopts, "on_stderr", on_stderr)
- && get_dict_callback(vopts, "on_exit", on_exit)) {
+ if (tv_dict_get_callback(vopts, S_LEN("on_stdout"), on_stdout)
+ &&tv_dict_get_callback(vopts, S_LEN("on_stderr"), on_stderr)
+ && tv_dict_get_callback(vopts, S_LEN("on_exit"), on_exit)) {
vopts->dv_refcount++;
return true;
}
@@ -23056,8 +22465,9 @@ static inline bool common_job_start(TerminalJobData *data, typval_T *rettv)
data->refcount++;
char *cmd = xstrdup(proc->argv[0]);
- if (!process_spawn(proc)) {
- EMSG2(_(e_jobspawn), cmd);
+ int status = process_spawn(proc);
+ if (status) {
+ EMSG3(_(e_jobspawn), os_strerror(status), cmd);
xfree(cmd);
if (proc->type == kProcessTypePty) {
xfree(data->proc.pty.term_name);
@@ -23070,8 +22480,9 @@ static inline bool common_job_start(TerminalJobData *data, typval_T *rettv)
if (data->rpc) {
- // the rpc channel takes over the in and out streams
- channel_from_process(proc, data->id);
+ eval_format_source_name_line((char *)IObuff, sizeof(IObuff));
+ // RPC channel takes over the in/out streams.
+ channel_from_process(proc, data->id, (char *)IObuff);
} else {
wstream_init(proc->in, 0);
if (proc->out) {
@@ -23115,7 +22526,7 @@ static inline void process_job_event(TerminalJobData *data, Callback *callback,
JobEvent event_data;
event_data.received = NULL;
if (buf) {
- event_data.received = list_alloc();
+ event_data.received = tv_list_alloc();
char *ptr = buf;
size_t remaining = count;
size_t off = 0;
@@ -23123,7 +22534,7 @@ static inline void process_job_event(TerminalJobData *data, Callback *callback,
while (off < remaining) {
// append the line
if (ptr[off] == NL) {
- list_append_string(event_data.received, (uint8_t *)ptr, off);
+ tv_list_append_string(event_data.received, ptr, off);
size_t skip = off + 1;
ptr += skip;
remaining -= skip;
@@ -23136,7 +22547,7 @@ static inline void process_job_event(TerminalJobData *data, Callback *callback,
}
off++;
}
- list_append_string(event_data.received, (uint8_t *)ptr, off);
+ tv_list_append_string(event_data.received, ptr, off);
} else {
event_data.status = status;
}
@@ -23179,11 +22590,10 @@ static void on_job_output(Stream *stream, TerminalJobData *data, RBuffer *buf,
terminal_receive(data->term, ptr, count);
}
+ rbuffer_consumed(buf, count);
if (callback->type != kCallbackNone) {
process_job_event(data, callback, type, ptr, count, 0);
}
-
- rbuffer_consumed(buf, count);
}
static void eval_job_process_exit_cb(Process *proc, int status, void *d)
@@ -23283,10 +22693,9 @@ static void on_job_event(JobEvent *ev)
argv[2].v_lock = 0;
argv[2].vval.v_string = (uint8_t *)ev->type;
- typval_T rettv;
- init_tv(&rettv);
+ typval_T rettv = TV_INITIAL_VALUE;
callback_call(ev->callback, 3, argv, &rettv);
- clear_tv(&rettv);
+ tv_clear(&rettv);
}
static TerminalJobData *find_job(uint64_t id)
@@ -23309,8 +22718,8 @@ static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv)
return;
}
- list_T *args = list_alloc();
- list_append_string(args, argvars[0].vval.v_string, -1);
+ list_T *args = tv_list_alloc();
+ tv_list_append_string(args, (const char *)argvars[0].vval.v_string, -1);
*rettv = eval_call_provider(name, "eval", args);
}
@@ -23342,11 +22751,12 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
arguments->lv_refcount++;
int dummy;
- (void)call_func((uint8_t *)func,
+ (void)call_func((const char_u *)func,
name_len,
&rettv,
2,
argvars,
+ NULL,
curwin->w_cursor.lnum,
curwin->w_cursor.lnum,
&dummy,
@@ -23354,7 +22764,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
NULL,
NULL);
- list_unref(arguments);
+ tv_list_unref(arguments);
// Restore caller scope information
restore_funccal(provider_caller_scope.funccalp);
provider_caller_scope = saved_provider_caller_scope;
@@ -23363,14 +22773,16 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
return rettv;
}
-bool eval_has_provider(char *name)
+bool eval_has_provider(const char *name)
{
-#define check_provider(name) \
+#define CHECK_PROVIDER(name) \
if (has_##name == -1) { \
- has_##name = !!find_func((uint8_t *)"provider#" #name "#Call"); \
+ has_##name = !!find_func((char_u *)"provider#" #name "#Call"); \
if (!has_##name) { \
- script_autoload((uint8_t *)"provider#" #name "#Call", false); \
- has_##name = !!find_func((uint8_t *)"provider#" #name "#Call"); \
+ script_autoload("provider#" #name "#Call", \
+ sizeof("provider#" #name "#Call") - 1, \
+ false); \
+ has_##name = !!find_func((char_u *)"provider#" #name "#Call"); \
} \
}
@@ -23379,107 +22791,27 @@ bool eval_has_provider(char *name)
static int has_python3 = -1;
static int has_ruby = -1;
- if (!strcmp(name, "clipboard")) {
- check_provider(clipboard);
+ if (strequal(name, "clipboard")) {
+ CHECK_PROVIDER(clipboard);
return has_clipboard;
- } else if (!strcmp(name, "python3")) {
- check_provider(python3);
+ } else if (strequal(name, "python3")) {
+ CHECK_PROVIDER(python3);
return has_python3;
- } else if (!strcmp(name, "python")) {
- check_provider(python);
+ } else if (strequal(name, "python")) {
+ CHECK_PROVIDER(python);
return has_python;
- } else if (!strcmp(name, "ruby")) {
- check_provider(ruby);
+ } else if (strequal(name, "ruby")) {
+ CHECK_PROVIDER(ruby);
return has_ruby;
}
return false;
}
-// Compute the `DictWatcher` address from a QUEUE node. This only exists for
-// .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer arithmetic).
-static DictWatcher *dictwatcher_node_data(QUEUE *q)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
-{
- return QUEUE_DATA(q, DictWatcher, node);
-}
-
-// Send a change notification to all `dict` watchers that match `key`.
-static void dictwatcher_notify(dict_T *dict, const char *key, typval_T *newtv,
- typval_T *oldtv)
- FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2)
-{
- typval_T argv[4];
- for (size_t i = 0; i < ARRAY_SIZE(argv); i++) {
- init_tv(argv + i);
- }
-
- argv[0].v_type = VAR_DICT;
- argv[0].vval.v_dict = dict;
- argv[1].v_type = VAR_STRING;
- argv[1].vval.v_string = (char_u *)xstrdup(key);
- argv[2].v_type = VAR_DICT;
- argv[2].vval.v_dict = dict_alloc();
- argv[2].vval.v_dict->dv_refcount++;
-
- if (newtv) {
- dictitem_T *v = dictitem_alloc((char_u *)"new");
- copy_tv(newtv, &v->di_tv);
- dict_add(argv[2].vval.v_dict, v);
- }
-
- if (oldtv) {
- dictitem_T *v = dictitem_alloc((char_u *)"old");
- copy_tv(oldtv, &v->di_tv);
- dict_add(argv[2].vval.v_dict, v);
- }
-
- typval_T rettv;
-
- QUEUE *w;
- QUEUE_FOREACH(w, &dict->watchers) {
- DictWatcher *watcher = dictwatcher_node_data(w);
- if (!watcher->busy && dictwatcher_matches(watcher, key)) {
- init_tv(&rettv);
- watcher->busy = true;
- callback_call(&watcher->callback, 3, argv, &rettv);
- watcher->busy = false;
- clear_tv(&rettv);
- }
- }
-
- for (size_t i = 1; i < ARRAY_SIZE(argv); i++) {
- clear_tv(argv + i);
- }
-}
-
-// Test if `key` matches with with `watcher->key_pattern`
-static bool dictwatcher_matches(DictWatcher *watcher, const char *key)
- FUNC_ATTR_NONNULL_ALL
-{
- // For now only allow very simple globbing in key patterns: a '*' at the end
- // of the string means it should match everything up to the '*' instead of the
- // whole string.
- char *nul = strchr(watcher->key_pattern, NUL);
- size_t len = nul - watcher->key_pattern;
- if (*(nul - 1) == '*') {
- return !strncmp(key, watcher->key_pattern, len - 1);
- } else {
- return !strcmp(key, watcher->key_pattern);
- }
-}
-
-// Perform all necessary cleanup for a `DictWatcher` instance.
-static void dictwatcher_free(DictWatcher *watcher)
- FUNC_ATTR_NONNULL_ALL
-{
- callback_free(&watcher->callback);
- xfree(watcher->key_pattern);
- xfree(watcher);
-}
-
-// Check if `d` has at least one watcher.
-static bool is_watched(dict_T *d)
+/// Writes "<sourcing_name>:<sourcing_lnum>" to `buf[bufsize]`.
+void eval_format_source_name_line(char *buf, size_t bufsize)
{
- return d && !QUEUE_EMPTY(&d->watchers);
+ snprintf(buf, bufsize, "%s:%" PRIdLINENR,
+ (sourcing_name ? sourcing_name : (char_u *)"?"),
+ (sourcing_name ? sourcing_lnum : 0));
}