aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval')
-rw-r--r--src/nvim/eval/buffer.c43
-rw-r--r--src/nvim/eval/buffer.h9
-rw-r--r--src/nvim/eval/decode.c34
-rw-r--r--src/nvim/eval/decode.h12
-rw-r--r--src/nvim/eval/encode.c29
-rw-r--r--src/nvim/eval/encode.h11
-rw-r--r--src/nvim/eval/executor.c11
-rw-r--r--src/nvim/eval/executor.h8
-rw-r--r--src/nvim/eval/funcs.c2257
-rw-r--r--src/nvim/eval/funcs.h22
-rw-r--r--src/nvim/eval/gc.c3
-rw-r--r--src/nvim/eval/gc.h7
-rw-r--r--src/nvim/eval/typval.c1382
-rw-r--r--src/nvim/eval/typval.h117
-rw-r--r--src/nvim/eval/typval_defs.h109
-rw-r--r--src/nvim/eval/typval_encode.c.h109
-rw-r--r--src/nvim/eval/typval_encode.h53
-rw-r--r--src/nvim/eval/userfunc.c1170
-rw-r--r--src/nvim/eval/userfunc.h27
-rw-r--r--src/nvim/eval/vars.c1074
-rw-r--r--src/nvim/eval/vars.h12
-rw-r--r--src/nvim/eval/window.c113
-rw-r--r--src/nvim/eval/window.h11
23 files changed, 3708 insertions, 2915 deletions
diff --git a/src/nvim/eval/buffer.c b/src/nvim/eval/buffer.c
index 2f37d1ba2e..c60a104381 100644
--- a/src/nvim/eval/buffer.c
+++ b/src/nvim/eval/buffer.c
@@ -1,12 +1,9 @@
-// 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/buffer.c: Buffer related builtin functions
#include <stdbool.h>
#include <string.h>
-#include "nvim/ascii.h"
+#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
@@ -17,17 +14,18 @@
#include "nvim/eval/funcs.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
+#include "nvim/func_attr.h"
#include "nvim/globals.h"
-#include "nvim/macros.h"
+#include "nvim/macros_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/move.h"
#include "nvim/path.h"
-#include "nvim/pos.h"
+#include "nvim/pos_defs.h"
#include "nvim/sign.h"
-#include "nvim/types.h"
+#include "nvim/types_defs.h"
#include "nvim/undo.h"
-#include "nvim/vim.h"
+#include "nvim/vim_defs.h"
typedef struct {
win_T *cob_curwin_save;
@@ -126,7 +124,7 @@ static void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, typval_
FUNC_ATTR_NONNULL_ARG(4, 5)
{
linenr_T lnum = lnum_arg + (append ? 1 : 0);
- long added = 0;
+ int added = 0;
// When using the current buffer ml_mfp will be set if needed. Useful when
// setline() is used on startup. For other buffers the buffer must be
@@ -160,10 +158,7 @@ static void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, typval_
if (lines->v_type == VAR_LIST) {
l = lines->vval.v_list;
if (l == NULL || tv_list_len(l) == 0) {
- // set proper return code
- if (lnum > curbuf->b_ml.ml_line_count) {
- rettv->vval.v_number = 1; // FAIL
- }
+ // not appending anything always succeeds
goto cleanup;
}
li = tv_list_first(l);
@@ -172,7 +167,7 @@ static void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, typval_
}
// Default result is zero == OK.
- for (;;) {
+ while (true) {
if (lines->v_type == VAR_LIST) {
// List argument, get next string.
if (li == NULL) {
@@ -304,7 +299,10 @@ void f_bufload(typval_T *argvars, typval_T *unused, EvalFuncData fptr)
buf_T *buf = get_buf_arg(&argvars[0]);
if (buf != NULL) {
- buffer_ensure_loaded(buf);
+ if (swap_exists_action != SEA_READONLY) {
+ swap_exists_action = SEA_NONE;
+ }
+ buf_ensure_loaded(buf);
}
}
@@ -442,7 +440,7 @@ void f_deletebufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (last > curbuf->b_ml.ml_line_count) {
last = curbuf->b_ml.ml_line_count;
}
- const long count = last - first + 1;
+ const int count = last - first + 1;
// When coming here from Insert mode, sync undo, so that this can be
// undone separately from what was previously inserted.
@@ -487,8 +485,7 @@ static dict_T *get_buffer_info(buf_T *buf)
dict_T *const dict = tv_dict_alloc();
tv_dict_add_nr(dict, S_LEN("bufnr"), buf->b_fnum);
- tv_dict_add_str(dict, S_LEN("name"),
- buf->b_ffname != NULL ? (const char *)buf->b_ffname : "");
+ tv_dict_add_str(dict, S_LEN("name"), buf->b_ffname != NULL ? buf->b_ffname : "");
tv_dict_add_nr(dict, S_LEN("lnum"),
buf == curbuf ? curwin->w_cursor.lnum : buflist_findlnum(buf));
tv_dict_add_nr(dict, S_LEN("linecount"), buf->b_ml.ml_line_count);
@@ -496,8 +493,7 @@ static dict_T *get_buffer_info(buf_T *buf)
tv_dict_add_nr(dict, S_LEN("listed"), buf->b_p_bl);
tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf));
tv_dict_add_nr(dict, S_LEN("changedtick"), buf_get_changedtick(buf));
- tv_dict_add_nr(dict, S_LEN("hidden"),
- buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0);
+ tv_dict_add_nr(dict, S_LEN("hidden"), buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0);
// Get a reference to buffer variables
tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars);
@@ -511,7 +507,7 @@ static dict_T *get_buffer_info(buf_T *buf)
}
tv_dict_add_list(dict, S_LEN("windows"), windows);
- if (buf->b_signlist != NULL) {
+ if (buf->b_signs) {
// List of signs placed in this buffer
tv_dict_add_list(dict, S_LEN("signs"), get_buffer_signs(buf));
}
@@ -609,13 +605,12 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli
}
tv_list_alloc_ret(rettv, end - start + 1);
while (start <= end) {
- tv_list_append_string(rettv->vval.v_list,
- (const char *)ml_get_buf(buf, start++, false), -1);
+ tv_list_append_string(rettv->vval.v_list, ml_get_buf(buf, start++), -1);
}
} else {
rettv->v_type = VAR_STRING;
rettv->vval.v_string = ((start >= 1 && start <= buf->b_ml.ml_line_count)
- ? xstrdup(ml_get_buf(buf, start, false)) : NULL);
+ ? xstrdup(ml_get_buf(buf, start)) : NULL);
}
}
diff --git a/src/nvim/eval/buffer.h b/src/nvim/eval/buffer.h
index 4a2f8f9e94..1d346b99a5 100644
--- a/src/nvim/eval/buffer.h
+++ b/src/nvim/eval/buffer.h
@@ -1,10 +1,9 @@
-#ifndef NVIM_EVAL_BUFFER_H
-#define NVIM_EVAL_BUFFER_H
+#pragma once
-#include "nvim/buffer_defs.h"
-#include "nvim/eval/typval_defs.h"
+#include "nvim/buffer_defs.h" // IWYU pragma: keep
+#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
+#include "nvim/types_defs.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/buffer.h.generated.h"
#endif
-#endif // NVIM_EVAL_BUFFER_H
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
index cd1479f150..03f79fca84 100644
--- a/src/nvim/eval/decode.c
+++ b/src/nvim/eval/decode.c
@@ -1,6 +1,3 @@
-// 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
-
#include <assert.h>
#include <msgpack/object.h>
#include <stdbool.h>
@@ -10,22 +7,22 @@
#include <string.h>
#include "klib/kvec.h"
-#include "nvim/ascii.h"
+#include "nvim/ascii_defs.h"
#include "nvim/charset.h"
#include "nvim/eval.h"
#include "nvim/eval/decode.h"
#include "nvim/eval/encode.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
+#include "nvim/func_attr.h"
#include "nvim/garray.h"
#include "nvim/gettext.h"
-#include "nvim/hashtab.h"
-#include "nvim/macros.h"
+#include "nvim/macros_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/types.h"
-#include "nvim/vim.h"
+#include "nvim/types_defs.h"
+#include "nvim/vim_defs.h"
/// Helper structure for container_struct
typedef struct {
@@ -148,7 +145,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, ValuesStack *const stack
assert(!(key.is_special_string
|| key.val.vval.v_string == NULL
|| *key.val.vval.v_string == NUL));
- dictitem_T *const obj_di = tv_dict_item_alloc((const char *)key.val.vval.v_string);
+ dictitem_T *const obj_di = tv_dict_item_alloc(key.val.vval.v_string);
tv_clear(&key.val);
if (tv_dict_add(last_container.container.vval.v_dict, obj_di)
== FAIL) {
@@ -179,8 +176,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, ValuesStack *const stack
&& (obj.is_special_string
|| obj.val.vval.v_string == NULL
|| *obj.val.vval.v_string == NUL
- || tv_dict_find(last_container.container.vval.v_dict,
- (const char *)obj.val.vval.v_string, -1))) {
+ || tv_dict_find(last_container.container.vval.v_dict, obj.val.vval.v_string, -1))) {
tv_clear(&obj.val);
// Restart
@@ -228,7 +224,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, ValuesStack *const stack
///
/// @param[out] ret_tv Address where new special dictionary is saved.
/// @param[in] len Expected number of items to be populated before list
-/// becomes accessible from VimL. It is still valid to
+/// becomes accessible from Vimscript. It is still valid to
/// underpopulate a list, value only controls how many elements
/// will be allocated in advance. @see ListLenSpecials.
///
@@ -439,7 +435,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len,
t += 4;
uvarnumber_T ch;
vim_str2nr(ubuf, NULL, NULL,
- STR2NR_HEX | STR2NR_FORCE, NULL, &ch, 4, true);
+ STR2NR_HEX | STR2NR_FORCE, NULL, &ch, 4, true, NULL);
if (ch == 0) {
hasnul = true;
}
@@ -608,7 +604,7 @@ parse_json_number_check:
// Convert integer
varnumber_T nr;
int num_len;
- vim_str2nr(s, NULL, &num_len, 0, &nr, NULL, (int)(p - s), true);
+ vim_str2nr(s, NULL, &num_len, 0, &nr, NULL, (int)(p - s), true, NULL);
if ((int)exp_num_len != num_len) {
semsg(_("E685: internal error: while converting number \"%.*s\" "
"to integer vim_str2nr consumed %i bytes in place of %zu"),
@@ -646,7 +642,7 @@ parse_json_number_ret:
} \
} while (0)
-/// Convert JSON string into VimL object
+/// Convert JSON string into Vimscript object
///
/// @param[in] buf String to convert. UTF-8 encoding is assumed.
/// @param[in] buf_len Length of the string.
@@ -922,7 +918,7 @@ json_decode_string_ret:
#undef DICT_LEN
-/// Convert msgpack object to a VimL one
+/// Convert msgpack object to a Vimscript one
int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -966,7 +962,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
}
break;
case MSGPACK_OBJECT_NEGATIVE_INTEGER:
- if (mobj.via.i64 >= VARNUMBER_MIN) { // -V547
+ if (mobj.via.i64 >= VARNUMBER_MIN) {
*rettv = (typval_T) {
.v_type = VAR_NUMBER,
.v_lock = VAR_UNLOCKED,
@@ -987,12 +983,8 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
tv_list_append_number(list, (varnumber_T)(n & 0x7FFFFFFF));
}
break;
-#ifdef NVIM_MSGPACK_HAS_FLOAT32
case MSGPACK_OBJECT_FLOAT32:
case MSGPACK_OBJECT_FLOAT64:
-#else
- case MSGPACK_OBJECT_FLOAT:
-#endif
*rettv = (typval_T) {
.v_type = VAR_FLOAT,
.v_lock = VAR_UNLOCKED,
diff --git a/src/nvim/eval/decode.h b/src/nvim/eval/decode.h
index f1be5a1f69..c0d10a469a 100644
--- a/src/nvim/eval/decode.h
+++ b/src/nvim/eval/decode.h
@@ -1,13 +1,11 @@
-#ifndef NVIM_EVAL_DECODE_H
-#define NVIM_EVAL_DECODE_H
+#pragma once
-#include <msgpack.h>
-#include <stddef.h>
+#include <msgpack.h> // IWYU pragma: keep
+#include <stddef.h> // IWYU pragma: keep
-#include "nvim/eval/typval.h"
-#include "nvim/globals.h"
+#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
+#include "nvim/types_defs.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/decode.h.generated.h"
#endif
-#endif // NVIM_EVAL_DECODE_H
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
index c2f1eae8af..8505c30fad 100644
--- a/src/nvim/eval/encode.c
+++ b/src/nvim/eval/encode.c
@@ -1,9 +1,6 @@
-// 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
-
/// @file encode.c
///
-/// File containing functions for encoding and decoding VimL values.
+/// File containing functions for encoding and decoding Vimscript values.
///
/// Split out from eval.c.
@@ -17,7 +14,7 @@
#include "klib/kvec.h"
#include "msgpack/pack.h"
-#include "nvim/ascii.h"
+#include "nvim/ascii_defs.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
#include "nvim/eval/typval.h"
@@ -25,14 +22,14 @@
#include "nvim/garray.h"
#include "nvim/gettext.h"
#include "nvim/hashtab.h"
-#include "nvim/macros.h"
+#include "nvim/macros_defs.h"
#include "nvim/math.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/strings.h"
-#include "nvim/types.h"
-#include "nvim/vim.h" // For _()
+#include "nvim/types_defs.h"
+#include "nvim/vim_defs.h" // For _()
const char *const encode_bool_var_names[] = {
[kBoolVarTrue] = "v:true",
@@ -152,9 +149,9 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
? 0
: (v.data.l.li == NULL
? tv_list_len(v.data.l.list) - 1
- : (int)tv_list_idx_of_item(v.data.l.list,
- TV_LIST_ITEM_PREV(v.data.l.list,
- v.data.l.li))));
+ : tv_list_idx_of_item(v.data.l.list,
+ TV_LIST_ITEM_PREV(v.data.l.list,
+ v.data.l.li))));
const listitem_T *const li = (v.data.l.li == NULL
? tv_list_last(v.data.l.list)
: TV_LIST_ITEM_PREV(v.data.l.list,
@@ -298,7 +295,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
- const char *const buf_ = (const char *)(buf); \
+ const char *const buf_ = (buf); \
if ((buf) == NULL) { \
ga_concat(gap, "''"); \
} else { \
@@ -376,7 +373,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
do { \
- const char *const fun_ = (const char *)(fun); \
+ const char *const fun_ = (fun); \
if (fun_ == NULL) { \
internal_error("string(): NULL function name"); \
ga_concat(gap, "function(NULL"); \
@@ -418,7 +415,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
ga_concat(gap, "v:null")
#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
- ga_concat(gap, ((num)? "v:true": "v:false"))
+ ga_concat(gap, ((num) ? "v:true" : "v:false"))
#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num)
@@ -544,7 +541,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
#undef TYPVAL_ENCODE_CONV_BOOL
#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
- ga_concat(gap, ((num)? "true": "false"))
+ ga_concat(gap, ((num) ? "true" : "false"))
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) \
@@ -712,7 +709,7 @@ static inline int convert_to_json_string(garray_T *const gap, const char *const
#undef TYPVAL_ENCODE_CONV_STRING
#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
- if (convert_to_json_string(gap, (const char *)(buf), (len)) != OK) { \
+ if (convert_to_json_string(gap, (buf), (len)) != OK) { \
return FAIL; \
} \
} while (0)
diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h
index 41e7614fc0..26a3286f2b 100644
--- a/src/nvim/eval/encode.h
+++ b/src/nvim/eval/encode.h
@@ -1,5 +1,4 @@
-#ifndef NVIM_EVAL_ENCODE_H
-#define NVIM_EVAL_ENCODE_H
+#pragma once
#include <msgpack.h>
#include <msgpack/pack.h>
@@ -9,10 +8,9 @@
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
-#include "nvim/garray.h"
-#include "nvim/vim.h"
+#include "nvim/garray_defs.h"
-/// Convert VimL value to msgpack string
+/// Convert Vimscript value to msgpack string
///
/// @param[out] packer Packer to save results in.
/// @param[in] tv Dumped value.
@@ -21,7 +19,7 @@
/// @return OK in case of success, FAIL otherwise.
int encode_vim_to_msgpack(msgpack_packer *packer, typval_T *tv, const char *objname);
-/// Convert VimL value to :echo output
+/// Convert Vimscript value to :echo output
///
/// @param[out] packer Packer to save results in.
/// @param[in] tv Dumped value.
@@ -74,4 +72,3 @@ extern const char *const encode_special_var_names[];
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/encode.h.generated.h"
#endif
-#endif // NVIM_EVAL_ENCODE_H
diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c
index 9caea2fef1..dc23fcdc72 100644
--- a/src/nvim/eval/executor.c
+++ b/src/nvim/eval/executor.c
@@ -1,6 +1,3 @@
-// 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
-
#include <inttypes.h>
#include <stdlib.h>
@@ -8,19 +5,21 @@
#include "nvim/eval/executor.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
+#include "nvim/func_attr.h"
#include "nvim/garray.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/message.h"
#include "nvim/strings.h"
-#include "nvim/types.h"
-#include "nvim/vim.h"
+#include "nvim/types_defs.h"
+#include "nvim/vim_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/executor.c.generated.h" // IWYU pragma: export
#endif
-char *e_listidx = N_("E684: list index out of range: %" PRId64);
+char *e_list_index_out_of_range_nr
+ = N_("E684: List index out of range: %" PRId64);
/// Handle tv1 += tv2, -=, *=, /=, %=, .=
///
diff --git a/src/nvim/eval/executor.h b/src/nvim/eval/executor.h
index 3d789f76a5..d36ce08542 100644
--- a/src/nvim/eval/executor.h
+++ b/src/nvim/eval/executor.h
@@ -1,11 +1,9 @@
-#ifndef NVIM_EVAL_EXECUTOR_H
-#define NVIM_EVAL_EXECUTOR_H
+#pragma once
-#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
-extern char *e_listidx;
+extern char *e_list_index_out_of_range_nr;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/executor.h.generated.h"
#endif
-#endif // NVIM_EVAL_EXECUTOR_H
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 6b580b5312..310ac29f88 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -1,6 +1,3 @@
-// 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
-
#include <assert.h>
#include <fcntl.h>
#include <float.h>
@@ -11,11 +8,13 @@
#include <msgpack/pack.h>
#include <msgpack/unpack.h>
#include <signal.h>
+#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <time.h>
#include <uv.h>
@@ -25,14 +24,15 @@
#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
-#include "nvim/ascii.h"
-#include "nvim/assert.h"
+#include "nvim/ascii_defs.h"
+#include "nvim/assert_defs.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/channel.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/context.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
@@ -44,6 +44,7 @@
#include "nvim/eval/executor.h"
#include "nvim/eval/funcs.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
#include "nvim/eval/window.h"
@@ -57,41 +58,40 @@
#include "nvim/ex_getln.h"
#include "nvim/file_search.h"
#include "nvim/fileio.h"
+#include "nvim/func_attr.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
-#include "nvim/grid_defs.h"
-#include "nvim/hashtab.h"
+#include "nvim/grid.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
#include "nvim/input.h"
+#include "nvim/insexpand.h"
#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
-#include "nvim/macros.h"
+#include "nvim/macros_defs.h"
#include "nvim/main.h"
#include "nvim/mark.h"
#include "nvim/math.h"
#include "nvim/mbyte.h"
-#include "nvim/memfile_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/menu.h"
#include "nvim/message.h"
-#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/msgpack_rpc/channel.h"
-#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/msgpack_rpc/server.h"
#include "nvim/normal.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/option_vars.h"
#include "nvim/optionstr.h"
#include "nvim/os/dl.h"
#include "nvim/os/fileio.h"
-#include "nvim/os/fs_defs.h"
+#include "nvim/os/fs.h"
#include "nvim/os/os.h"
#include "nvim/os/pty_process.h"
#include "nvim/os/shell.h"
@@ -100,7 +100,7 @@
#include "nvim/path.h"
#include "nvim/plines.h"
#include "nvim/popupmenu.h"
-#include "nvim/pos.h"
+#include "nvim/pos_defs.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
@@ -113,9 +113,8 @@
#include "nvim/syntax.h"
#include "nvim/tag.h"
#include "nvim/ui.h"
-#include "nvim/undo.h"
#include "nvim/version.h"
-#include "nvim/vim.h"
+#include "nvim/vim_defs.h"
#include "nvim/window.h"
/// Describe data to return from find_some_match()
@@ -145,11 +144,17 @@ PRAGMA_DIAG_POP
PRAGMA_DIAG_POP
#endif
-static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob");
-static char *e_invalwindow = N_("E957: Invalid window number");
-static char *e_reduceempty = N_("E998: Reduce of an empty %s with no initial value");
-static char e_using_number_as_bool_nr[]
- = N_("E1023: Using a Number as a Bool: %d");
+static const char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob");
+static const char *e_invalwindow = N_("E957: Invalid window number");
+static const char e_argument_of_str_must_be_list_string_or_dictionary[]
+ = N_("E706: Argument of %s must be a List, String or Dictionary");
+static const char e_invalid_submatch_number_nr[]
+ = N_("E935: Invalid submatch number: %d");
+static const char *e_reduceempty = N_("E998: Reduce of an empty %s with no initial value");
+static const char e_string_list_or_blob_required[]
+ = N_("E1098: String, List or Blob required");
+static const char e_missing_function_argument[]
+ = N_("E1132: Missing function argument");
/// Dummy va_list for passing to vim_snprintf
///
@@ -226,6 +231,31 @@ const EvalFuncDef *find_internal_func(const char *const name)
return index >= 0 ? &functions[index] : NULL;
}
+/// Check the argument count to use for internal function "fdef".
+/// @return -1 for failure, 0 if no method base accepted, 1 if method base is
+/// first argument, 2 if method base is second argument, etc.
+int check_internal_func(const EvalFuncDef *const fdef, const int argcount)
+ FUNC_ATTR_NONNULL_ALL
+{
+ int res;
+
+ if (argcount < fdef->min_argc) {
+ res = FCERR_TOOFEW;
+ } else if (argcount > fdef->max_argc) {
+ res = FCERR_TOOMANY;
+ } else {
+ return fdef->base_arg;
+ }
+
+ const char *const name = fdef->name;
+ if (res == FCERR_TOOMANY) {
+ semsg(_(e_toomanyarg), name);
+ } else {
+ semsg(_(e_toofewarg), name);
+ }
+ return -1;
+}
+
int call_internal_func(const char *const fname, const int argcount, typval_T *const argvars,
typval_T *const rettv)
FUNC_ATTR_NONNULL_ALL
@@ -261,6 +291,9 @@ int call_internal_method(const char *const fname, const int argcount, typval_T *
typval_T argv[MAX_FUNC_ARGS + 1];
const ptrdiff_t base_index = fdef->base_arg == BASE_LAST ? argcount : fdef->base_arg - 1;
+ if (argcount < base_index) {
+ return FCERR_TOOFEW;
+ }
memcpy(argv, argvars, (size_t)base_index * sizeof(typval_T));
argv[base_index] = *basetv;
memcpy(argv + base_index + 1, argvars + base_index,
@@ -319,11 +352,12 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
Object result = handler.fn(VIML_INTERNAL_CALL, args, &res_arena, &err);
if (ERROR_SET(&err)) {
- semsg_multiline((const char *)e_api_error, err.msg);
+ semsg_multiline(e_api_error, err.msg);
goto end;
}
if (!object_to_vim(result, rettv, &err)) {
+ assert(ERROR_SET(&err));
semsg(_("Error converting the call result: %s"), err.msg);
}
@@ -448,7 +482,7 @@ buf_T *tv_get_buf(typval_T *tv, int curtab_only)
int save_magic = p_magic;
p_magic = true;
char *save_cpo = p_cpo;
- p_cpo = empty_option;
+ p_cpo = empty_string_option;
buf_T *buf = buflist_findnr(buflist_findpat(name, name + strlen(name),
true, false, curtab_only));
@@ -492,7 +526,7 @@ buf_T *get_buf_arg(typval_T *arg)
/// "byte2line(byte)" function
static void f_byte2line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- long boff = tv_get_number(&argvars[0]) - 1;
+ int boff = (int)tv_get_number(&argvars[0]) - 1;
if (boff < 0) {
rettv->vval.v_number = -1;
} else {
@@ -501,41 +535,6 @@ static void f_byte2line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
-static void byteidx(typval_T *argvars, typval_T *rettv, int comp)
-{
- 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) {
- return;
- }
-
- const char *t = str;
- for (; idx > 0; idx--) {
- if (*t == NUL) { // EOL reached.
- return;
- }
- if (comp) {
- t += utf_ptr2len(t);
- } else {
- t += utfc_ptr2len(t);
- }
- }
- rettv->vval.v_number = (varnumber_T)(t - str);
-}
-
-/// "byteidx()" function
-static void f_byteidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- byteidx(argvars, rettv, false);
-}
-
-/// "byteidxcomp()" function
-static void f_byteidxcomp(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- byteidx(argvars, rettv, true);
-}
-
/// "call(func, arglist [, dict])" function
static void f_call(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -563,14 +562,13 @@ static void f_call(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
func = (char *)tv_get_string(&argvars[0]);
}
- if (*func == NUL) {
- return; // type error or empty name
+ if (func == NULL || *func == NUL) {
+ return; // type error, empty name or null function
}
dict_T *selfdict = NULL;
if (argvars[2].v_type != VAR_UNKNOWN) {
- if (argvars[2].v_type != VAR_DICT) {
- emsg(_(e_dictreq));
+ if (tv_check_for_dict_arg(argvars, 2) == FAIL) {
if (owned) {
func_unref(func);
}
@@ -653,7 +651,7 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
bool crlf = false;
#else
Channel *chan = find_channel(id);
- bool crlf = (chan != NULL && chan->term) ? true: false;
+ bool crlf = (chan != NULL && chan->term) ? true : false;
#endif
if (argvars[1].v_type == VAR_BLOB) {
@@ -761,53 +759,6 @@ static void f_charcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
get_col(argvars, rettv, true);
}
-/// "charidx()" function
-static void f_charidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->vval.v_number = -1;
-
- if (argvars[0].v_type != VAR_STRING
- || argvars[1].v_type != VAR_NUMBER
- || (argvars[2].v_type != VAR_UNKNOWN
- && argvars[2].v_type != VAR_NUMBER
- && argvars[2].v_type != VAR_BOOL)) {
- emsg(_(e_invarg));
- return;
- }
-
- const char *str = tv_get_string_chk(&argvars[0]);
- varnumber_T idx = tv_get_number_chk(&argvars[1], NULL);
- if (str == NULL || idx < 0) {
- return;
- }
- int countcc = 0;
- if (argvars[2].v_type != VAR_UNKNOWN) {
- countcc = (int)tv_get_number(&argvars[2]);
- }
- if (countcc < 0 || countcc > 1) {
- semsg(_(e_using_number_as_bool_nr), countcc);
- return;
- }
-
- int (*ptr2len)(const char *);
- if (countcc) {
- ptr2len = utf_ptr2len;
- } else {
- ptr2len = utfc_ptr2len;
- }
-
- const char *p;
- int len;
- for (p = str, len = 0; p <= str + idx; len++) {
- if (*p == NUL) {
- return;
- }
- p += ptr2len(p);
- }
-
- rettv->vval.v_number = len > 0 ? len - 1 : 0;
-}
-
/// "chdir(dir)" function
static void f_chdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -925,8 +876,7 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
if (!error) {
- rettv->vval.v_number = do_dialog(type, NULL, (char *)message, (char *)buttons, def, NULL,
- false);
+ rettv->vval.v_number = do_dialog(type, NULL, message, buttons, def, NULL, false);
}
}
@@ -936,10 +886,90 @@ static void f_copy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
var_item_copy(NULL, &argvars[0], rettv, false, 0);
}
+/// Count the number of times "needle" occurs in string "haystack".
+///
+/// @param ic ignore case
+static varnumber_T count_string(const char *haystack, const char *needle, bool ic)
+{
+ varnumber_T n = 0;
+ const char *p = haystack;
+
+ if (p == NULL || needle == NULL || *needle == NUL) {
+ return 0;
+ }
+
+ if (ic) {
+ const size_t len = strlen(needle);
+
+ while (*p != NUL) {
+ if (mb_strnicmp(p, needle, len) == 0) {
+ n++;
+ p += len;
+ } else {
+ MB_PTR_ADV(p);
+ }
+ }
+ } else {
+ const char *next;
+ while ((next = strstr(p, needle)) != NULL) {
+ n++;
+ p = next + strlen(needle);
+ }
+ }
+
+ return n;
+}
+
+/// Count the number of times item "needle" occurs in List "l" starting at index "idx".
+///
+/// @param ic ignore case
+static varnumber_T count_list(list_T *l, typval_T *needle, int64_t idx, bool ic)
+{
+ if (tv_list_len(l) == 0) {
+ return 0;
+ }
+
+ listitem_T *li = tv_list_find(l, (int)idx);
+ if (li == NULL) {
+ semsg(_(e_list_index_out_of_range_nr), idx);
+ return 0;
+ }
+
+ varnumber_T n = 0;
+
+ for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) {
+ if (tv_equal(TV_LIST_ITEM_TV(li), needle, ic, false)) {
+ n++;
+ }
+ }
+
+ return n;
+}
+
+/// Count the number of times item "needle" occurs in Dict "d".
+///
+/// @param ic ignore case
+static varnumber_T count_dict(dict_T *d, typval_T *needle, bool ic)
+{
+ if (d == NULL) {
+ return 0;
+ }
+
+ varnumber_T n = 0;
+
+ TV_DICT_ITER(d, di, {
+ if (tv_equal(&di->di_tv, needle, ic, false)) {
+ n++;
+ }
+ });
+
+ return n;
+}
+
/// "count()" function
static void f_count(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- long n = 0;
+ varnumber_T n = 0;
int ic = 0;
bool error = false;
@@ -947,78 +977,30 @@ static void f_count(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
ic = (int)tv_get_number_chk(&argvars[2], &error);
}
- if (argvars[0].v_type == VAR_STRING) {
- const char *expr = tv_get_string_chk(&argvars[1]);
- const char *p = argvars[0].vval.v_string;
-
- if (!error && expr != NULL && *expr != NUL && p != NULL) {
- if (ic) {
- const size_t len = strlen(expr);
-
- while (*p != NUL) {
- if (mb_strnicmp((char *)p, (char *)expr, len) == 0) {
- n++;
- p += len;
- } else {
- MB_PTR_ADV(p);
- }
- }
- } else {
- char *next;
- while ((next = strstr((char *)p, (char *)expr)) != NULL) {
- n++;
- p = next + strlen(expr);
- }
- }
+ if (!error && argvars[0].v_type == VAR_STRING) {
+ n = count_string(argvars[0].vval.v_string, tv_get_string_chk(&argvars[1]), ic);
+ } else if (!error && argvars[0].v_type == VAR_LIST) {
+ int64_t idx = 0;
+ if (argvars[2].v_type != VAR_UNKNOWN
+ && argvars[3].v_type != VAR_UNKNOWN) {
+ idx = (int64_t)tv_get_number_chk(&argvars[3], &error);
}
- } else if (argvars[0].v_type == VAR_LIST) {
- list_T *l = argvars[0].vval.v_list;
-
- if (l != NULL) {
- listitem_T *li = tv_list_first(l);
- if (argvars[2].v_type != VAR_UNKNOWN) {
- if (argvars[3].v_type != VAR_UNKNOWN) {
- long idx = tv_get_number_chk(&argvars[3], &error);
- if (!error) {
- li = tv_list_find(l, (int)idx);
- if (li == NULL) {
- semsg(_(e_listidx), (int64_t)idx);
- }
- }
- }
- if (error) {
- li = NULL;
- }
- }
-
- for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) {
- if (tv_equal(TV_LIST_ITEM_TV(li), &argvars[1], ic, false)) {
- n++;
- }
- }
+ if (!error) {
+ n = count_list(argvars[0].vval.v_list, &argvars[1], idx, ic);
}
- } else if (argvars[0].v_type == VAR_DICT) {
+ } else if (!error && argvars[0].v_type == VAR_DICT) {
dict_T *d = argvars[0].vval.v_dict;
if (d != NULL) {
- if (argvars[2].v_type != VAR_UNKNOWN) {
- if (argvars[3].v_type != VAR_UNKNOWN) {
- emsg(_(e_invarg));
- }
- }
-
- int todo = error ? 0 : (int)d->dv_hashtab.ht_used;
- for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) {
- if (!HASHITEM_EMPTY(hi)) {
- todo--;
- if (tv_equal(&TV_DICT_HI2DI(hi)->di_tv, &argvars[1], ic, false)) {
- n++;
- }
- }
+ if (argvars[2].v_type != VAR_UNKNOWN
+ && argvars[3].v_type != VAR_UNKNOWN) {
+ emsg(_(e_invarg));
+ } else {
+ n = count_dict(argvars[0].vval.v_dict, &argvars[1], ic);
}
}
- } else {
- semsg(_(e_listdictarg), "count()");
+ } else if (!error) {
+ semsg(_(e_argument_of_str_must_be_list_string_or_dictionary), "count()");
}
rettv->vval.v_number = n;
}
@@ -1042,7 +1024,7 @@ static void f_ctxget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
Dictionary ctx_dict = ctx_to_dict(ctx);
Error err = ERROR_INIT;
- object_to_vim(DICTIONARY_OBJ(ctx_dict), rettv, &err);
+ (void)object_to_vim(DICTIONARY_OBJ(ctx_dict), rettv, &err);
api_free_dictionary(ctx_dict);
api_clear_error(&err);
}
@@ -1064,17 +1046,17 @@ static void f_ctxpush(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
TV_LIST_ITER(argvars[0].vval.v_list, li, {
typval_T *tv_li = TV_LIST_ITEM_TV(li);
if (tv_li->v_type == VAR_STRING) {
- if (strequal((char *)tv_li->vval.v_string, "regs")) {
+ if (strequal(tv_li->vval.v_string, "regs")) {
types |= kCtxRegs;
- } else if (strequal((char *)tv_li->vval.v_string, "jumps")) {
+ } else if (strequal(tv_li->vval.v_string, "jumps")) {
types |= kCtxJumps;
- } else if (strequal((char *)tv_li->vval.v_string, "bufs")) {
+ } else if (strequal(tv_li->vval.v_string, "bufs")) {
types |= kCtxBufs;
- } else if (strequal((char *)tv_li->vval.v_string, "gvars")) {
+ } else if (strequal(tv_li->vval.v_string, "gvars")) {
types |= kCtxGVars;
- } else if (strequal((char *)tv_li->vval.v_string, "sfuncs")) {
+ } else if (strequal(tv_li->vval.v_string, "sfuncs")) {
types |= kCtxSFuncs;
- } else if (strequal((char *)tv_li->vval.v_string, "funcs")) {
+ } else if (strequal(tv_li->vval.v_string, "funcs")) {
types |= kCtxFuncs;
}
}
@@ -1108,14 +1090,16 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
- int save_did_emsg = did_emsg;
+ const int save_did_emsg = did_emsg;
did_emsg = false;
Dictionary dict = vim_to_object(&argvars[0]).data.dictionary;
Context tmp = CONTEXT_INIT;
- ctx_from_dict(dict, &tmp);
+ Error err = ERROR_INIT;
+ ctx_from_dict(dict, &tmp, &err);
- if (did_emsg) {
+ if (ERROR_SET(&err)) {
+ semsg("%s", err.msg);
ctx_free(&tmp);
} else {
ctx_free(ctx);
@@ -1123,6 +1107,7 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
api_free_dictionary(dict);
+ api_clear_error(&err);
did_emsg = save_did_emsg;
}
@@ -1134,12 +1119,13 @@ static void f_ctxsize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
/// Set the cursor position.
-/// If 'charcol' is true, then use the column number as a character offset.
+/// If "charcol" is true, then use the column number as a character offset.
/// Otherwise use the column number as a byte offset.
static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol)
{
- long lnum, col;
- long coladd = 0;
+ linenr_T lnum;
+ colnr_T col;
+ colnr_T coladd = 0;
bool set_curswant = true;
rettv->vval.v_number = -1;
@@ -1167,12 +1153,12 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol)
} else if (lnum == 0) {
lnum = curwin->w_cursor.lnum;
}
- col = (long)tv_get_number_chk(&argvars[1], NULL);
+ col = (colnr_T)tv_get_number_chk(&argvars[1], NULL);
if (charcol) {
- col = buf_charidx_to_byteidx(curbuf, (linenr_T)lnum, (int)col) + 1;
+ col = buf_charidx_to_byteidx(curbuf, lnum, (int)col) + 1;
}
if (argvars[2].v_type != VAR_UNKNOWN) {
- coladd = (long)tv_get_number_chk(&argvars[2], NULL);
+ coladd = (colnr_T)tv_get_number_chk(&argvars[2], NULL);
}
} else {
emsg(_(e_invarg));
@@ -1182,12 +1168,12 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol)
return; // type error; errmsg already given
}
if (lnum > 0) {
- curwin->w_cursor.lnum = (linenr_T)lnum;
+ curwin->w_cursor.lnum = lnum;
}
if (col > 0) {
- curwin->w_cursor.col = (colnr_T)col - 1;
+ curwin->w_cursor.col = col - 1;
}
- curwin->w_cursor.coladd = (colnr_T)coladd;
+ curwin->w_cursor.coladd = coladd;
// Make sure the cursor is in a valid position.
check_cursor();
@@ -1236,18 +1222,16 @@ static void f_debugbreak(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "deepcopy()" function
static void f_deepcopy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- int noref = 0;
+ if (tv_check_for_opt_bool_arg(argvars, 1) == FAIL) {
+ return;
+ }
+ varnumber_T noref = 0;
if (argvars[1].v_type != VAR_UNKNOWN) {
- noref = (int)tv_get_bool_chk(&argvars[1], NULL);
- }
- if (noref < 0 || noref > 1) {
- semsg(_(e_using_number_as_bool_nr), noref);
- } else {
- var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0
- ? get_copyID()
- : 0));
+ noref = tv_get_bool_chk(&argvars[1], NULL);
}
+
+ var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0 ? get_copyID() : 0));
}
/// "delete()" function
@@ -1546,11 +1530,11 @@ static void f_eval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *s = tv_get_string_chk(&argvars[0]);
if (s != NULL) {
- s = (const char *)skipwhite(s);
+ s = skipwhite(s);
}
const char *const expr_start = s;
- if (s == NULL || eval1((char **)&s, rettv, true) == FAIL) {
+ if (s == NULL || eval1((char **)&s, rettv, &EVALARG_EVALUATE) == FAIL) {
if (expr_start != NULL && !aborting()) {
semsg(_(e_invexpr2), expr_start);
}
@@ -1722,7 +1706,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
xfree(exp);
}
} else if (*p == '&' || *p == '+') { // Option.
- n = (get_option_tv(&p, NULL, true) == OK);
+ n = (eval_option(&p, NULL, true) == OK);
if (*skipwhite(p) != NUL) {
n = false; // Trailing garbage.
}
@@ -1752,7 +1736,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
char *p_csl_save = p_csl;
// avoid using 'completeslash' here
- p_csl = empty_option;
+ p_csl = empty_string_option;
#endif
rettv->v_type = VAR_STRING;
@@ -1769,7 +1753,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
emsg_off++;
}
size_t len;
- char *errormsg = NULL;
+ const char *errormsg = NULL;
char *result = eval_vars((char *)s, s, &len, NULL, &errormsg, NULL, false);
if (p_verbose == 0) {
emsg_off--;
@@ -1779,7 +1763,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (rettv->v_type == VAR_LIST) {
tv_list_alloc_ret(rettv, (result != NULL));
if (result != NULL) {
- tv_list_append_string(rettv->vval.v_list, (const char *)result, -1);
+ tv_list_append_string(rettv->vval.v_list, result, -1);
}
XFREE_CLEAR(result);
} else {
@@ -1805,8 +1789,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
ExpandOne(&xpc, (char *)s, NULL, options, WILD_ALL_KEEP);
tv_list_alloc_ret(rettv, xpc.xp_numfiles);
for (int i = 0; i < xpc.xp_numfiles; i++) {
- tv_list_append_string(rettv->vval.v_list,
- (const char *)xpc.xp_files[i], -1);
+ tv_list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1);
}
ExpandCleanup(&xpc);
}
@@ -1835,7 +1818,7 @@ static void f_menu_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// Expand all the special characters in a command string.
static void f_expandcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- char *errormsg = NULL;
+ const char *errormsg = NULL;
bool emsgoff = true;
if (argvars[1].v_type == VAR_DICT
@@ -1870,8 +1853,8 @@ static void f_expandcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
rettv->vval.v_string = cmdstr;
}
-/// "flatten(list[, {maxdepth}])" function
-static void f_flatten(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+/// "flatten()" and "flattennew()" functions
+static void flatten_common(typval_T *argvars, typval_T *rettv, bool make_copy)
{
bool error = false;
@@ -1880,11 +1863,11 @@ static void f_flatten(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
- long maxdepth;
+ int maxdepth;
if (argvars[1].v_type == VAR_UNKNOWN) {
maxdepth = 999999;
} else {
- maxdepth = (long)tv_get_number_chk(&argvars[1], &error);
+ maxdepth = (int)tv_get_number_chk(&argvars[1], &error);
if (error) {
return;
}
@@ -1895,92 +1878,179 @@ static void f_flatten(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
list_T *list = argvars[0].vval.v_list;
- if (list != NULL
- && !value_check_lock(tv_list_locked(list),
- N_("flatten() argument"),
- TV_TRANSLATE)
- && tv_list_flatten(list, maxdepth) == OK) {
- tv_copy(&argvars[0], rettv);
+ rettv->v_type = VAR_LIST;
+ rettv->vval.v_list = list;
+ if (list == NULL) {
+ return;
}
+
+ if (make_copy) {
+ list = tv_list_copy(NULL, list, false, get_copyID());
+ rettv->vval.v_list = list;
+ if (list == NULL) {
+ return;
+ }
+ } else {
+ if (value_check_lock(tv_list_locked(list), N_("flatten() argument"), TV_TRANSLATE)) {
+ return;
+ }
+ tv_list_ref(list);
+ }
+
+ tv_list_flatten(list, NULL, tv_list_len(list), maxdepth);
}
-/// "extend(list, list [, idx])" function
-/// "extend(dict, dict [, action])" function
-static void f_extend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+/// "flatten(list[, {maxdepth}])" function
+static void f_flatten(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- const char *const arg_errmsg = N_("extend() argument");
+ flatten_common(argvars, rettv, false);
+}
- if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) {
- bool error = false;
+/// "flattennew(list[, {maxdepth}])" function
+static void f_flattennew(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ flatten_common(argvars, rettv, true);
+}
- list_T *const l1 = argvars[0].vval.v_list;
- list_T *const l2 = argvars[1].vval.v_list;
- if (!value_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) {
- listitem_T *item;
- if (argvars[2].v_type != VAR_UNKNOWN) {
- long before = (long)tv_get_number_chk(&argvars[2], &error);
- if (error) {
- return; // Type error; errmsg already given.
- }
+/// extend() a List. Append List argvars[1] to List argvars[0] before index
+/// argvars[3] and return the resulting list in "rettv".
+///
+/// @param is_new true for extendnew()
+static void extend_list(typval_T *argvars, const char *arg_errmsg, bool is_new, typval_T *rettv)
+{
+ bool error = false;
- if (before == tv_list_len(l1)) {
- item = NULL;
- } else {
- item = tv_list_find(l1, (int)before);
- if (item == NULL) {
- semsg(_(e_listidx), (int64_t)before);
- return;
- }
- }
- } else {
+ list_T *l1 = argvars[0].vval.v_list;
+ list_T *const l2 = argvars[1].vval.v_list;
+ if (is_new || !value_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) {
+ if (is_new) {
+ l1 = tv_list_copy(NULL, l1, false, get_copyID());
+ if (l1 == NULL) {
+ return;
+ }
+ }
+
+ listitem_T *item;
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ int before = (int)tv_get_number_chk(&argvars[2], &error);
+ if (error) {
+ return; // Type error; errmsg already given.
+ }
+
+ if (before == tv_list_len(l1)) {
item = NULL;
+ } else {
+ item = tv_list_find(l1, before);
+ if (item == NULL) {
+ semsg(_(e_list_index_out_of_range_nr), (int64_t)before);
+ return;
+ }
}
- tv_list_extend(l1, l2, item);
+ } else {
+ item = NULL;
+ }
+ tv_list_extend(l1, l2, item);
+ if (is_new) {
+ *rettv = (typval_T){
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval.v_list = l1,
+ };
+ } else {
tv_copy(&argvars[0], rettv);
}
- } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type ==
- VAR_DICT) {
- dict_T *const d1 = argvars[0].vval.v_dict;
- dict_T *const d2 = argvars[1].vval.v_dict;
- if (d1 == NULL) {
- const bool locked = value_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 (!value_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) {
- const char *action = "force";
- // Check the third argument.
- if (argvars[2].v_type != VAR_UNKNOWN) {
- const char *const av[] = { "keep", "force", "error" };
+ }
+}
- 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) {
- semsg(_(e_invarg2), action);
- return;
+/// extend() a Dict. Append Dict argvars[1] to Dict argvars[0] and return the
+/// resulting Dict in "rettv".
+///
+/// @param is_new true for extendnew()
+static void extend_dict(typval_T *argvars, const char *arg_errmsg, bool is_new, typval_T *rettv)
+{
+ dict_T *d1 = argvars[0].vval.v_dict;
+ dict_T *const d2 = argvars[1].vval.v_dict;
+ if (d1 == NULL) {
+ const bool locked = value_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 (is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) {
+ if (is_new) {
+ d1 = tv_dict_copy(NULL, d1, false, get_copyID());
+ if (d1 == NULL) {
+ return;
+ }
+ }
+
+ const char *action = "force";
+ // Check the third argument.
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ const char *const av[] = { "keep", "force", "error" };
+
+ 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) {
+ semsg(_(e_invarg2), action);
+ return;
+ }
+ }
- tv_dict_extend(d1, d2, action);
+ tv_dict_extend(d1, d2, action);
+ if (is_new) {
+ *rettv = (typval_T){
+ .v_type = VAR_DICT,
+ .v_lock = VAR_UNLOCKED,
+ .vval.v_dict = d1,
+ };
+ } else {
tv_copy(&argvars[0], rettv);
}
+ }
+}
+
+/// "extend()" or "extendnew()" function.
+///
+/// @param is_new true for extendnew()
+static void extend(typval_T *argvars, typval_T *rettv, char *arg_errmsg, bool is_new)
+{
+ if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) {
+ extend_list(argvars, arg_errmsg, is_new, rettv);
+ } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) {
+ extend_dict(argvars, arg_errmsg, is_new, rettv);
} else {
- semsg(_(e_listdictarg), "extend()");
+ semsg(_(e_listdictarg), is_new ? "extendnew()" : "extend()");
}
}
+/// "extend(list, list [, idx])" function
+/// "extend(dict, dict [, action])" function
+static void f_extend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ char *errmsg = N_("extend() argument");
+ extend(argvars, rettv, errmsg, false);
+}
+
+/// "extendnew(list, list [, idx])" function
+/// "extendnew(dict, dict [, action])" function
+static void f_extendnew(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ char *errmsg = N_("extendnew() argument");
+ extend(argvars, rettv, errmsg, true);
+}
+
/// "feedkeys()" function
static void f_feedkeys(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -2053,6 +2123,9 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
}
if (*fname != NUL && !error) {
+ char *file_to_find = NULL;
+ char *search_ctx = NULL;
+
do {
if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) {
xfree(fresult);
@@ -2063,13 +2136,17 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
find_what, curbuf->b_ffname,
(find_what == FINDFILE_DIR
? ""
- : curbuf->b_p_sua));
+ : curbuf->b_p_sua),
+ &file_to_find, &search_ctx);
first = false;
if (fresult != NULL && rettv->v_type == VAR_LIST) {
- tv_list_append_string(rettv->vval.v_list, (const char *)fresult, -1);
+ tv_list_append_string(rettv->vval.v_list, fresult, -1);
}
} while ((rettv->v_type == VAR_LIST || --count > 0) && fresult != NULL);
+
+ xfree(file_to_find);
+ vim_findfile_cleanup(search_ctx);
}
if (rettv->v_type == VAR_STRING) {
@@ -2077,12 +2154,6 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
}
}
-/// "filter()" function
-static void f_filter(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- filter_map(argvars, rettv, false);
-}
-
/// "finddir({fname}[, {path}[, {count}]])" function
static void f_finddir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -2164,7 +2235,8 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "foreground()" function
static void f_foreground(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{}
+{
+}
static void f_funcref(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -2433,7 +2505,7 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
[kCdScopeTabpage] = 0, // Number of tab to look at.
};
- char *cwd = NULL; // Current working directory to print
+ char *cwd = NULL; // Current working directory to print
char *from = NULL; // The original string to copy
tabpage_T *tp = curtab; // The tabpage to look at.
@@ -2681,47 +2753,6 @@ static void f_getmarklist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
get_buf_local_marks(buf, rettv->vval.v_list);
}
-/// "getmousepos()" function
-static void f_getmousepos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- int row = mouse_row;
- int col = mouse_col;
- int grid = mouse_grid;
- varnumber_T winid = 0;
- varnumber_T winrow = 0;
- varnumber_T wincol = 0;
- linenr_T lnum = 0;
- varnumber_T column = 0;
-
- tv_dict_alloc_ret(rettv);
- dict_T *d = rettv->vval.v_dict;
-
- tv_dict_add_nr(d, S_LEN("screenrow"), (varnumber_T)mouse_row + 1);
- tv_dict_add_nr(d, S_LEN("screencol"), (varnumber_T)mouse_col + 1);
-
- win_T *wp = mouse_find_win(&grid, &row, &col);
- if (wp != NULL) {
- int height = wp->w_height + wp->w_hsep_height + wp->w_status_height;
- // The height is adjusted by 1 when there is a bottom border. This is not
- // necessary for a top border since `row` starts at -1 in that case.
- if (row < height + wp->w_border_adj[2]) {
- winid = wp->handle;
- winrow = row + 1 + wp->w_winrow_off; // Adjust by 1 for top border
- wincol = col + 1 + wp->w_wincol_off; // Adjust by 1 for left border
- if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width) {
- (void)mouse_comp_pos(wp, &row, &col, &lnum);
- col = vcol2col(wp, lnum, col);
- column = col + 1;
- }
- }
- }
- tv_dict_add_nr(d, S_LEN("winid"), winid);
- tv_dict_add_nr(d, S_LEN("winrow"), winrow);
- tv_dict_add_nr(d, S_LEN("wincol"), wincol);
- tv_dict_add_nr(d, S_LEN("line"), (varnumber_T)lnum);
- tv_dict_add_nr(d, S_LEN("column"), column);
-}
-
/// "getpid()" function
static void f_getpid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -2840,7 +2871,8 @@ static void f_gettagstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// Dummy timer callback. Used by f_wait().
static void dummy_timer_due_cb(TimeWatcher *tw, void *data)
-{}
+{
+}
/// Dummy timer close callback. Used by f_wait().
static void dummy_timer_close_cb(TimeWatcher *tw, void *data)
@@ -2867,8 +2899,8 @@ static void f_wait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
int timeout = (int)argvars[0].vval.v_number;
typval_T expr = argvars[1];
int interval = argvars[2].v_type == VAR_NUMBER
- ? (int)argvars[2].vval.v_number
- : 200; // Default.
+ ? (int)argvars[2].vval.v_number
+ : 200; // Default.
TimeWatcher *tw = xmalloc(sizeof(TimeWatcher));
// Start dummy timer.
@@ -2882,8 +2914,11 @@ static void f_wait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
bool error = false;
const int called_emsg_before = called_emsg;
+ // Flush screen updates before blocking.
+ ui_flush();
+
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, timeout,
- eval_expr_typval(&expr, &argv, 0, &exprval) != OK
+ eval_expr_typval(&expr, false, &argv, 0, &exprval) != OK
|| tv_get_number_chk(&exprval, &error)
|| called_emsg > called_emsg_before || error || got_int);
@@ -2941,8 +2976,7 @@ static void f_glob(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
WILD_ALL_KEEP);
tv_list_alloc_ret(rettv, xpc.xp_numfiles);
for (int i = 0; i < xpc.xp_numfiles; i++) {
- tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i],
- -1);
+ tv_list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1);
}
ExpandCleanup(&xpc);
}
@@ -2983,7 +3017,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (file != NULL && !error) {
garray_T ga;
ga_init(&ga, (int)sizeof(char *), 10);
- globpath((char *)tv_get_string(&argvars[0]), (char *)file, &ga, flags);
+ globpath((char *)tv_get_string(&argvars[0]), (char *)file, &ga, flags, false);
if (rettv->v_type == VAR_STRING) {
rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n");
@@ -3013,14 +3047,12 @@ static void f_glob2regpat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "gettext()" function
static void f_gettext(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- if (argvars[0].v_type != VAR_STRING
- || argvars[0].vval.v_string == NULL
- || *argvars[0].vval.v_string == NUL) {
- semsg(_(e_invarg2), tv_get_string(&argvars[0]));
- } else {
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = xstrdup(_(argvars[0].vval.v_string));
+ if (tv_check_for_nonempty_string_arg(argvars, 0) == FAIL) {
+ return;
}
+
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = xstrdup(_(argvars[0].vval.v_string));
}
/// "has()" function
@@ -3064,9 +3096,6 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
"conceal",
"cursorbind",
"cursorshape",
-#ifdef DEBUG
- "debug",
-#endif
"dialog_con",
"diff",
"digraphs",
@@ -3149,8 +3178,11 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
"windows",
"winaltkeys",
"writebackup",
+#ifdef HAVE_XATTR
+ "xattr",
+#endif
"nvim",
- "userreg",
+ "rneovim",
};
// XXX: eval_has_provider() may shell out :(
@@ -3165,7 +3197,9 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
if (!n) {
- if (STRNICMP(name, "patch", 5) == 0) {
+ if (STRNICMP(name, "gui_running", 11) == 0) {
+ n = ui_gui_attached();
+ } else if (STRNICMP(name, "patch", 5) == 0) {
if (name[5] == '-'
&& strlen(name) >= 11
&& ascii_isdigit(name[6])
@@ -3220,7 +3254,7 @@ static bool has_wsl(void)
static TriState has_wsl = kNone;
if (has_wsl == kNone) {
Error err = ERROR_INIT;
- Object o = nlua_exec(STATIC_CSTR_AS_STRING("return vim.loop.os_uname()['release']:lower()"
+ Object o = nlua_exec(STATIC_CSTR_AS_STRING("return vim.uv.os_uname()['release']:lower()"
":match('microsoft') and true or false"),
(Array)ARRAY_DICT_INIT, &err);
assert(!ERROR_SET(&err));
@@ -3254,7 +3288,7 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
[kCdScopeTabpage] = 0, // Number of tab to look at.
};
- tabpage_T *tp = curtab; // The tabpage to look at.
+ tabpage_T *tp = curtab; // The tabpage to look at.
win_T *win = curwin; // The window to look at.
rettv->v_type = VAR_NUMBER;
@@ -3353,34 +3387,6 @@ static void f_hostname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
rettv->vval.v_string = xstrdup(hostname);
}
-/// iconv() function
-static void f_iconv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- vimconv_T vimconv;
-
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
-
- const char *const str = tv_get_string(&argvars[0]);
- char buf1[NUMBUFLEN];
- char *const from = enc_canonize(enc_skip((char *)tv_get_string_buf(&argvars[1], buf1)));
- char buf2[NUMBUFLEN];
- char *const to = enc_canonize(enc_skip((char *)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 = xstrdup(str);
- } else {
- rettv->vval.v_string = string_convert(&vimconv, (char *)str, NULL);
- }
-
- convert_setup(&vimconv, NULL, NULL);
- xfree(from);
- xfree(to);
-}
-
/// "indent()" function
static void f_indent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -3395,7 +3401,7 @@ static void f_indent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "index()" function
static void f_index(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- long idx = 0;
+ int idx = 0;
bool ic = false;
rettv->vval.v_number = -1;
@@ -3422,7 +3428,7 @@ static void f_index(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
for (idx = start; idx < tv_blob_len(b); idx++) {
typval_T tv;
tv.v_type = VAR_NUMBER;
- tv.vval.v_number = tv_blob_get(b, (int)idx);
+ tv.vval.v_number = tv_blob_get(b, idx);
if (tv_equal(&tv, &argvars[1], ic, false)) {
rettv->vval.v_number = idx;
return;
@@ -3448,7 +3454,7 @@ static void f_index(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (error || idx == -1) {
item = NULL;
} else {
- item = tv_list_find(l, (int)idx);
+ item = tv_list_find(l, idx);
assert(item != NULL);
}
if (argvars[3].v_type != VAR_UNKNOWN) {
@@ -3467,6 +3473,138 @@ static void f_index(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
+/// Evaluate "expr" with the v:key and v:val arguments and return the result.
+/// The expression is expected to return a boolean value. The caller should set
+/// the VV_KEY and VV_VAL vim variables before calling this function.
+static varnumber_T indexof_eval_expr(typval_T *expr)
+{
+ typval_T argv[3];
+ argv[0] = *get_vim_var_tv(VV_KEY);
+ argv[1] = *get_vim_var_tv(VV_VAL);
+ typval_T newtv;
+ newtv.v_type = VAR_UNKNOWN;
+
+ if (eval_expr_typval(expr, false, argv, 2, &newtv) == FAIL) {
+ return false;
+ }
+
+ bool error = false;
+ varnumber_T found = tv_get_bool_chk(&newtv, &error);
+ tv_clear(&newtv);
+
+ return error ? false : found;
+}
+
+/// Evaluate "expr" for each byte in the Blob "b" starting with the byte at
+/// "startidx" and return the index of the byte where "expr" is TRUE. Returns
+/// -1 if "expr" doesn't evaluate to TRUE for any of the bytes.
+static varnumber_T indexof_blob(blob_T *b, varnumber_T startidx, typval_T *expr)
+{
+ if (b == NULL) {
+ return -1;
+ }
+
+ if (startidx < 0) {
+ // negative index: index from the last byte
+ startidx = tv_blob_len(b) + startidx;
+ if (startidx < 0) {
+ startidx = 0;
+ }
+ }
+
+ for (varnumber_T idx = startidx; idx < tv_blob_len(b); idx++) {
+ set_vim_var_nr(VV_KEY, idx);
+ set_vim_var_nr(VV_VAL, tv_blob_get(b, (int)idx));
+
+ if (indexof_eval_expr(expr)) {
+ return idx;
+ }
+ }
+
+ return -1;
+}
+
+/// Evaluate "expr" for each item in the List "l" starting with the item at
+/// "startidx" and return the index of the item where "expr" is TRUE. Returns
+/// -1 if "expr" doesn't evaluate to TRUE for any of the items.
+static varnumber_T indexof_list(list_T *l, varnumber_T startidx, typval_T *expr)
+{
+ if (l == NULL) {
+ return -1;
+ }
+
+ listitem_T *item;
+ varnumber_T idx = 0;
+ if (startidx == 0) {
+ item = tv_list_first(l);
+ } else {
+ // Start at specified item.
+ idx = tv_list_uidx(l, (int)startidx);
+ if (idx == -1) {
+ item = NULL;
+ } else {
+ item = tv_list_find(l, (int)idx);
+ assert(item != NULL);
+ }
+ }
+
+ for (; item != NULL; item = TV_LIST_ITEM_NEXT(l, item), idx++) {
+ set_vim_var_nr(VV_KEY, idx);
+ tv_copy(TV_LIST_ITEM_TV(item), get_vim_var_tv(VV_VAL));
+
+ bool found = indexof_eval_expr(expr);
+ tv_clear(get_vim_var_tv(VV_VAL));
+
+ if (found) {
+ return idx;
+ }
+ }
+
+ return -1;
+}
+
+/// "indexof()" function
+static void f_indexof(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = -1;
+
+ if (tv_check_for_list_or_blob_arg(argvars, 0) == FAIL
+ || tv_check_for_string_or_func_arg(argvars, 1) == FAIL
+ || tv_check_for_opt_dict_arg(argvars, 2) == FAIL) {
+ return;
+ }
+
+ if ((argvars[1].v_type == VAR_STRING && argvars[1].vval.v_string == NULL)
+ || (argvars[1].v_type == VAR_FUNC && argvars[1].vval.v_partial == NULL)) {
+ return;
+ }
+
+ varnumber_T startidx = 0;
+ if (argvars[2].v_type == VAR_DICT) {
+ startidx = tv_dict_get_number_def(argvars[2].vval.v_dict, "startidx", 0);
+ }
+
+ typval_T save_val;
+ typval_T save_key;
+ prepare_vimvar(VV_VAL, &save_val);
+ prepare_vimvar(VV_KEY, &save_key);
+
+ // We reset "did_emsg" to be able to detect whether an error occurred
+ // during evaluation of the expression.
+ const int save_did_emsg = did_emsg;
+ did_emsg = false;
+
+ if (argvars[0].v_type == VAR_BLOB) {
+ rettv->vval.v_number = indexof_blob(argvars[0].vval.v_blob, startidx, &argvars[1]);
+ } else {
+ rettv->vval.v_number = indexof_list(argvars[0].vval.v_list, startidx, &argvars[1]);
+ }
+
+ restore_vimvar(VV_KEY, &save_key);
+ restore_vimvar(VV_VAL, &save_val);
+ did_emsg |= save_did_emsg;
+}
+
static bool inputsecret_flag = false;
/// "input()" function
@@ -3548,7 +3686,6 @@ static void f_inputsecret(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "insert()" function
static void f_insert(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- list_T *l;
bool error = false;
if (argvars[0].v_type == VAR_BLOB) {
@@ -3560,11 +3697,11 @@ static void f_insert(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
- long before = 0;
+ int before = 0;
const int len = tv_blob_len(b);
if (argvars[2].v_type != VAR_UNKNOWN) {
- before = (long)tv_get_number_chk(&argvars[2], &error);
+ before = (int)tv_get_number_chk(&argvars[2], &error);
if (error) {
return; // type error; errmsg already given
}
@@ -3591,9 +3728,13 @@ static void f_insert(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
tv_copy(&argvars[0], rettv);
} else if (argvars[0].v_type != VAR_LIST) {
semsg(_(e_listblobarg), "insert()");
- } else if (!value_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
- N_("insert() argument"), TV_TRANSLATE)) {
- long before = 0;
+ } else {
+ list_T *l = argvars[0].vval.v_list;
+ if (value_check_lock(tv_list_locked(l), N_("insert() argument"), TV_TRANSLATE)) {
+ return;
+ }
+
+ int64_t before = 0;
if (argvars[2].v_type != VAR_UNKNOWN) {
before = tv_get_number_chk(&argvars[2], &error);
}
@@ -3606,7 +3747,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (before != tv_list_len(l)) {
item = tv_list_find(l, (int)before);
if (item == NULL) {
- semsg(_(e_listidx), (int64_t)before);
+ semsg(_(e_list_index_out_of_range_nr), before);
l = NULL;
}
}
@@ -3618,8 +3759,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
/// "interrupt()" function
-static void f_interrupt(typval_T *argvars FUNC_ATTR_UNUSED, typval_T *rettv FUNC_ATTR_UNUSED,
- EvalFuncData fptr FUNC_ATTR_UNUSED)
+static void f_interrupt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
got_int = true;
}
@@ -3796,7 +3936,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en
if (!clear_env) {
typval_T temp_env = TV_INITIAL_VALUE;
- f_environ(NULL, &temp_env, (EvalFuncData){ .nullptr = NULL });
+ f_environ(NULL, &temp_env, (EvalFuncData){ .null = NULL });
tv_dict_extend(env, temp_env.vval.v_dict, "force");
tv_dict_free(temp_env.vval.v_dict);
@@ -3813,12 +3953,13 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en
}
}
#ifndef MSWIN
- // Set COLORTERM to "truecolor" if termguicolors is set and 256
- // otherwise, but only if it was set in the parent terminal at all
- dictitem_T *dv = tv_dict_find(env, S_LEN("COLORTERM"));
- if (dv) {
- tv_dict_item_remove(env, dv);
- tv_dict_add_str(env, S_LEN("COLORTERM"), p_tgc ? "truecolor" : "256");
+ // Set COLORTERM to "truecolor" if termguicolors is set
+ if (p_tgc) {
+ dictitem_T *dv = tv_dict_find(env, S_LEN("COLORTERM"));
+ if (dv) {
+ tv_dict_item_remove(env, dv);
+ }
+ tv_dict_add_str(env, S_LEN("COLORTERM"), "truecolor");
}
#endif
}
@@ -3850,7 +3991,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en
#ifdef MSWIN
TV_DICT_ITER(job_env->di_tv.vval.v_dict, var, {
// Always use upper-case keys for Windows so we detect duplicate keys
- char *const key = strcase_save((const char *)var->di_key, true);
+ char *const key = strcase_save(var->di_key, true);
size_t len = strlen(key);
dictitem_T *dv = tv_dict_find(env, key, len);
if (dv) {
@@ -3996,7 +4137,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
env = create_environment(job_env, clear_env, pty, term_name);
- Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty,
+ Channel *chan = channel_job_start(argv, NULL, on_stdout, on_stderr, on_exit, pty,
rpc, overlapped, detach, stdin_mode, cwd,
width, height, env, &rettv->vval.v_number);
if (chan) {
@@ -4053,6 +4194,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
ui_busy_start();
+ ui_flush();
list_T *args = argvars[0].vval.v_list;
Channel **jobs = xcalloc((size_t)tv_list_len(args), sizeof(*jobs));
MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop);
@@ -4365,30 +4507,24 @@ static void f_luaeval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
nlua_typval_eval(cstr_as_string((char *)str), &argvars[1], rettv);
}
-/// "map()" function
-static void f_map(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- filter_map(argvars, rettv, true);
-}
-
static void find_some_match(typval_T *const argvars, typval_T *const rettv,
const SomeMatchType type)
{
char *str = NULL;
- long len = 0;
+ int64_t len = 0;
char *expr = NULL;
regmatch_T regmatch;
- long start = 0;
- long nth = 1;
+ int64_t start = 0;
+ int64_t nth = 1;
colnr_T startcol = 0;
bool match = false;
list_T *l = NULL;
- long idx = 0;
+ int idx = 0;
char *tofree = NULL;
// Make 'cpoptions' empty, the 'l' flag should not be used here.
char *save_cpo = p_cpo;
- p_cpo = empty_option;
+ p_cpo = empty_string_option;
rettv->vval.v_number = -1;
switch (type) {
@@ -4422,7 +4558,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
li = tv_list_first(l);
} else {
expr = str = (char *)tv_get_string(&argvars[0]);
- len = (long)strlen(str);
+ len = (int64_t)strlen(str);
}
char patbuf[NUMBUFLEN];
@@ -4443,7 +4579,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
if (idx == -1) {
goto theend;
}
- li = tv_list_find(l, (int)idx);
+ li = tv_list_find(l, idx);
} else {
if (start < 0) {
start = 0;
@@ -4470,11 +4606,11 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
}
}
- regmatch.regprog = vim_regcomp((char *)pat, RE_MAGIC + RE_STRING);
+ regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL) {
regmatch.rm_ic = p_ic;
- for (;;) {
+ while (true) {
if (l != NULL) {
if (li == NULL) {
match = false;
@@ -4535,8 +4671,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
if (regmatch.endp[i] == NULL) {
tv_list_append_string(rettv->vval.v_list, NULL, 0);
} else {
- tv_list_append_string(rettv->vval.v_list,
- (const char *)regmatch.startp[i],
+ tv_list_append_string(rettv->vval.v_list, regmatch.startp[i],
(regmatch.endp[i] - regmatch.startp[i]));
}
}
@@ -4546,7 +4681,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
if (l != NULL) {
tv_copy(TV_LIST_ITEM_TV(li), rettv);
} else {
- rettv->vval.v_string = xmemdupz((const char *)regmatch.startp[0],
+ rettv->vval.v_string = xmemdupz(regmatch.startp[0],
(size_t)(regmatch.endp[0] -
regmatch.startp[0]));
}
@@ -4675,7 +4810,7 @@ static void f_min(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "mkdir()" function
static void f_mkdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- int prot = 0755; // -V536
+ int prot = 0755;
rettv->vval.v_number = FAIL;
if (check_secure()) {
@@ -4693,6 +4828,9 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
*path_tail_with_sep((char *)dir) = NUL;
}
+ bool defer = false;
+ bool defer_recurse = false;
+ char *created = NULL;
if (argvars[1].v_type != VAR_UNKNOWN) {
if (argvars[2].v_type != VAR_UNKNOWN) {
prot = (int)tv_get_number_chk(&argvars[2], NULL);
@@ -4700,9 +4838,17 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
}
- if (strcmp(tv_get_string(&argvars[1]), "p") == 0) {
+ const char *arg2 = tv_get_string(&argvars[1]);
+ defer = vim_strchr(arg2, 'D') != NULL;
+ defer_recurse = vim_strchr(arg2, 'R') != NULL;
+ if ((defer || defer_recurse) && !can_add_defer()) {
+ return;
+ }
+
+ if (vim_strchr(arg2, 'p') != NULL) {
char *failed_dir;
- int ret = os_mkdir_recurse(dir, prot, &failed_dir);
+ int ret = os_mkdir_recurse(dir, prot, &failed_dir,
+ defer || defer_recurse ? &created : NULL);
if (ret != 0) {
semsg(_(e_mkdir), failed_dir, os_strerror(ret));
xfree(failed_dir);
@@ -4710,10 +4856,27 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
rettv->vval.v_number = OK;
- return;
}
}
- rettv->vval.v_number = vim_mkdir_emsg(dir, prot);
+ if (rettv->vval.v_number == FAIL) {
+ rettv->vval.v_number = vim_mkdir_emsg(dir, prot);
+ }
+
+ // Handle "D" and "R": deferred deletion of the created directory.
+ if (rettv->vval.v_number == OK
+ && created == NULL && (defer || defer_recurse)) {
+ created = FullName_save(dir, false);
+ }
+ if (created != NULL) {
+ typval_T tv[2];
+ tv[0].v_type = VAR_STRING;
+ tv[0].v_lock = VAR_UNLOCKED;
+ tv[0].vval.v_string = created;
+ tv[1].v_type = VAR_STRING;
+ tv[1].v_lock = VAR_UNLOCKED;
+ tv[1].vval.v_string = xstrdup(defer_recurse ? "rf" : "d");
+ add_defer("delete", 2, tv);
+ }
}
/// "mode()" function
@@ -4733,6 +4896,50 @@ static void f_mode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
rettv->v_type = VAR_STRING;
}
+static void may_add_state_char(garray_T *gap, const char *include, uint8_t c)
+{
+ if (include == NULL || vim_strchr(include, c) != NULL) {
+ ga_append(gap, c);
+ }
+}
+
+/// "state()" function
+static void f_state(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ garray_T ga;
+ ga_init(&ga, 1, 20);
+ const char *include = NULL;
+
+ if (argvars[0].v_type != VAR_UNKNOWN) {
+ include = tv_get_string(&argvars[0]);
+ }
+
+ if (!(stuff_empty() && typebuf.tb_len == 0 && !using_script())) {
+ may_add_state_char(&ga, include, 'm');
+ }
+ if (op_pending()) {
+ may_add_state_char(&ga, include, 'o');
+ }
+ if (autocmd_busy) {
+ may_add_state_char(&ga, include, 'x');
+ }
+ if (ins_compl_active()) {
+ may_add_state_char(&ga, include, 'a');
+ }
+ if (!get_was_safe_state()) {
+ may_add_state_char(&ga, include, 'S');
+ }
+ for (int i = 0; i < get_callback_depth() && i < 3; i++) {
+ may_add_state_char(&ga, include, 'c');
+ }
+ if (msg_scrolled > 0) {
+ may_add_state_char(&ga, include, 's');
+ }
+
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = ga.ga_data;
+}
+
/// "msgpackdump()" function
static void f_msgpackdump(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FUNC_ATTR_NONNULL_ALL
@@ -4756,7 +4963,7 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
char msgbuf[sizeof("msgpackdump() argument, index ") * 4 + NUMBUFLEN];
int idx = 0;
TV_LIST_ITER(list, li, {
- vim_snprintf(msgbuf, sizeof(msgbuf), (char *)msg, idx);
+ vim_snprintf(msgbuf, sizeof(msgbuf), msg, idx);
idx++;
if (encode_vim_to_msgpack(packer, TV_LIST_ITEM_TV(li), msgbuf) == FAIL) {
break;
@@ -4791,9 +4998,10 @@ static int msgpackparse_convert_item(const msgpack_object data, const msgpack_un
tv_list_append_owned_tv(ret_list, tv);
return OK;
}
- default:
+ case MSGPACK_UNPACK_EXTRA_BYTES:
abort();
}
+ UNREACHABLE;
}
static void msgpackparse_unpack_list(const list_T *const list, list_T *const ret_list)
@@ -4814,7 +5022,7 @@ static void msgpackparse_unpack_list(const list_T *const list, list_T *const ret
}
msgpack_unpacked unpacked;
msgpack_unpacked_init(&unpacked);
- do {
+ while (true) {
if (!msgpack_unpacker_reserve_buffer(unpacker, IOSIZE)) {
emsg(_(e_outofmem));
goto end;
@@ -4844,7 +5052,7 @@ static void msgpackparse_unpack_list(const list_T *const list, list_T *const ret
if (rlret == OK) {
break;
}
- } while (true);
+ }
end:
msgpack_unpacker_free(unpacker);
@@ -4929,7 +5137,7 @@ static void f_nr2char(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
- char buf[MB_MAXBYTES];
+ char buf[MB_MAXCHAR];
const int len = utf_char2bytes((int)num, buf);
rettv->v_type = VAR_STRING;
@@ -5058,7 +5266,7 @@ static void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv, EvalFuncDa
}
callback_free(&buf->b_prompt_interrupt);
- buf->b_prompt_interrupt= interrupt_callback;
+ buf->b_prompt_interrupt = interrupt_callback;
}
/// "prompt_getprompt({buffer})" function
@@ -5214,10 +5422,10 @@ static void f_rand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
goto theend;
}
- typval_T *const tvx = TV_LIST_ITEM_TV(tv_list_find(l, 0L));
- typval_T *const tvy = TV_LIST_ITEM_TV(tv_list_find(l, 1L));
- typval_T *const tvz = TV_LIST_ITEM_TV(tv_list_find(l, 2L));
- typval_T *const tvw = TV_LIST_ITEM_TV(tv_list_find(l, 3L));
+ typval_T *const tvx = TV_LIST_ITEM_TV(tv_list_find(l, 0));
+ typval_T *const tvy = TV_LIST_ITEM_TV(tv_list_find(l, 1));
+ typval_T *const tvz = TV_LIST_ITEM_TV(tv_list_find(l, 2));
+ typval_T *const tvw = TV_LIST_ITEM_TV(tv_list_find(l, 3));
if (tvx->v_type != VAR_NUMBER) {
goto theend;
}
@@ -5345,7 +5553,7 @@ static varnumber_T readdir_checkitem(void *context, const char *name)
argv[0].vval.v_string = (char *)name;
typval_T rettv;
- if (eval_expr_typval(expr, argv, 1, &rettv) == FAIL) {
+ if (eval_expr_typval(expr, false, argv, 1, &rettv) == FAIL) {
goto theend;
}
@@ -5389,18 +5597,27 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl
char buf[(IOSIZE/256) * 256]; // rounded to avoid odd + 1
int io_size = sizeof(buf);
char *prev = NULL; // previously read bytes, if any
- ptrdiff_t prevlen = 0; // length of data in prev
+ ptrdiff_t prevlen = 0; // length of data in prev
ptrdiff_t prevsize = 0; // size of prev buffer
- long maxline = MAXLNUM;
+ int64_t maxline = MAXLNUM;
+ off_T offset = 0;
+ off_T size = -1;
if (argvars[1].v_type != VAR_UNKNOWN) {
- if (strcmp(tv_get_string(&argvars[1]), "b") == 0) {
- binary = true;
- } else if (strcmp(tv_get_string(&argvars[1]), "B") == 0) {
- blob = true;
- }
- if (argvars[2].v_type != VAR_UNKNOWN) {
- maxline = tv_get_number(&argvars[2]);
+ if (always_blob) {
+ offset = (off_T)tv_get_number(&argvars[1]);
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ size = (off_T)tv_get_number(&argvars[2]);
+ }
+ } else {
+ if (strcmp(tv_get_string(&argvars[1]), "b") == 0) {
+ binary = true;
+ } else if (strcmp(tv_get_string(&argvars[1]), "B") == 0) {
+ blob = true;
+ }
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ maxline = tv_get_number(&argvars[2]);
+ }
}
}
@@ -5419,11 +5636,8 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl
if (blob) {
tv_blob_alloc_ret(rettv);
- if (!read_blob(fd, rettv->vval.v_blob)) {
+ if (read_blob(fd, rettv, offset, size) == FAIL) {
semsg(_(e_notread), fname);
- // An empty blob is returned on error.
- tv_blob_free(rettv->vval.v_blob);
- rettv->vval.v_blob = NULL;
}
fclose(fd);
return;
@@ -5445,7 +5659,7 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl
p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary));
p++) {
if (readlen <= 0 || *p == '\n') {
- char *s = NULL;
+ char *s = NULL;
size_t len = (size_t)(p - start);
// Finished a line. Remove CRs before NL.
@@ -5462,7 +5676,7 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl
}
if (prevlen == 0) {
assert(len < INT_MAX);
- s = xstrnsave(start, len);
+ s = xmemdupz(start, len);
} else {
// Change "prev" buffer to be the right size. This way
// the bytes are only copied once, and very long lines are
@@ -5501,11 +5715,11 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl
// Find the two bytes before the 0xbf. If p is at buf, or buf + 1,
// these may be in the "prev" string.
char back1 = p >= buf + 1 ? p[-1]
- : prevlen >= 1 ? prev[prevlen - 1] : NUL;
+ : prevlen >= 1 ? prev[prevlen - 1] : NUL;
char back2 = p >= buf + 2 ? p[-2]
- : p == buf + 1 && prevlen >= 1 ? prev[prevlen - 1]
- : prevlen >=
- 2 ? prev[prevlen - 2] : NUL;
+ : (p == buf + 1 && prevlen >= 1
+ ? prev[prevlen - 1]
+ : prevlen >= 2 ? prev[prevlen - 2] : NUL);
if ((uint8_t)back2 == 0xef && (uint8_t)back1 == 0xbb) {
char *dest = p - 2;
@@ -5518,9 +5732,9 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl
// have to shuffle buf to close gap
int adjust_prevlen = 0;
- if (dest < buf) { // -V782
+ if (dest < buf) {
// adjust_prevlen must be 1 or 2.
- adjust_prevlen = (int)(buf - dest); // -V782
+ adjust_prevlen = (int)(buf - dest);
dest = buf;
}
if (readlen > p - buf + 1) {
@@ -5548,7 +5762,7 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl
prevsize = p - start;
} else {
ptrdiff_t grow50pc = (prevsize * 3) / 2;
- ptrdiff_t growmin = (p - start) * 2 + prevlen;
+ ptrdiff_t growmin = (p - start) * 2 + prevlen;
prevsize = grow50pc > growmin ? grow50pc : growmin;
}
prev = xrealloc(prev, (size_t)prevsize);
@@ -5655,8 +5869,8 @@ static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL
}
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);
+ varnumber_T n1 = tv_list_find_nr(arg->vval.v_list, 0, &error);
+ varnumber_T n2 = tv_list_find_nr(arg->vval.v_list, 1, &error);
if (error) {
return FAIL;
}
@@ -5770,6 +5984,37 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
while (n-- > 0) {
tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL);
}
+ } else if (argvars[0].v_type == VAR_BLOB) {
+ tv_blob_alloc_ret(rettv);
+ if (argvars[0].vval.v_blob == NULL || n <= 0) {
+ return;
+ }
+
+ const int slen = argvars[0].vval.v_blob->bv_ga.ga_len;
+ const int len = (int)(slen * n);
+ if (len <= 0) {
+ return;
+ }
+
+ ga_grow(&rettv->vval.v_blob->bv_ga, len);
+
+ rettv->vval.v_blob->bv_ga.ga_len = len;
+
+ int i;
+ for (i = 0; i < slen; i++) {
+ if (tv_blob_get(argvars[0].vval.v_blob, i) != 0) {
+ break;
+ }
+ }
+
+ if (i == slen) {
+ // No need to copy since all bytes are already zero
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ tv_blob_set_range(rettv->vval.v_blob, i * slen, (i + 1) * slen - 1, argvars);
+ }
} else {
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
@@ -5843,8 +6088,8 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
char *const buf = xmallocz(MAXPATHL);
char *cpy;
- for (;;) {
- for (;;) {
+ while (true) {
+ while (true) {
len = readlink(p, buf, MAXPATHL);
if (len <= 0) {
break;
@@ -5861,13 +6106,13 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
// Ensure that the result will have a trailing path separator
- // if the argument has one. */
+ // 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. */
+ // concatenate the remainders.
q = (char *)path_next_component(vim_ispathsep(*buf) ? buf + 1 : buf);
if (*q != NUL) {
cpy = remain;
@@ -5881,7 +6126,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
q = path_tail(p);
if (q > p && *q == NUL) {
// Ignore trailing path separator.
- q[-1] = NUL;
+ p[q - p - 1] = NUL;
q = path_tail(p);
}
if (q > p && !path_is_absolute(buf)) {
@@ -5960,7 +6205,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
# else
char *v = os_realpath(fname, NULL);
- rettv->vval.v_string = (char_u *)(v == NULL ? xstrdup(fname) : v);
+ rettv->vval.v_string = v == NULL ? xstrdup(fname) : v;
# endif
#endif
@@ -5970,6 +6215,10 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "reverse({list})" function
static void f_reverse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
+ if (tv_check_for_string_or_list_or_blob_arg(argvars, 0) == FAIL) {
+ return;
+ }
+
if (argvars[0].v_type == VAR_BLOB) {
blob_T *const b = argvars[0].vval.v_blob;
const int len = tv_blob_len(b);
@@ -5980,9 +6229,14 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
tv_blob_set(b, len - i - 1, tmp);
}
tv_blob_set_ret(rettv, b);
- } else if (argvars[0].v_type != VAR_LIST) {
- semsg(_(e_listblobarg), "reverse()");
- } else {
+ } else if (argvars[0].v_type == VAR_STRING) {
+ rettv->v_type = VAR_STRING;
+ if (argvars[0].vval.v_string != NULL) {
+ rettv->vval.v_string = reverse_text(argvars[0].vval.v_string);
+ } else {
+ rettv->vval.v_string = NULL;
+ }
+ } else if (argvars[0].v_type == VAR_LIST) {
list_T *const l = argvars[0].vval.v_list;
if (!value_check_lock(tv_list_locked(l), N_("reverse() argument"),
TV_TRANSLATE)) {
@@ -5992,102 +6246,181 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
-/// "reduce(list, { accumulator, element -> value } [, initial])" function
-static void f_reduce(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+/// Implementation of reduce() for list "argvars[0]", using the function "expr"
+/// starting with the optional initial value argvars[2] and return the result in
+/// "rettv".
+static void reduce_list(typval_T *argvars, typval_T *expr, typval_T *rettv)
{
- if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) {
- emsg(_(e_listblobreq));
- return;
- }
+ list_T *const l = argvars[0].vval.v_list;
+ const int called_emsg_start = called_emsg;
- const char *func_name;
- partial_T *partial = NULL;
- if (argvars[1].v_type == VAR_FUNC) {
- func_name = argvars[1].vval.v_string;
- } else if (argvars[1].v_type == VAR_PARTIAL) {
- partial = argvars[1].vval.v_partial;
- func_name = partial_name(partial);
+ typval_T initial;
+ const listitem_T *li = NULL;
+ if (argvars[2].v_type == VAR_UNKNOWN) {
+ if (tv_list_len(l) == 0) {
+ semsg(_(e_reduceempty), "List");
+ return;
+ }
+ const listitem_T *const first = tv_list_first(l);
+ initial = *TV_LIST_ITEM_TV(first);
+ li = TV_LIST_ITEM_NEXT(l, first);
} else {
- func_name = tv_get_string(&argvars[1]);
+ initial = argvars[2];
+ li = tv_list_first(l);
}
- if (*func_name == NUL) {
- return; // type error or empty name
+
+ tv_copy(&initial, rettv);
+
+ if (l == NULL) {
+ return;
}
- funcexe_T funcexe = FUNCEXE_INIT;
- funcexe.fe_evaluate = true;
- funcexe.fe_partial = partial;
+ const VarLockStatus prev_locked = tv_list_locked(l);
- typval_T initial;
- typval_T argv[3];
- if (argvars[0].v_type == VAR_LIST) {
- list_T *const l = argvars[0].vval.v_list;
- const listitem_T *li;
+ tv_list_set_lock(l, VAR_FIXED); // disallow the list changing here
+ for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) {
+ typval_T argv[3];
+ argv[0] = *rettv;
+ argv[1] = *TV_LIST_ITEM_TV(li);
+ rettv->v_type = VAR_UNKNOWN;
- if (argvars[2].v_type == VAR_UNKNOWN) {
- if (tv_list_len(l) == 0) {
- semsg(_(e_reduceempty), "List");
- return;
- }
- const listitem_T *const first = tv_list_first(l);
- initial = *TV_LIST_ITEM_TV(first);
- li = TV_LIST_ITEM_NEXT(l, first);
- } else {
- initial = argvars[2];
- li = tv_list_first(l);
+ const int r = eval_expr_typval(expr, true, argv, 2, rettv);
+
+ tv_clear(&argv[0]);
+ if (r == FAIL || called_emsg != called_emsg_start) {
+ break;
}
+ }
+ tv_list_set_lock(l, prev_locked);
+}
- tv_copy(&initial, rettv);
+/// Implementation of reduce() for String "argvars[0]" using the function "expr"
+/// starting with the optional initial value "argvars[2]" and return the result
+/// in "rettv".
+static void reduce_string(typval_T *argvars, typval_T *expr, typval_T *rettv)
+{
+ const char *p = tv_get_string(&argvars[0]);
+ int len;
+ const int called_emsg_start = called_emsg;
- if (l != NULL) {
- const VarLockStatus prev_locked = tv_list_locked(l);
- const int called_emsg_start = called_emsg;
-
- tv_list_set_lock(l, VAR_FIXED); // disallow the list changing here
- for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) {
- argv[0] = *rettv;
- argv[1] = *TV_LIST_ITEM_TV(li);
- rettv->v_type = VAR_UNKNOWN;
- const int r = call_func((char *)func_name, -1, rettv, 2, argv, &funcexe);
- tv_clear(&argv[0]);
- if (r == FAIL || called_emsg != called_emsg_start) {
- break;
- }
- }
- tv_list_set_lock(l, prev_locked);
+ if (argvars[2].v_type == VAR_UNKNOWN) {
+ if (*p == NUL) {
+ semsg(_(e_reduceempty), "String");
+ return;
}
+ len = utfc_ptr2len(p);
+ *rettv = (typval_T){
+ .v_type = VAR_STRING,
+ .v_lock = VAR_UNLOCKED,
+ .vval.v_string = xmemdupz(p, (size_t)len),
+ };
+ p += len;
+ } else if (tv_check_for_string_arg(argvars, 2) == FAIL) {
+ return;
} else {
- const blob_T *const b = argvars[0].vval.v_blob;
- int i;
+ tv_copy(&argvars[2], rettv);
+ }
- if (argvars[2].v_type == VAR_UNKNOWN) {
- if (tv_blob_len(b) == 0) {
- semsg(_(e_reduceempty), "Blob");
- return;
- }
- initial.v_type = VAR_NUMBER;
- initial.vval.v_number = tv_blob_get(b, 0);
- i = 1;
- } else if (argvars[2].v_type != VAR_NUMBER) {
- emsg(_(e_number_exp));
+ for (; *p != NUL; p += len) {
+ typval_T argv[3];
+ argv[0] = *rettv;
+ len = utfc_ptr2len(p);
+ argv[1] = (typval_T){
+ .v_type = VAR_STRING,
+ .v_lock = VAR_UNLOCKED,
+ .vval.v_string = xmemdupz(p, (size_t)len),
+ };
+
+ const int r = eval_expr_typval(expr, true, argv, 2, rettv);
+
+ tv_clear(&argv[0]);
+ tv_clear(&argv[1]);
+ if (r == FAIL || called_emsg != called_emsg_start) {
+ break;
+ }
+ }
+}
+
+/// Implementation of reduce() for Blob "argvars[0]" using the function "expr"
+/// starting with the optional initial value "argvars[2]" and return the result
+/// in "rettv".
+static void reduce_blob(typval_T *argvars, typval_T *expr, typval_T *rettv)
+{
+ const blob_T *const b = argvars[0].vval.v_blob;
+ const int called_emsg_start = called_emsg;
+
+ typval_T initial;
+ int i;
+ if (argvars[2].v_type == VAR_UNKNOWN) {
+ if (tv_blob_len(b) == 0) {
+ semsg(_(e_reduceempty), "Blob");
return;
- } else {
- initial = argvars[2];
- i = 0;
}
+ initial = (typval_T){
+ .v_type = VAR_NUMBER,
+ .v_lock = VAR_UNLOCKED,
+ .vval.v_number = tv_blob_get(b, 0),
+ };
+ i = 1;
+ } else if (tv_check_for_number_arg(argvars, 2) == FAIL) {
+ return;
+ } else {
+ initial = argvars[2];
+ i = 0;
+ }
+
+ tv_copy(&initial, rettv);
+ for (; i < tv_blob_len(b); i++) {
+ typval_T argv[3];
+ argv[0] = *rettv;
+ argv[1] = (typval_T){
+ .v_type = VAR_NUMBER,
+ .v_lock = VAR_UNLOCKED,
+ .vval.v_number = tv_blob_get(b, i),
+ };
- tv_copy(&initial, rettv);
- for (; i < tv_blob_len(b); i++) {
- argv[0] = *rettv;
- argv[1].v_type = VAR_NUMBER;
- argv[1].vval.v_number = tv_blob_get(b, i);
- if (call_func((char *)func_name, -1, rettv, 2, argv, &funcexe) == FAIL) {
- return;
- }
+ const int r = eval_expr_typval(expr, true, argv, 2, rettv);
+
+ if (r == FAIL || called_emsg != called_emsg_start) {
+ return;
}
}
}
+/// "reduce(list, { accumulator, element -> value } [, initial])" function
+/// "reduce(blob, { accumulator, element -> value } [, initial])" function
+/// "reduce(string, { accumulator, element -> value } [, initial])" function
+static void f_reduce(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ if (argvars[0].v_type != VAR_STRING
+ && argvars[0].v_type != VAR_LIST
+ && argvars[0].v_type != VAR_BLOB) {
+ emsg(_(e_string_list_or_blob_required));
+ return;
+ }
+
+ const char *func_name;
+ if (argvars[1].v_type == VAR_FUNC) {
+ func_name = argvars[1].vval.v_string;
+ } else if (argvars[1].v_type == VAR_PARTIAL) {
+ func_name = partial_name(argvars[1].vval.v_partial);
+ } else {
+ func_name = tv_get_string(&argvars[1]);
+ }
+ if (func_name == NULL || *func_name == NUL) {
+ emsg(_(e_missing_function_argument));
+ return;
+ }
+
+ if (argvars[0].v_type == VAR_LIST) {
+ reduce_list(argvars, &argvars[1], rettv);
+ } else if (argvars[0].v_type == VAR_STRING) {
+ reduce_string(argvars, &argvars[1], rettv);
+ } else {
+ reduce_blob(argvars, &argvars[1], rettv);
+ }
+}
+
#define SP_NOMOVE 0x01 ///< don't move cursor
#define SP_REPEAT 0x02 ///< repeat to find outer pair
#define SP_RETCOUNT 0x04 ///< return matchcount
@@ -6165,8 +6498,8 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
{
bool save_p_ws = p_ws;
int retval = 0; // default: FAIL
- long lnum_stop = 0;
- long time_limit = 0;
+ linenr_T lnum_stop = 0;
+ int64_t time_limit = 0;
int options = SEARCH_KEEP;
bool use_skip = false;
@@ -6188,7 +6521,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
// Optional arguments: line number to stop searching, timeout and skip.
if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) {
- lnum_stop = tv_get_number_chk(&argvars[2], NULL);
+ lnum_stop = (linenr_T)tv_get_number_chk(&argvars[2], NULL);
if (lnum_stop < 0) {
goto theend;
}
@@ -6218,14 +6551,14 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
pos_T pos = save_cursor = curwin->w_cursor;
pos_T firstpos = { 0 };
searchit_arg_T sia = {
- .sa_stop_lnum = (linenr_T)lnum_stop,
+ .sa_stop_lnum = lnum_stop,
.sa_tm = &tm,
};
int subpatnum;
// Repeat until {skip} returns false.
- for (;;) {
+ while (true) {
subpatnum
= searchit(curwin, curbuf, &pos, NULL, dir, (char *)pat, 1, options, RE_SEARCH, &sia);
// finding the first match again means there is no match where {skip}
@@ -6363,6 +6696,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
sctx_T save_current_sctx;
char *save_autocmd_fname, *save_autocmd_match;
+ bool save_autocmd_fname_full;
int save_autocmd_bufnr;
funccal_entry_T funccal_entry;
@@ -6372,6 +6706,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
save_current_sctx = current_sctx;
save_autocmd_fname = autocmd_fname;
save_autocmd_match = autocmd_match;
+ save_autocmd_fname_full = autocmd_fname_full;
save_autocmd_bufnr = autocmd_bufnr;
save_funccal(&funccal_entry);
@@ -6380,6 +6715,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
((estack_T *)exestack.ga_data)[exestack.ga_len++] = provider_caller_scope.es_entry;
autocmd_fname = provider_caller_scope.autocmd_fname;
autocmd_match = provider_caller_scope.autocmd_match;
+ autocmd_fname_full = provider_caller_scope.autocmd_fname_full;
autocmd_bufnr = provider_caller_scope.autocmd_bufnr;
set_current_funccal((funccall_T *)(provider_caller_scope.funccalp));
}
@@ -6397,6 +6733,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
exestack.ga_len--;
autocmd_fname = save_autocmd_fname;
autocmd_match = save_autocmd_match;
+ autocmd_fname_full = save_autocmd_fname_full;
autocmd_bufnr = save_autocmd_bufnr;
restore_funccal();
}
@@ -6405,7 +6742,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
const char *name = NULL;
Channel *chan = find_channel(chan_id);
if (chan) {
- name = rpc_client_name(chan);
+ name = get_client_info(chan, "name");
}
msg_ext_set_kind("rpc_error");
if (name) {
@@ -6420,6 +6757,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
if (!object_to_vim(result, rettv, &err)) {
+ assert(ERROR_SET(&err));
semsg(_("Error converting the call result: %s"), err.msg);
}
@@ -6485,7 +6823,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
// The last item of argv must be NULL
argv[i] = NULL;
- Channel *chan = channel_job_start(argv, CALLBACK_READER_INIT,
+ Channel *chan = channel_job_start(argv, NULL, CALLBACK_READER_INIT,
CALLBACK_READER_INIT, CALLBACK_NONE,
false, true, false, false,
kChannelStdinPipe, NULL, 0, 0, NULL,
@@ -6556,7 +6894,9 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) {
c = -1;
} else {
- c = utf_ptr2char((char *)grid->chars[grid->line_offset[row] + (size_t)col]);
+ char buf[MAX_SCHAR_SIZE + 1];
+ schar_get(buf, grid_getchar(grid, row, col, NULL));
+ c = utf_ptr2char(buf);
}
rettv->vval.v_number = c;
}
@@ -6570,21 +6910,22 @@ static void f_screenchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
ScreenGrid *grid;
screenchar_adjust(&grid, &row, &col);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) {
- tv_list_alloc_ret(rettv, 0);
return;
}
- int pcc[MAX_MCO];
- int c = utfc_ptr2char((char *)grid->chars[grid->line_offset[row] + (size_t)col], pcc);
- int composing_len = 0;
- while (pcc[composing_len] != 0) {
- composing_len++;
- }
- tv_list_alloc_ret(rettv, composing_len + 1);
- tv_list_append_number(rettv->vval.v_list, c);
- for (int i = 0; i < composing_len; i++) {
- tv_list_append_number(rettv->vval.v_list, pcc[i]);
- }
+
+ char buf[MAX_SCHAR_SIZE + 1];
+ schar_get(buf, grid_getchar(grid, row, col, NULL));
+
+ // schar values are already processed chars which are always NUL-terminated.
+ // A single [0] is expected when char is NUL.
+ size_t i = 0;
+ do {
+ int c = utf_ptr2char(buf + i);
+ tv_list_append_number(rettv->vval.v_list, c);
+ i += (size_t)utf_ptr2len(buf + i);
+ } while (buf[i] != NUL);
}
/// "screencol()" function
@@ -6617,7 +6958,9 @@ static void f_screenstring(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
return;
}
- rettv->vval.v_string = xstrdup((char *)grid->chars[grid->line_offset[row] + (size_t)col]);
+ char buf[MAX_SCHAR_SIZE + 1];
+ schar_get(buf, grid_getchar(grid, row, col, NULL));
+ rettv->vval.v_string = xstrdup(buf);
}
/// "search()" function
@@ -6656,8 +6999,8 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos)
bool save_p_ws = p_ws;
int flags = 0;
int retval = 0; // default: FAIL
- long lnum_stop = 0;
- long time_limit = 0;
+ linenr_T lnum_stop = 0;
+ int64_t time_limit = 0;
// Get the three pattern arguments: start, middle, end. Will result in an
// error if not a valid argument.
@@ -6699,7 +7042,7 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos)
skip = &argvars[4];
if (argvars[5].v_type != VAR_UNKNOWN) {
- lnum_stop = tv_get_number_chk(&argvars[5], NULL);
+ lnum_stop = (linenr_T)tv_get_number_chk(&argvars[5], NULL);
if (lnum_stop < 0) {
semsg(_(e_invarg2), tv_get_string(&argvars[5]));
goto theend;
@@ -6714,8 +7057,8 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos)
}
}
- retval = (int)do_searchpair(spat, mpat, epat, dir, skip,
- flags, match_pos, (linenr_T)lnum_stop, time_limit);
+ retval = do_searchpair(spat, mpat, epat, dir, skip,
+ flags, match_pos, lnum_stop, time_limit);
theend:
p_ws = save_p_ws;
@@ -6760,19 +7103,19 @@ static void f_searchpairpos(typval_T *argvars, typval_T *rettv, EvalFuncData fpt
/// @param time_limit stop after this many msec
///
/// @returns 0 or -1 for no match,
-long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir,
- const typval_T *skip, int flags, pos_T *match_pos, linenr_T lnum_stop,
- long time_limit)
+int do_searchpair(const char *spat, const char *mpat, const char *epat, int dir,
+ const typval_T *skip, int flags, pos_T *match_pos, linenr_T lnum_stop,
+ int64_t time_limit)
FUNC_ATTR_NONNULL_ARG(1, 2, 3)
{
- long retval = 0;
+ int retval = 0;
int nest = 1;
bool use_skip = false;
int options = SEARCH_KEEP;
// Make 'cpoptions' empty, the 'l' flag should not be used here.
char *save_cpo = p_cpo;
- p_cpo = empty_option;
+ p_cpo = empty_string_option;
// Set the time limit, if there is one.
proftime_T tm = profile_setlimit(time_limit);
@@ -6805,13 +7148,13 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir
pos_T foundpos;
clearpos(&foundpos);
char *pat = pat3;
- for (;;) {
+ while (true) {
searchit_arg_T sia = {
.sa_stop_lnum = lnum_stop,
.sa_tm = &tm,
};
- int n = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1L,
+ int n = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1,
options, RE_SEARCH, &sia);
if (n == FAIL || (firstpos.lnum != 0 && equalpos(pos, firstpos))) {
// didn't find it or found the first match again: FAIL
@@ -6898,14 +7241,14 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir
xfree(pat2);
xfree(pat3);
- if (p_cpo == empty_option) {
+ if (p_cpo == empty_string_option) {
p_cpo = save_cpo;
} else {
// Darn, evaluating the {skip} expression changed the value.
// If it's still empty it was changed and restored, need to restore in
// the complicated way.
if (*p_cpo == NUL) {
- set_option_value_give_err("cpo", 0L, save_cpo, 0);
+ set_option_value_give_err("cpo", CSTR_AS_OPTVAL(save_cpo), 0);
}
free_string_option(save_cpo);
}
@@ -7012,7 +7355,7 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
/// Set the cursor or mark position.
-/// If 'charpos' is true, then use the column number as a character offset.
+/// If "charpos" is true, then use the column number as a character offset.
/// Otherwise use the column number as a byte offset.
static void set_position(typval_T *argvars, typval_T *rettv, bool charpos)
{
@@ -7060,8 +7403,7 @@ static void f_setcharpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
static void f_setcharsearch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- if (argvars[0].v_type != VAR_DICT) {
- emsg(_(e_dictreq));
+ if (tv_check_for_dict_arg(argvars, 0) == FAIL) {
return;
}
@@ -7072,8 +7414,7 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, EvalFuncData fpt
char *const csearch = tv_dict_get_string(d, "char", false);
if (csearch != NULL) {
- int pcc[MAX_MCO];
- const int c = utfc_ptr2char(csearch, pcc);
+ int c = utf_ptr2char(csearch);
set_last_csearch(c, csearch, utfc_ptr2len(csearch));
}
@@ -7101,6 +7442,13 @@ static void f_setenv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
char valbuf[NUMBUFLEN];
const char *name = tv_get_string_buf(&argvars[0], namebuf);
+ // setting an environment variable may be dangerous, e.g. you could
+ // setenv GCONV_PATH=/tmp and then have iconv() unexpectedly call
+ // a shell command using some shared library:
+ if (check_secure()) {
+ return;
+ }
+
if (argvars[1].v_type == VAR_SPECIAL
&& argvars[1].vval.v_special == kSpecialVarNull) {
vim_unsetenv_ext(name);
@@ -7147,7 +7495,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
/// Translate a register type string to the yank type and block length
-static int get_yank_type(char **const pp, MotionType *const yank_type, long *const block_len)
+static int get_yank_type(char **const pp, MotionType *const yank_type, int *const block_len)
FUNC_ATTR_NONNULL_ALL
{
char *stropt = *pp;
@@ -7165,7 +7513,7 @@ static int get_yank_type(char **const pp, MotionType *const yank_type, long *con
*yank_type = kMTBlockWise;
if (ascii_isdigit(stropt[1])) {
stropt++;
- *block_len = getdigits_long(&stropt, false, 0) - 1;
+ *block_len = getdigits_int(&stropt, false, 0) - 1;
stropt--;
}
break;
@@ -7181,7 +7529,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
bool append = false;
- long block_len = -1;
+ int block_len = -1;
MotionType yank_type = kMTUnknown;
rettv->vval.v_number = 1; // FAIL is default.
@@ -7320,7 +7668,7 @@ free_lstval:
/// "settagstack()" function
static void f_settagstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- static char *e_invact2 = N_("E962: Invalid action: '%s'");
+ static const char *e_invact2 = N_("E962: Invalid action: '%s'");
char action = 'r';
rettv->vval.v_number = -1;
@@ -7332,8 +7680,7 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
// second argument: dict with items to set in the tag stack
- if (argvars[1].v_type != VAR_DICT) {
- emsg(_(e_dictreq));
+ if (tv_check_for_dict_arg(argvars, 1) == FAIL) {
return;
}
dict_T *d = argvars[1].vval.v_dict;
@@ -7344,8 +7691,10 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
// third argument: action - 'a' for append and 'r' for replace.
// default is to replace the stack.
if (argvars[2].v_type == VAR_UNKNOWN) {
- action = 'r';
- } else if (argvars[2].v_type == VAR_STRING) {
+ // action = 'r';
+ } else if (tv_check_for_string_arg(argvars, 2) == FAIL) {
+ return;
+ } else {
const char *actstr;
actstr = tv_get_string_chk(&argvars[2]);
if (actstr == NULL) {
@@ -7358,9 +7707,6 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
semsg(_(e_invact2), actstr);
return;
}
- } else {
- emsg(_(e_stringreq));
- return;
}
if (set_tagstack(wp, d, action) == OK) {
@@ -7395,11 +7741,11 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
rettv->vval.v_number = 0;
if (argvars[0].v_type != VAR_UNKNOWN) {
- long col = (long)tv_get_number_chk(argvars, NULL);
+ colnr_T col = (colnr_T)tv_get_number_chk(argvars, NULL);
if (col < 0) {
return; // type error; errmsg already given
}
- rettv->vval.v_number = get_sw_value_col(curbuf, (colnr_T)col);
+ rettv->vval.v_number = get_sw_value_col(curbuf, col);
return;
}
rettv->vval.v_number = get_sw_value(curbuf);
@@ -7527,7 +7873,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
const int wo_spell_save = curwin->w_p_spell;
if (!curwin->w_p_spell) {
- did_set_spelllang(curwin);
+ parse_spelllang(curwin);
curwin->w_p_spell = true;
}
@@ -7571,10 +7917,11 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
tv_list_alloc_ret(rettv, 2);
tv_list_append_string(rettv->vval.v_list, word, (ssize_t)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);
+ (attr == HLF_SPB
+ ? "bad" : (attr == HLF_SPR
+ ? "rare" : (attr == HLF_SPL
+ ? "local" : (attr == HLF_SPC
+ ? "caps" : NULL)))), -1);
}
/// "spellsuggest()" function
@@ -7584,7 +7931,7 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
const int wo_spell_save = curwin->w_p_spell;
if (!curwin->w_p_spell) {
- did_set_spelllang(curwin);
+ parse_spelllang(curwin);
curwin->w_p_spell = true;
}
@@ -7633,7 +7980,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
// Make 'cpoptions' empty, the 'l' flag should not be used here.
char *save_cpo = p_cpo;
- p_cpo = empty_option;
+ p_cpo = empty_string_option;
const char *str = tv_get_string(&argvars[0]);
const char *pat = NULL;
@@ -7658,7 +8005,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
regmatch_T regmatch = {
- .regprog = vim_regcomp((char *)pat, RE_MAGIC + RE_STRING),
+ .regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING),
.startp = { NULL },
.endp = { NULL },
.rm_ic = false,
@@ -7669,18 +8016,18 @@ static void f_split(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (*str == NUL) {
match = false; // Empty item at the end.
} else {
- match = vim_regexec_nl(&regmatch, (char *)str, col);
+ match = vim_regexec_nl(&regmatch, str, col);
}
const char *end;
if (match) {
- end = (const char *)regmatch.startp[0];
+ end = regmatch.startp[0];
} else {
end = str + strlen(str);
}
if (keepempty || end > str || (tv_list_len(rettv->vval.v_list) > 0
&& *str != NUL
&& match
- && end < (const char *)regmatch.endp[0])) {
+ && end < regmatch.endp[0])) {
tv_list_append_string(rettv->vval.v_list, str, end - str);
}
if (!match) {
@@ -7693,7 +8040,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
// Don't get stuck at the same match.
col = utfc_ptr2len(regmatch.endp[0]);
}
- str = (const char *)regmatch.endp[0];
+ str = regmatch.endp[0];
}
vim_regfree(regmatch.regprog);
@@ -7751,60 +8098,6 @@ static void f_str2float(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
rettv->v_type = VAR_FLOAT;
}
-/// "str2list()" function
-static void f_str2list(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- tv_list_alloc_ret(rettv, kListLenUnknown);
- const char *p = tv_get_string(&argvars[0]);
-
- for (; *p != NUL; p += utf_ptr2len(p)) {
- tv_list_append_number(rettv->vval.v_list, utf_ptr2char(p));
- }
-}
-
-/// "str2nr()" function
-static void f_str2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- int base = 10;
- int what = 0;
-
- if (argvars[1].v_type != VAR_UNKNOWN) {
- base = (int)tv_get_number(&argvars[1]);
- if (base != 2 && base != 8 && base != 10 && base != 16) {
- emsg(_(e_invarg));
- return;
- }
- if (argvars[2].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[2])) {
- what |= STR2NR_QUOTE;
- }
- }
-
- char *p = skipwhite(tv_get_string(&argvars[0]));
- bool isneg = (*p == '-');
- if (*p == '+' || *p == '-') {
- p = skipwhite(p + 1);
- }
- switch (base) {
- case 2:
- what |= STR2NR_BIN | STR2NR_FORCE;
- break;
- case 8:
- what |= STR2NR_OCT | STR2NR_OOCT | STR2NR_FORCE;
- break;
- case 16:
- what |= STR2NR_HEX | STR2NR_FORCE;
- break;
- }
- varnumber_T n;
- vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, false);
- // Text after the number is silently ignored.
- if (isneg) {
- rettv->vval.v_number = -n;
- } else {
- rettv->vval.v_number = n;
- }
-}
-
/// "strftime({format}[, {time}])" function
static void f_strftime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -7824,263 +8117,35 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
// MSVC returns NULL for an invalid value of seconds.
if (curtime_ptr == NULL) {
rettv->vval.v_string = xstrdup(_("(Invalid)"));
- } else {
- vimconv_T conv;
-
- conv.vc_type = CONV_NONE;
- char *enc = enc_locale();
- convert_setup(&conv, p_enc, enc);
- if (conv.vc_type != CONV_NONE) {
- p = string_convert(&conv, p, NULL);
- }
- char result_buf[256];
- if (p == NULL || strftime(result_buf, sizeof(result_buf), p, curtime_ptr) == 0) {
- result_buf[0] = NUL;
- }
-
- 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 = xstrdup(result_buf);
- }
-
- // Release conversion descriptors.
- convert_setup(&conv, NULL, NULL);
- xfree(enc);
- }
-}
-
-/// "strgetchar()" function
-static void f_strgetchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->vval.v_number = -1;
-
- const char *const str = tv_get_string_chk(&argvars[0]);
- if (str == NULL) {
return;
}
- bool error = false;
- varnumber_T charidx = tv_get_number_chk(&argvars[1], &error);
- if (error) {
- return;
- }
-
- const size_t len = strlen(str);
- size_t byteidx = 0;
-
- while (charidx >= 0 && byteidx < len) {
- if (charidx == 0) {
- rettv->vval.v_number = utf_ptr2char(str + byteidx);
- break;
- }
- charidx--;
- byteidx += (size_t)utf_ptr2len(str + byteidx);
- }
-}
-
-/// "stridx()" function
-static void f_stridx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->vval.v_number = -1;
-
- 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) {
- bool error = false;
-
- 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) {
- haystack += start_idx;
- }
- }
-
- const char *pos = strstr(haystack, needle);
- if (pos != NULL) {
- rettv->vval.v_number = (varnumber_T)(pos - haystack_start);
- }
-}
-
-/// "string()" function
-static void f_string(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = encode_tv2string(&argvars[0], NULL);
-}
-
-/// "strlen()" function
-static void f_strlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->vval.v_number = (varnumber_T)strlen(tv_get_string(&argvars[0]));
-}
-
-static void strchar_common(typval_T *argvars, typval_T *rettv, bool skipcc)
-{
- const char *s = tv_get_string(&argvars[0]);
- varnumber_T len = 0;
- int (*func_mb_ptr2char_adv)(const char **pp);
-
- func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
- while (*s != NUL) {
- func_mb_ptr2char_adv(&s);
- len++;
- }
- rettv->vval.v_number = len;
-}
-
-/// "strcharlen()" function
-static void f_strcharlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- strchar_common(argvars, rettv, true);
-}
-
-/// "strchars()" function
-static void f_strchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- int skipcc = false;
-
- if (argvars[1].v_type != VAR_UNKNOWN) {
- skipcc = (int)tv_get_bool(&argvars[1]);
- }
- if (skipcc < 0 || skipcc > 1) {
- semsg(_(e_using_number_as_bool_nr), skipcc);
- } else {
- strchar_common(argvars, rettv, skipcc);
- }
-}
-
-/// "strdisplaywidth()" function
-static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- const char *const s = tv_get_string(&argvars[0]);
- int col = 0;
-
- if (argvars[1].v_type != VAR_UNKNOWN) {
- col = (int)tv_get_number(&argvars[1]);
- }
-
- rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char *)s) - col);
-}
-
-/// "strwidth()" function
-static void f_strwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- const char *const s = tv_get_string(&argvars[0]);
-
- rettv->vval.v_number = (varnumber_T)mb_string2cells(s);
-}
-/// "strcharpart()" function
-static void f_strcharpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- const char *const p = tv_get_string(&argvars[0]);
- const size_t slen = strlen(p);
+ vimconv_T conv;
- int nbyte = 0;
- bool error = false;
- varnumber_T nchar = tv_get_number_chk(&argvars[1], &error);
- if (!error) {
- if (nchar > 0) {
- while (nchar > 0 && (size_t)nbyte < slen) {
- nbyte += utf_ptr2len(p + nbyte);
- nchar--;
- }
- } else {
- nbyte = (int)nchar;
- }
+ conv.vc_type = CONV_NONE;
+ char *enc = enc_locale();
+ convert_setup(&conv, p_enc, enc);
+ if (conv.vc_type != CONV_NONE) {
+ p = string_convert(&conv, p, NULL);
}
- int len = 0;
- if (argvars[2].v_type != VAR_UNKNOWN) {
- int charlen = (int)tv_get_number(&argvars[2]);
- while (charlen > 0 && nbyte + len < (int)slen) {
- int off = nbyte + len;
-
- if (off < 0) {
- len += 1;
- } else {
- len += utf_ptr2len(p + off);
- }
- charlen--;
- }
- } else {
- len = (int)slen - nbyte; // default: all bytes that are available.
+ char result_buf[256];
+ if (p == NULL || strftime(result_buf, sizeof(result_buf), p, curtime_ptr) == 0) {
+ result_buf[0] = NUL;
}
- // Only return the overlap between the specified part and the actual
- // string.
- if (nbyte < 0) {
- len += nbyte;
- nbyte = 0;
- } else if ((size_t)nbyte > slen) {
- nbyte = (int)slen;
- }
- if (len < 0) {
- len = 0;
- } else if (nbyte + len > (int)slen) {
- len = (int)slen - nbyte;
+ if (conv.vc_type != CONV_NONE) {
+ xfree(p);
}
-
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = xstrndup(p + nbyte, (size_t)len);
-}
-
-/// "strpart()" function
-static void f_strpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- bool error = false;
-
- const char *const p = tv_get_string(&argvars[0]);
- const size_t slen = strlen(p);
-
- 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 = tv_get_number(&argvars[2]);
+ convert_setup(&conv, enc, p_enc);
+ if (conv.vc_type != CONV_NONE) {
+ rettv->vval.v_string = string_convert(&conv, result_buf, NULL);
} else {
- len = (varnumber_T)slen - n; // Default len: all bytes that are available.
- }
-
- // Only return the overlap between the specified part and the actual
- // string.
- if (n < 0) {
- len += n;
- n = 0;
- } else if (n > (varnumber_T)slen) {
- n = (varnumber_T)slen;
- }
- if (len < 0) {
- len = 0;
- } else if (n + len > (varnumber_T)slen) {
- len = (varnumber_T)slen - n;
- }
-
- if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN) {
- int off;
-
- // length in characters
- for (off = (int)n; off < (int)slen && len > 0; len--) {
- off += utfc_ptr2len(p + off);
- }
- len = off - n;
+ rettv->vval.v_string = xstrdup(result_buf);
}
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = xmemdupz(p + n, (size_t)len);
+ // Release conversion descriptors.
+ convert_setup(&conv, NULL, NULL);
+ xfree(enc);
}
/// "strptime({format}, {timestring})" function
@@ -8115,56 +8180,6 @@ static void f_strptime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
xfree(enc);
}
-/// "strridx()" function
-static void f_strridx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- 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.
- }
-
- 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 = (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.
- lastmatch = haystack + end_idx;
- } else {
- 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) {
- rettv->vval.v_number = (varnumber_T)(lastmatch - haystack);
- }
-}
-
-/// "strtrans()" function
-static void f_strtrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = transstr(tv_get_string(&argvars[0]), true);
-}
-
/// "submatch()" function
static void f_submatch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -8175,7 +8190,7 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
if (no < 0 || no >= NSUBEXP) {
- semsg(_("E935: invalid submatch number: %d"), no);
+ semsg(_(e_invalid_submatch_number_nr), no);
return;
}
int retList = 0;
@@ -8225,11 +8240,18 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
+/// "swapfilelist()" function
+static void f_swapfilelist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ tv_list_alloc_ret(rettv, kListLenUnknown);
+ recover_names(NULL, false, rettv->vval.v_list, 0, NULL);
+}
+
/// "swapinfo(swap_filename)" function
static void f_swapinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
- get_b0_dict(tv_get_string(argvars), rettv->vval.v_dict);
+ swapfile_dict(tv_get_string(argvars), rettv->vval.v_dict);
}
/// "swapname(expr)" function
@@ -8349,7 +8371,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char *)(p == NULL ? p : xstrdup(p));
+ rettv->vval.v_string = p == NULL ? NULL : xstrdup(p);
}
/// "synIDtrans(id)" function
@@ -8392,8 +8414,8 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
cchar = syn_get_sub_char();
if (cchar == NUL && curwin->w_p_cole == 1) {
cchar = (curwin->w_p_lcs_chars.conceal == NUL)
- ? ' '
- : curwin->w_p_lcs_chars.conceal;
+ ? ' '
+ : curwin->w_p_lcs_chars.conceal;
}
if (cchar != NUL) {
utf_char2bytes(cchar, str);
@@ -8432,7 +8454,7 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
-/// f_system - the VimL system() function
+/// f_system - the Vimscript system() function
static void f_system(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
get_system_output_as_rettv(argvars, rettv, false);
@@ -8513,7 +8535,10 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (check_secure()) {
return;
}
-
+ if (text_locked()) {
+ text_locked_msg();
+ return;
+ }
if (curbuf->b_changed) {
emsg(_("Can only call this function in an unmodified buffer"));
return;
@@ -8580,7 +8605,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
const bool detach = false;
ChannelStdinMode stdin_mode = kChannelStdinPipe;
uint16_t term_width = (uint16_t)MAX(0, curwin->w_width_inner - win_col_off(curwin));
- Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit,
+ Channel *chan = channel_job_start(argv, NULL, on_stdout, on_stderr, on_exit,
pty, rpc, overlapped, detach, stdin_mode,
cwd, term_width, (uint16_t)curwin->w_height_inner,
env, &rettv->vval.v_number);
@@ -8626,21 +8651,24 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
INTEGER_OBJ(pid), false, false, &err);
api_clear_error(&err);
+ channel_incref(chan);
channel_terminal_open(curbuf, chan);
channel_create_event(chan, NULL);
+ channel_decref(chan);
}
/// "timer_info([timer])" function
static void f_timer_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
+ tv_list_alloc_ret(rettv, kListLenUnknown);
+
+ if (tv_check_for_opt_number_arg(argvars, 0) == FAIL) {
+ return;
+ }
+
if (argvars[0].v_type != VAR_UNKNOWN) {
- if (argvars[0].v_type != VAR_NUMBER) {
- emsg(_(e_number_exp));
- return;
- }
- tv_list_alloc_ret(rettv, 1);
timer_T *timer = find_timer_by_nr(tv_get_number(&argvars[0]));
- if (timer != NULL && !timer->stopped) {
+ if (timer != NULL && (!timer->stopped || timer->refcount > 1)) {
add_timer_info(rettv, timer);
}
} else {
@@ -8655,6 +8683,7 @@ static void f_timer_pause(typval_T *argvars, typval_T *unused, EvalFuncData fptr
emsg(_(e_number_exp));
return;
}
+
int paused = (bool)tv_get_number(&argvars[1]);
timer_T *timer = find_timer_by_nr(tv_get_number(&argvars[0]));
if (timer != NULL) {
@@ -8679,11 +8708,10 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
if (argvars[2].v_type != VAR_UNKNOWN) {
- dict_T *dict = argvars[2].vval.v_dict;
- if (argvars[2].v_type != VAR_DICT || dict == NULL) {
- semsg(_(e_invarg2), tv_get_string(&argvars[2]));
+ if (tv_check_for_nonnull_dict_arg(argvars, 2) == FAIL) {
return;
}
+ dict_T *dict = argvars[2].vval.v_dict;
dictitem_T *const di = tv_dict_find(dict, S_LEN("repeat"));
if (di != NULL) {
repeat = (int)tv_get_number(&di->di_tv);
@@ -8703,8 +8731,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "timer_stop(timerid)" function
static void f_timer_stop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- if (argvars[0].v_type != VAR_NUMBER) {
- emsg(_(e_number_exp));
+ if (tv_check_for_number_arg(argvars, 0) == FAIL) {
return;
}
@@ -8721,186 +8748,6 @@ static void f_timer_stopall(typval_T *argvars, typval_T *unused, EvalFuncData fp
timer_stop_all();
}
-/// "tolower(string)" function
-static void f_tolower(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = strcase_save(tv_get_string(&argvars[0]), false);
-}
-
-/// "toupper(string)" function
-static void f_toupper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = strcase_save(tv_get_string(&argvars[0]), true);
-}
-
-/// "tr(string, fromstr, tostr)" function
-static void f_tr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- char buf[NUMBUFLEN];
- char buf2[NUMBUFLEN];
-
- 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.
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
- if (fromstr == NULL || tostr == NULL) {
- return; // Type error; errmsg already given.
- }
- garray_T ga;
- ga_init(&ga, (int)sizeof(char), 80);
-
- // fromstr and tostr have to contain the same number of chars.
- bool first = true;
- while (*in_str != NUL) {
- const char *cpstr = in_str;
- const int inlen = utfc_ptr2len(in_str);
- int cplen = inlen;
- int idx = 0;
- int fromlen;
- for (const char *p = fromstr; *p != NUL; p += fromlen) {
- fromlen = utfc_ptr2len(p);
- if (fromlen == inlen && strncmp(in_str, p, (size_t)inlen) == 0) {
- int tolen;
- for (p = tostr; *p != NUL; p += tolen) {
- tolen = utfc_ptr2len(p);
- if (idx-- == 0) {
- cplen = tolen;
- cpstr = (char *)p;
- break;
- }
- }
- if (*p == NUL) { // tostr is shorter than fromstr.
- goto error;
- }
- break;
- }
- 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;
- int tolen;
- for (const char *p = tostr; *p != NUL; p += tolen) {
- tolen = utfc_ptr2len(p);
- idx--;
- }
- if (idx != 0) {
- goto error;
- }
- }
-
- ga_grow(&ga, cplen);
- memmove((char *)ga.ga_data + ga.ga_len, cpstr, (size_t)cplen);
- ga.ga_len += cplen;
-
- in_str += inlen;
- }
-
- // add a terminating NUL
- ga_append(&ga, NUL);
-
- rettv->vval.v_string = ga.ga_data;
- return;
-error:
- semsg(_(e_invarg2), fromstr);
- ga_clear(&ga);
-}
-
-/// "trim({expr})" function
-static void f_trim(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- char buf1[NUMBUFLEN];
- char buf2[NUMBUFLEN];
- const char *head = tv_get_string_buf_chk(&argvars[0], buf1);
- const char *mask = NULL;
- const char *prev;
- const char *p;
- int dir = 0;
-
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
- if (head == NULL) {
- return;
- }
-
- if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_STRING) {
- semsg(_(e_invarg2), tv_get_string(&argvars[1]));
- return;
- }
-
- if (argvars[1].v_type == VAR_STRING) {
- mask = tv_get_string_buf_chk(&argvars[1], buf2);
- if (argvars[2].v_type != VAR_UNKNOWN) {
- bool error = false;
- // leading or trailing characters to trim
- dir = (int)tv_get_number_chk(&argvars[2], &error);
- if (error) {
- return;
- }
- if (dir < 0 || dir > 2) {
- semsg(_(e_invarg2), tv_get_string(&argvars[2]));
- return;
- }
- }
- }
-
- int c1;
- if (dir == 0 || dir == 1) {
- // Trim leading characters
- while (*head != NUL) {
- c1 = utf_ptr2char((char *)head);
- if (mask == NULL) {
- if (c1 > ' ' && c1 != 0xa0) {
- break;
- }
- } else {
- for (p = mask; *p != NUL; MB_PTR_ADV(p)) {
- if (c1 == utf_ptr2char((char *)p)) {
- break;
- }
- }
- if (*p == NUL) {
- break;
- }
- }
- MB_PTR_ADV(head);
- }
- }
-
- const char *tail = head + strlen(head);
- if (dir == 0 || dir == 2) {
- // Trim trailing characters
- for (; tail > head; tail = prev) {
- prev = tail;
- MB_PTR_BACK(head, prev);
- c1 = utf_ptr2char((char *)prev);
- if (mask == NULL) {
- if (c1 > ' ' && c1 != 0xa0) {
- break;
- }
- } else {
- for (p = mask; *p != NUL; MB_PTR_ADV(p)) {
- if (c1 == utf_ptr2char((char *)p)) {
- break;
- }
- }
- if (*p == NUL) {
- break;
- }
- }
- }
- }
- rettv->vval.v_string = xstrnsave(head, (size_t)(tail - head));
-}
-
/// "type(expr)" function
static void f_type(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -8933,49 +8780,31 @@ static void f_type(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
rettv->vval.v_number = n;
}
-/// "undofile(name)" function
-static void f_undofile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+/// "virtcol({expr}, [, {list} [, {winid}]])" function
+static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- rettv->v_type = VAR_STRING;
- 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(fname, true);
+ colnr_T vcol_start = 0;
+ colnr_T vcol_end = 0;
+ switchwin_T switchwin;
+ bool winchanged = false;
- if (ffname != NULL) {
- rettv->vval.v_string = u_get_undo_file_name(ffname, false);
+ if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) {
+ // use the window specified in the third argument
+ tabpage_T *tp;
+ win_T *wp = win_id2wp_tp((int)tv_get_number(&argvars[2]), &tp);
+ if (wp == NULL || tp == NULL) {
+ goto theend;
}
- xfree(ffname);
- }
-}
-/// "undotree()" function
-static void f_undotree(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- tv_dict_alloc_ret(rettv);
-
- dict_T *dict = rettv->vval.v_dict;
-
- 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);
+ if (switch_win_noblock(&switchwin, wp, tp, true) != OK) {
+ goto theend;
+ }
- tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(curbuf->b_u_oldhead));
-}
+ check_cursor();
+ winchanged = true;
+ }
-/// "virtcol(string)" function
-static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- colnr_T vcol = 0;
int fnum = curbuf->b_fnum;
-
pos_T *fp = var2fpos(&argvars[0], false, &fnum, false);
if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count
&& fnum == curbuf->b_fnum) {
@@ -8988,11 +8817,23 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
fp->col = (colnr_T)len;
}
}
- getvvcol(curwin, fp, NULL, NULL, &vcol);
- vcol++;
+ getvvcol(curwin, fp, &vcol_start, NULL, &vcol_end);
+ vcol_start++;
+ vcol_end++;
}
- rettv->vval.v_number = vcol;
+theend:
+ if (argvars[1].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[1])) {
+ tv_list_alloc_ret(rettv, 2);
+ tv_list_append_number(rettv->vval.v_list, vcol_start);
+ tv_list_append_number(rettv->vval.v_list, vcol_end);
+ } else {
+ rettv->vval.v_number = vcol_end;
+ }
+
+ if (winchanged) {
+ restore_win_noblock(&switchwin, true);
+ }
}
/// "visualmode()" function
@@ -9056,6 +8897,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
bool binary = false;
bool append = false;
+ bool defer = false;
bool do_fsync = !!p_fs;
bool mkdir_p = false;
if (argvars[2].v_type != VAR_UNKNOWN) {
@@ -9069,6 +8911,8 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
binary = true; break;
case 'a':
append = true; break;
+ case 'D':
+ defer = true; break;
case 's':
do_fsync = true; break;
case 'S':
@@ -9088,6 +8932,11 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (fname == NULL) {
return;
}
+
+ if (defer && !can_add_defer()) {
+ return;
+ }
+
FileDescriptor fp;
int error;
if (*fname == NUL) {
@@ -9096,9 +8945,17 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
((append ? kFileAppend : kFileTruncate)
| (mkdir_p ? kFileMkDir : kFileCreate)
| kFileCreate), 0666)) != 0) {
- semsg(_("E482: Can't open file %s for writing: %s"),
- fname, os_strerror(error));
+ semsg(_("E482: Can't open file %s for writing: %s"), fname, os_strerror(error));
} else {
+ if (defer) {
+ typval_T tv = {
+ .v_type = VAR_STRING,
+ .v_lock = VAR_UNLOCKED,
+ .vval.v_string = FullName_save(fname, false),
+ };
+ add_defer("delete", 1, &tv);
+ }
+
bool write_ok;
if (argvars[0].v_type == VAR_BLOB) {
write_ok = write_blob(&fp, argvars[0].vval.v_blob);
diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h
index 1ae031a952..0c345dacb4 100644
--- a/src/nvim/eval/funcs.h
+++ b/src/nvim/eval/funcs.h
@@ -1,23 +1,24 @@
-#ifndef NVIM_EVAL_FUNCS_H
-#define NVIM_EVAL_FUNCS_H
+#pragma once
#include <stdbool.h>
#include <stdint.h>
-#include "nvim/api/private/dispatch.h"
-#include "nvim/buffer_defs.h"
-#include "nvim/eval/typval.h"
+#include "nvim/buffer_defs.h" // IWYU pragma: keep
+#include "nvim/cmdexpand_defs.h" // IWYU pragma: keep
#include "nvim/eval/typval_defs.h"
-#include "nvim/types.h"
+#include "nvim/pos_defs.h" // IWYU pragma: keep
+#include "nvim/types_defs.h"
-/// Prototype of C function that implements VimL function
+/// Prototype of C function that implements Vimscript function
typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, EvalFuncData data);
/// Special flags for base_arg @see EvalFuncDef
-#define BASE_NONE 0 ///< Not a method (no base argument).
-#define BASE_LAST UINT8_MAX ///< Use the last argument as the method base.
+enum {
+ BASE_NONE = 0, ///< Not a method (no base argument).
+ BASE_LAST = UINT8_MAX, ///< Use the last argument as the method base.
+};
-/// Structure holding VimL function definition
+/// Structure holding Vimscript function definition
typedef struct {
char *name; ///< Name of the function.
uint8_t min_argc; ///< Minimal number of arguments.
@@ -31,4 +32,3 @@ typedef struct {
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/funcs.h.generated.h"
#endif
-#endif // NVIM_EVAL_FUNCS_H
diff --git a/src/nvim/eval/gc.c b/src/nvim/eval/gc.c
index 6a54c4ddc1..bcebd87f71 100644
--- a/src/nvim/eval/gc.c
+++ b/src/nvim/eval/gc.c
@@ -1,6 +1,3 @@
-// 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
-
#include <stddef.h>
#include "nvim/eval/gc.h"
diff --git a/src/nvim/eval/gc.h b/src/nvim/eval/gc.h
index 3185750c3b..36149ec060 100644
--- a/src/nvim/eval/gc.h
+++ b/src/nvim/eval/gc.h
@@ -1,13 +1,10 @@
-#ifndef NVIM_EVAL_GC_H
-#define NVIM_EVAL_GC_H
+#pragma once
-#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
extern dict_T *gc_first_dict;
extern list_T *gc_first_list;
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "eval/gc.h.generated.h"
+# include "eval/gc.h.generated.h" // IWYU pragma: export
#endif
-#endif // NVIM_EVAL_GC_H
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 05b4737206..069cdced34 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -1,16 +1,14 @@
-// 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
-
#include <assert.h>
+#include <lauxlib.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/types.h>
-#include "lauxlib.h"
-#include "nvim/ascii.h"
-#include "nvim/assert.h"
+#include "nvim/ascii_defs.h"
+#include "nvim/assert_defs.h"
#include "nvim/charset.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
@@ -21,33 +19,82 @@
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
#include "nvim/garray.h"
+#include "nvim/garray_defs.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/hashtab.h"
#include "nvim/lib/queue.h"
#include "nvim/lua/executor.h"
-#include "nvim/macros.h"
+#include "nvim/macros_defs.h"
#include "nvim/mbyte.h"
#include "nvim/mbyte_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/os/input.h"
-#include "nvim/pos.h"
-#include "nvim/types.h"
-#include "nvim/vim.h"
+#include "nvim/pos_defs.h"
+#include "nvim/strings.h"
+#include "nvim/types_defs.h"
+#include "nvim/vim_defs.h"
+
+/// struct storing information about current sort
+typedef struct {
+ int item_compare_ic;
+ bool item_compare_lc;
+ bool item_compare_numeric;
+ bool item_compare_numbers;
+ bool item_compare_float;
+ const char *item_compare_func;
+ partial_T *item_compare_partial;
+ dict_T *item_compare_selfdict;
+ bool item_compare_func_err;
+} sortinfo_T;
+
+/// Structure representing one list item, used for sort array.
+typedef struct {
+ listitem_T *item; ///< Sorted list item.
+ int idx; ///< Sorted list item index.
+} ListSortItem;
+
+typedef int (*ListSorter)(const void *, const void *);
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/typval.c.generated.h"
#endif
-static char e_string_required_for_argument_nr[]
+static const char e_variable_nested_too_deep_for_unlock[]
+ = N_("E743: Variable nested too deep for (un)lock");
+static const char e_using_invalid_value_as_string[]
+ = N_("E908: Using an invalid value as a String");
+static const char e_string_required_for_argument_nr[]
= N_("E1174: String required for argument %d");
-static char e_non_empty_string_required_for_argument_nr[]
+static const char e_non_empty_string_required_for_argument_nr[]
= N_("E1175: Non-empty string required for argument %d");
-static char e_number_required_for_argument_nr[]
+static const char e_dict_required_for_argument_nr[]
+ = N_("E1206: Dictionary required for argument %d");
+static const char e_number_required_for_argument_nr[]
= N_("E1210: Number required for argument %d");
-static char e_string_or_list_required_for_argument_nr[]
+static const char e_list_required_for_argument_nr[]
+ = N_("E1211: List required for argument %d");
+static const char e_bool_required_for_argument_nr[]
+ = N_("E1212: Bool required for argument %d");
+static const char e_float_or_number_required_for_argument_nr[]
+ = N_("E1219: Float or Number required for argument %d");
+static const char e_string_or_number_required_for_argument_nr[]
+ = N_("E1220: String or Number required for argument %d");
+static const char e_string_or_list_required_for_argument_nr[]
= N_("E1222: String or List required for argument %d");
+static const char e_list_or_blob_required_for_argument_nr[]
+ = N_("E1226: List or Blob required for argument %d");
+static const char e_blob_required_for_argument_nr[]
+ = N_("E1238: Blob required for argument %d");
+static const char e_invalid_value_for_blob_nr[]
+ = N_("E1239: Invalid value for blob: %d");
+static const char e_string_list_or_blob_required_for_argument_nr[]
+ = N_("E1252: String, List or Blob required for argument %d");
+static const char e_string_or_function_required_for_argument_nr[]
+ = N_("E1256: String or function required for argument %d");
+static const char e_non_null_dict_required_for_argument_nr[]
+ = N_("E1297: Non-NULL Dictionary required for argument %d");
bool tv_in_free_unref_items = false;
@@ -58,70 +105,6 @@ bool tv_in_free_unref_items = false;
const char *const tv_empty_string = "";
//{{{1 Lists
-//{{{2 List log
-#ifdef LOG_LIST_ACTIONS
-ListLog *list_log_first = NULL;
-ListLog *list_log_last = NULL;
-
-/// Write list log to the given file
-///
-/// @param[in] fname File to write log to. Will be appended to if already
-/// present.
-void list_write_log(const char *const fname)
- FUNC_ATTR_NONNULL_ALL
-{
- FileDescriptor fp;
- const int fo_ret = file_open(&fp, fname, kFileCreate|kFileAppend, 0600);
- if (fo_ret != 0) {
- semsg(_("E5142: Failed to open file %s: %s"), fname, os_strerror(fo_ret));
- return;
- }
- for (ListLog *chunk = list_log_first; chunk != NULL;) {
- for (size_t i = 0; i < chunk->size; i++) {
- char buf[10 + 1 + ((16 + 3) * 3) + (8 + 2) + 2];
- // act : hex " c:" len "[]" "\n\0"
- const ListLogEntry entry = chunk->entries[i];
- const size_t snp_len = (size_t)snprintf(buf, sizeof(buf),
- "%-10.10s: l:%016" PRIxPTR "[%08d] 1:%016" PRIxPTR " 2:%016" PRIxPTR
- "\n",
- entry.action, entry.l, entry.len, entry.li1,
- entry.li2);
- assert(snp_len + 1 == sizeof(buf));
- const ptrdiff_t fw_ret = file_write(&fp, buf, snp_len);
- if (fw_ret != (ptrdiff_t)snp_len) {
- assert(fw_ret < 0);
- if (i) {
- memmove(chunk->entries, chunk->entries + i,
- sizeof(chunk->entries[0]) * (chunk->size - i));
- chunk->size -= i;
- }
- semsg(_("E5143: Failed to write to file %s: %s"),
- fname, os_strerror((int)fw_ret));
- return;
- }
- }
- list_log_first = chunk->next;
- xfree(chunk);
- chunk = list_log_first;
- }
- const int fc_ret = file_close(&fp, true);
- if (fc_ret != 0) {
- semsg(_("E5144: Failed to close file %s: %s"), fname, os_strerror(fc_ret));
- }
-}
-
-# ifdef EXITFREE
-/// Free list log
-void list_free_log(void)
-{
- for (ListLog *chunk = list_log_first; chunk != NULL;) {
- list_log_first = chunk->next;
- xfree(chunk);
- chunk = list_log_first;
- }
-}
-# endif
-#endif
//{{{2 List item
/// Allocate a list item
@@ -210,7 +193,7 @@ void tv_list_watch_fix(list_T *const l, const listitem_T *const item)
/// Caller should take care of the reference count.
///
/// @param[in] len Expected number of items to be populated before list
-/// becomes accessible from VimL. It is still valid to
+/// becomes accessible from Vimscript. It is still valid to
/// underpopulate a list, value only controls how many elements
/// will be allocated in advance. Currently does nothing.
/// @see ListLenSpecials.
@@ -228,7 +211,6 @@ list_T *tv_list_alloc(const ptrdiff_t len)
list->lv_used_prev = NULL;
list->lv_used_next = gc_first_list;
gc_first_list = list;
- list_log(list, NULL, (void *)(uintptr_t)len, "alloc");
list->lua_table_ref = LUA_NOREF;
return list;
}
@@ -259,8 +241,6 @@ void tv_list_init_static10(staticList10_T *const sl)
li->li_prev = li - 1;
li->li_next = li + 1;
}
- list_log((const list_T *)sl, &sl->sl_items[0], &sl->sl_items[SL_SIZE - 1],
- "s10init");
#undef SL_SIZE
}
@@ -272,7 +252,6 @@ void tv_list_init_static(list_T *const l)
{
CLEAR_POINTER(l);
l->lv_refcount = DO_NOT_FREE_CNT;
- list_log(l, NULL, NULL, "sinit");
}
/// Free items contained in a list
@@ -281,7 +260,6 @@ void tv_list_init_static(list_T *const l)
void tv_list_free_contents(list_T *const l)
FUNC_ATTR_NONNULL_ALL
{
- list_log(l, NULL, NULL, "freecont");
for (listitem_T *item = l->lv_first; item != NULL; item = l->lv_first) {
// Remove the item before deleting it.
l->lv_first = item->li_next;
@@ -311,7 +289,6 @@ void tv_list_free_list(list_T *const l)
if (l->lv_used_next != NULL) {
l->lv_used_next->lv_used_prev = l->lv_used_prev;
}
- list_log(l, NULL, NULL, "freelist");
NLUA_CLEAR_REF(l->lua_table_ref);
xfree(l);
@@ -358,7 +335,6 @@ void tv_list_unref(list_T *const l)
void tv_list_drop_items(list_T *const l, listitem_T *const item, listitem_T *const item2)
FUNC_ATTR_NONNULL_ALL
{
- list_log(l, item, item2, "drop");
// Notify watchers.
for (listitem_T *ip = item; ip != item2->li_next; ip = ip->li_next) {
l->lv_len--;
@@ -376,14 +352,12 @@ void tv_list_drop_items(list_T *const l, listitem_T *const item, listitem_T *con
item->li_prev->li_next = item2->li_next;
}
l->lv_idx_item = NULL;
- list_log(l, l->lv_first, l->lv_last, "afterdrop");
}
/// Like tv_list_drop_items, but also frees all removed items
void tv_list_remove_items(list_T *const l, listitem_T *const item, listitem_T *const item2)
FUNC_ATTR_NONNULL_ALL
{
- list_log(l, item, item2, "remove");
tv_list_drop_items(l, item, item2);
for (listitem_T *li = item;;) {
tv_clear(TV_LIST_ITEM_TV(li));
@@ -407,7 +381,6 @@ void tv_list_move_items(list_T *const l, listitem_T *const item, listitem_T *con
list_T *const tgt_l, const int cnt)
FUNC_ATTR_NONNULL_ALL
{
- list_log(l, item, item2, "move");
tv_list_drop_items(l, item, item2);
item->li_prev = tgt_l->lv_last;
item2->li_next = NULL;
@@ -418,7 +391,6 @@ void tv_list_move_items(list_T *const l, listitem_T *const item, listitem_T *con
}
tgt_l->lv_last = item2;
tgt_l->lv_len += cnt;
- list_log(tgt_l, tgt_l->lv_first, tgt_l->lv_last, "movetgt");
}
/// Insert list item
@@ -446,11 +418,10 @@ void tv_list_insert(list_T *const l, listitem_T *const ni, listitem_T *const ite
}
item->li_prev = ni;
l->lv_len++;
- list_log(l, ni, item, "insert");
}
}
-/// Insert VimL value into a list
+/// Insert Vimscript value into a list
///
/// @param[out] l List to insert to.
/// @param[in,out] tv Value to insert. Is copied (@see tv_copy()) to an
@@ -472,7 +443,6 @@ void tv_list_insert_tv(list_T *const l, typval_T *const tv, listitem_T *const it
void tv_list_append(list_T *const l, listitem_T *const item)
FUNC_ATTR_NONNULL_ALL
{
- list_log(l, item, NULL, "append");
if (l->lv_last == NULL) {
// empty list
l->lv_first = item;
@@ -487,7 +457,7 @@ void tv_list_append(list_T *const l, listitem_T *const item)
item->li_next = NULL;
}
-/// Append VimL value to the end of list
+/// Append Vimscript value to the end of list
///
/// @param[out] l List to append to.
/// @param[in,out] tv Value to append. Is copied (@see tv_copy()) to an
@@ -647,55 +617,166 @@ tv_list_copy_error:
return NULL;
}
-/// Flatten "list" in place to depth "maxdepth".
+/// Get the list item in "l" with index "n1". "n1" is adjusted if needed.
+/// Return NULL if there is no such item.
+listitem_T *tv_list_check_range_index_one(list_T *const l, int *const n1, const bool quiet)
+{
+ listitem_T *li = tv_list_find_index(l, n1);
+ if (li == NULL) {
+ if (!quiet) {
+ semsg(_(e_list_index_out_of_range_nr), (int64_t)(*n1));
+ }
+ return NULL;
+ }
+ return li;
+}
+
+/// Check that "n2" can be used as the second index in a range of list "l".
+/// If "n1" or "n2" is negative it is changed to the positive index.
+/// "li1" is the item for item "n1".
+/// Return OK or FAIL.
+int tv_list_check_range_index_two(list_T *const l, int *const n1, const listitem_T *const li1,
+ int *const n2, const bool quiet)
+{
+ if (*n2 < 0) {
+ listitem_T *ni = tv_list_find(l, *n2);
+ if (ni == NULL) {
+ if (!quiet) {
+ semsg(_(e_list_index_out_of_range_nr), (int64_t)(*n2));
+ }
+ return FAIL;
+ }
+ *n2 = tv_list_idx_of_item(l, ni);
+ }
+
+ // Check that n2 isn't before n1.
+ if (*n1 < 0) {
+ *n1 = tv_list_idx_of_item(l, li1);
+ }
+ if (*n2 < *n1) {
+ if (!quiet) {
+ semsg(_(e_list_index_out_of_range_nr), (int64_t)(*n2));
+ }
+ return FAIL;
+ }
+ return OK;
+}
+
+/// Assign values from list "src" into a range of "dest".
+/// "idx1_arg" is the index of the first item in "dest" to be replaced.
+/// "idx2" is the index of last item to be replaced, but when "empty_idx2" is
+/// true then replace all items after "idx1".
+/// "op" is the operator, normally "=" but can be "+=" and the like.
+/// "varname" is used for error messages.
+/// Returns OK or FAIL.
+int tv_list_assign_range(list_T *const dest, list_T *const src, const int idx1_arg, const int idx2,
+ const bool empty_idx2, const char *const op, const char *const varname)
+{
+ int idx1 = idx1_arg;
+ listitem_T *const first_li = tv_list_find_index(dest, &idx1);
+ listitem_T *src_li;
+
+ // Check whether any of the list items is locked before making any changes.
+ int idx = idx1;
+ listitem_T *dest_li = first_li;
+ for (src_li = tv_list_first(src); src_li != NULL && dest_li != NULL;) {
+ if (value_check_lock(TV_LIST_ITEM_TV(dest_li)->v_lock, varname, TV_CSTRING)) {
+ return FAIL;
+ }
+ src_li = TV_LIST_ITEM_NEXT(src, src_li);
+ if (src_li == NULL || (!empty_idx2 && idx2 == idx)) {
+ break;
+ }
+ dest_li = TV_LIST_ITEM_NEXT(dest, dest_li);
+ idx++;
+ }
+
+ // Assign the List values to the list items.
+ idx = idx1;
+ dest_li = first_li;
+ for (src_li = tv_list_first(src); src_li != NULL;) {
+ assert(dest_li != NULL);
+ if (op != NULL && *op != '=') {
+ eexe_mod_op(TV_LIST_ITEM_TV(dest_li), TV_LIST_ITEM_TV(src_li), op);
+ } else {
+ tv_clear(TV_LIST_ITEM_TV(dest_li));
+ tv_copy(TV_LIST_ITEM_TV(src_li), TV_LIST_ITEM_TV(dest_li));
+ }
+ src_li = TV_LIST_ITEM_NEXT(src, src_li);
+ if (src_li == NULL || (!empty_idx2 && idx2 == idx)) {
+ break;
+ }
+ if (TV_LIST_ITEM_NEXT(dest, dest_li) == NULL) {
+ // Need to add an empty item.
+ tv_list_append_number(dest, 0);
+ // "dest_li" may have become invalid after append, don’t use it.
+ dest_li = tv_list_last(dest); // Valid again.
+ } else {
+ dest_li = TV_LIST_ITEM_NEXT(dest, dest_li);
+ }
+ idx++;
+ }
+ if (src_li != NULL) {
+ emsg(_("E710: List value has more items than target"));
+ return FAIL;
+ }
+ if (empty_idx2
+ ? (dest_li != NULL && TV_LIST_ITEM_NEXT(dest, dest_li) != NULL)
+ : idx != idx2) {
+ emsg(_("E711: List value has not enough items"));
+ return FAIL;
+ }
+ return OK;
+}
+
+/// Flatten up to "maxitems" in "list", starting at "first" to depth "maxdepth".
+/// When "first" is NULL use the first item.
/// Does nothing if "maxdepth" is 0.
///
/// @param[in,out] list List to flatten
/// @param[in] maxdepth Maximum depth that will be flattened
///
/// @return OK or FAIL
-int tv_list_flatten(list_T *list, long maxdepth)
- FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT
+void tv_list_flatten(list_T *list, listitem_T *first, int64_t maxitems, int64_t maxdepth)
+ FUNC_ATTR_NONNULL_ARG(1)
{
listitem_T *item;
- listitem_T *to_free;
- int n;
+ int done = 0;
if (maxdepth == 0) {
- return OK;
+ return;
+ }
+
+ if (first == NULL) {
+ item = list->lv_first;
+ } else {
+ item = first;
}
- n = 0;
- item = list->lv_first;
- while (item != NULL) {
+ while (item != NULL && done < maxitems) {
+ listitem_T *next = item->li_next;
+
fast_breakcheck();
if (got_int) {
- return FAIL;
+ return;
}
if (item->li_tv.v_type == VAR_LIST) {
- listitem_T *next = item->li_next;
+ list_T *itemlist = item->li_tv.vval.v_list;
tv_list_drop_items(list, item, item);
- tv_list_extend(list, item->li_tv.vval.v_list, next);
- tv_clear(&item->li_tv);
- to_free = item;
+ tv_list_extend(list, itemlist, next);
- if (item->li_prev == NULL) {
- item = list->lv_first;
- } else {
- item = item->li_prev->li_next;
- }
- xfree(to_free);
-
- if (++n >= maxdepth) {
- n = 0;
- item = next;
+ if (maxdepth > 0) {
+ tv_list_flatten(list,
+ item->li_prev == NULL ? list->lv_first : item->li_prev->li_next,
+ itemlist->lv_len, maxdepth - 1);
}
- } else {
- n = 0;
- item = item->li_next;
+ tv_clear(&item->li_tv);
+ xfree(item);
}
+
+ done++;
+ item = next;
}
- return OK;
}
/// Extend first list with the second
@@ -750,9 +831,67 @@ int tv_list_concat(list_T *const l1, list_T *const l2, typval_T *const tv)
return OK;
}
+static list_T *tv_list_slice(list_T *ol, varnumber_T n1, varnumber_T n2)
+{
+ list_T *l = tv_list_alloc(n2 - n1 + 1);
+ listitem_T *item = tv_list_find(ol, (int)n1);
+ for (; n1 <= n2; n1++) {
+ tv_list_append_tv(l, TV_LIST_ITEM_TV(item));
+ item = TV_LIST_ITEM_NEXT(rettv->vval.v_list, item);
+ }
+ return l;
+}
+
+int tv_list_slice_or_index(list_T *list, bool range, varnumber_T n1_arg, varnumber_T n2_arg,
+ bool exclusive, typval_T *rettv, bool verbose)
+{
+ int len = tv_list_len(rettv->vval.v_list);
+ varnumber_T n1 = n1_arg;
+ varnumber_T n2 = n2_arg;
+
+ if (n1 < 0) {
+ n1 = len + n1;
+ }
+ if (n1 < 0 || n1 >= len) {
+ // For a range we allow invalid values and return an empty list.
+ // A list index out of range is an error.
+ if (!range) {
+ if (verbose) {
+ semsg(_(e_list_index_out_of_range_nr), (int64_t)n1);
+ }
+ return FAIL;
+ }
+ n1 = len;
+ }
+ if (range) {
+ if (n2 < 0) {
+ n2 = len + n2;
+ } else if (n2 >= len) {
+ n2 = len - (exclusive ? 0 : 1);
+ }
+ if (exclusive) {
+ n2--;
+ }
+ if (n2 < 0 || n2 + 1 < n1) {
+ n2 = -1;
+ }
+ list_T *l = tv_list_slice(rettv->vval.v_list, n1, n2);
+ tv_clear(rettv);
+ tv_list_set_ret(rettv, l);
+ } else {
+ // copy the item to "var1" to avoid that freeing the list makes it
+ // invalid.
+ typval_T var1;
+ tv_copy(TV_LIST_ITEM_TV(tv_list_find(rettv->vval.v_list, (int)n1)), &var1);
+ tv_clear(rettv);
+ *rettv = var1;
+ }
+ return OK;
+}
+
typedef struct {
- char_u *s;
- char_u *tofree;
+ char *s;
+ char *tofree;
} Join;
/// Join list into a string, helper function
@@ -785,7 +924,7 @@ static int list_join_inner(garray_T *const gap, list_T *const l, const char *con
sumlen += len;
Join *const p = GA_APPEND_VIA_PTR(Join, join_gap);
- p->tofree = p->s = (char_u *)s;
+ p->tofree = p->s = s;
line_breakcheck();
});
@@ -806,7 +945,7 @@ static int list_join_inner(garray_T *const gap, list_T *const l, const char *con
const Join *const p = ((const Join *)join_gap->ga_data) + i;
if (p->s != NULL) {
- ga_concat(gap, (char *)p->s);
+ ga_concat(gap, p->s);
}
line_breakcheck();
}
@@ -886,8 +1025,8 @@ void f_list2str(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
char buf[MB_MAXBYTES + 1];
TV_LIST_ITER_CONST(l, li, {
- buf[utf_char2bytes((int)tv_get_number(TV_LIST_ITEM_TV(li)), (char *)buf)] = NUL;
- ga_concat(&ga, (char *)buf);
+ buf[utf_char2bytes((int)tv_get_number(TV_LIST_ITEM_TV(li)), buf)] = NUL;
+ ga_concat(&ga, buf);
});
ga_append(&ga, NUL);
@@ -905,14 +1044,14 @@ void tv_list_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
return;
}
- long idx = tv_get_number_chk(&argvars[1], &error);
+ int64_t idx = tv_get_number_chk(&argvars[1], &error);
listitem_T *item;
if (error) {
// Type error: do nothing, errmsg already given.
} else if ((item = tv_list_find(l, (int)idx)) == NULL) {
- semsg(_(e_listidx), (int64_t)idx);
+ semsg(_(e_list_index_out_of_range_nr), idx);
} else {
if (argvars[2].v_type == VAR_UNKNOWN) {
// Remove one item, return its value.
@@ -922,11 +1061,11 @@ void tv_list_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
} else {
listitem_T *item2;
// Remove range of items, return list with values.
- long end = tv_get_number_chk(&argvars[2], &error);
+ int64_t end = tv_get_number_chk(&argvars[2], &error);
if (error) {
// Type error: do nothing.
} else if ((item2 = tv_list_find(l, (int)end)) == NULL) {
- semsg(_(e_listidx), (int64_t)end);
+ semsg(_(e_list_index_out_of_range_nr), end);
} else {
int cnt = 0;
@@ -948,18 +1087,6 @@ void tv_list_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
}
}
-/// struct storing information about current sort
-typedef struct {
- int item_compare_ic;
- bool item_compare_lc;
- bool item_compare_numeric;
- bool item_compare_numbers;
- bool item_compare_float;
- const char *item_compare_func;
- partial_T *item_compare_partial;
- dict_T *item_compare_selfdict;
- bool item_compare_func_err;
-} sortinfo_T;
static sortinfo_T *sortinfo = NULL;
#define ITEM_COMPARE_FAIL 999
@@ -1027,12 +1154,11 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
if (sortinfo->item_compare_lc) {
res = strcoll(p1, p2);
} else {
- res = sortinfo->item_compare_ic ? STRICMP(p1, p2): strcmp(p1, p2);
+ res = sortinfo->item_compare_ic ? STRICMP(p1, p2) : strcmp(p1, p2);
}
} else {
- double n1, n2;
- n1 = strtod(p1, &p1);
- n2 = strtod(p2, &p2);
+ double n1 = strtod(p1, &p1);
+ double n2 = strtod(p2, &p2);
res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1;
}
@@ -1062,8 +1188,6 @@ static int item_compare_not_keeping_zero(const void *s1, const void *s2)
static int item_compare2(const void *s1, const void *s2, bool keep_zero)
{
- ListSortItem *si1, *si2;
- int res;
typval_T rettv;
typval_T argv[3];
const char *func_name;
@@ -1074,13 +1198,13 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
return 0;
}
- si1 = (ListSortItem *)s1;
- si2 = (ListSortItem *)s2;
+ ListSortItem *si1 = (ListSortItem *)s1;
+ ListSortItem *si2 = (ListSortItem *)s2;
if (partial == NULL) {
func_name = sortinfo->item_compare_func;
} else {
- func_name = (const char *)partial_name(partial);
+ func_name = partial_name(partial);
}
// Copy the values. This is needed to be able to set v_lock to VAR_FIXED
@@ -1093,7 +1217,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
funcexe.fe_evaluate = true;
funcexe.fe_partial = partial;
funcexe.fe_selfdict = sortinfo->item_compare_selfdict;
- res = call_func(func_name, -1, &rettv, 2, argv, &funcexe);
+ int res = call_func(func_name, -1, &rettv, 2, argv, &funcexe);
tv_clear(&argv[0]);
tv_clear(&argv[1]);
@@ -1135,149 +1259,188 @@ static int item_compare2_not_keeping_zero(const void *s1, const void *s2)
return item_compare2(s1, s2, false);
}
-/// "sort({list})" function
-static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
+/// sort() List "l"
+static void do_sort(list_T *l, sortinfo_T *info)
{
- ListSortItem *ptrs;
- long len;
- long i;
+ const int len = tv_list_len(l);
- // Pointer to current info struct used in compare function. Save and restore
- // the current one for nested calls.
- sortinfo_T info;
- sortinfo_T *old_sortinfo = sortinfo;
- sortinfo = &info;
+ // Make an array with each entry pointing to an item in the List.
+ ListSortItem *ptrs = xmalloc((size_t)((unsigned)len * sizeof(ListSortItem)));
+
+ // f_sort(): ptrs will be the list to sort
+ int i = 0;
+ TV_LIST_ITER(l, li, {
+ ptrs[i].item = li;
+ ptrs[i].idx = i;
+ i++;
+ });
- const char *const arg_errmsg = (sort
- ? N_("sort() argument")
- : N_("uniq() argument"));
+ info->item_compare_func_err = false;
+ ListSorter item_compare_func = ((info->item_compare_func == NULL
+ && info->item_compare_partial == NULL)
+ ? item_compare_not_keeping_zero
+ : item_compare2_not_keeping_zero);
- if (argvars[0].v_type != VAR_LIST) {
- semsg(_(e_listarg), sort ? "sort()" : "uniq()");
- } else {
- list_T *const l = argvars[0].vval.v_list;
- if (value_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) {
- goto theend;
+ // Sort the array with item pointers.
+ qsort(ptrs, (size_t)len, sizeof(ListSortItem), item_compare_func);
+ if (!info->item_compare_func_err) {
+ // Clear the list and append the items in the sorted order.
+ l->lv_first = NULL;
+ l->lv_last = NULL;
+ l->lv_idx_item = NULL;
+ l->lv_len = 0;
+ for (i = 0; i < len; i++) {
+ tv_list_append(l, ptrs[i].item);
}
- tv_list_set_ret(rettv, l);
+ }
+ if (info->item_compare_func_err) {
+ emsg(_("E702: Sort compare function failed"));
+ }
- len = tv_list_len(l);
- if (len <= 1) {
- goto theend; // short list sorts pretty quickly
- }
-
- info.item_compare_ic = false;
- info.item_compare_lc = false;
- info.item_compare_numeric = false;
- info.item_compare_numbers = false;
- info.item_compare_float = false;
- info.item_compare_func = NULL;
- info.item_compare_partial = NULL;
- info.item_compare_selfdict = NULL;
-
- if (argvars[1].v_type != VAR_UNKNOWN) {
- // optional second argument: {func}
- if (argvars[1].v_type == VAR_FUNC) {
- 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 {
- bool error = false;
+ xfree(ptrs);
+}
- 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 = tv_get_string(&argvars[1]);
- } else if (i != 0) {
- emsg(_(e_invarg));
- goto theend;
- }
- if (info.item_compare_func != NULL) {
- 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) {
- info.item_compare_func = NULL;
- info.item_compare_numeric = true;
- } 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) {
- info.item_compare_func = NULL;
- info.item_compare_float = true;
- } else if (strcmp(info.item_compare_func, "i") == 0) {
- info.item_compare_func = NULL;
- info.item_compare_ic = true;
- } else if (strcmp(info.item_compare_func, "l") == 0) {
- info.item_compare_func = NULL;
- info.item_compare_lc = true;
- }
- }
- }
+/// uniq() List "l"
+static void do_uniq(list_T *l, sortinfo_T *info)
+{
+ const int len = tv_list_len(l);
- if (argvars[2].v_type != VAR_UNKNOWN) {
- // optional third argument: {dict}
- if (argvars[2].v_type != VAR_DICT) {
- emsg(_(e_dictreq));
- goto theend;
- }
- info.item_compare_selfdict = argvars[2].vval.v_dict;
- }
- }
+ // Make an array with each entry pointing to an item in the List.
+ ListSortItem *ptrs = xmalloc((size_t)((unsigned)len * sizeof(ListSortItem)));
- // Make an array with each entry pointing to an item in the List.
- ptrs = xmalloc((size_t)((unsigned)len * sizeof(ListSortItem)));
+ // f_uniq(): ptrs will be a stack of items to remove.
- if (sort) {
- info.item_compare_func_err = false;
- tv_list_item_sort(l, ptrs,
- ((info.item_compare_func == NULL
- && info.item_compare_partial == NULL)
- ? item_compare_not_keeping_zero
- : item_compare2_not_keeping_zero),
- &info.item_compare_func_err);
- if (info.item_compare_func_err) {
- emsg(_("E702: Sort compare function failed"));
- }
+ info->item_compare_func_err = false;
+ ListSorter item_compare_func = ((info->item_compare_func == NULL
+ && info->item_compare_partial == NULL)
+ ? item_compare_keeping_zero
+ : item_compare2_keeping_zero);
+
+ for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l)); li != NULL;) {
+ listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li);
+ if (item_compare_func(&prev_li, &li) == 0) {
+ li = tv_list_item_remove(l, li);
} else {
- ListSorter item_compare_func_ptr;
+ li = TV_LIST_ITEM_NEXT(l, li);
+ }
+ if (info->item_compare_func_err) {
+ emsg(_("E882: Uniq compare function failed"));
+ break;
+ }
+ }
- // f_uniq(): ptrs will be a stack of items to remove.
- info.item_compare_func_err = false;
- if (info.item_compare_func != NULL
- || info.item_compare_partial != NULL) {
- item_compare_func_ptr = item_compare2_keeping_zero;
- } else {
- item_compare_func_ptr = item_compare_keeping_zero;
- }
+ xfree(ptrs);
+}
- for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l))
- ; li != NULL;) {
- listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li);
- if (item_compare_func_ptr(&prev_li, &li) == 0) {
- li = tv_list_item_remove(l, li);
- } else {
- li = TV_LIST_ITEM_NEXT(l, li);
- }
- if (info.item_compare_func_err) {
- emsg(_("E882: Uniq compare function failed"));
- break;
- }
+/// Parse the optional arguments to sort() and uniq() and return the values in "info".
+static int parse_sort_uniq_args(typval_T *argvars, sortinfo_T *info)
+{
+ info->item_compare_ic = false;
+ info->item_compare_lc = false;
+ info->item_compare_numeric = false;
+ info->item_compare_numbers = false;
+ info->item_compare_float = false;
+ info->item_compare_func = NULL;
+ info->item_compare_partial = NULL;
+ info->item_compare_selfdict = NULL;
+
+ if (argvars[1].v_type == VAR_UNKNOWN) {
+ return OK;
+ }
+
+ // optional second argument: {func}
+ if (argvars[1].v_type == VAR_FUNC) {
+ info->item_compare_func = argvars[1].vval.v_string;
+ } else if (argvars[1].v_type == VAR_PARTIAL) {
+ info->item_compare_partial = argvars[1].vval.v_partial;
+ } else {
+ bool error = false;
+ int nr = (int)tv_get_number_chk(&argvars[1], &error);
+ if (error) {
+ return FAIL; // type error; errmsg already given
+ }
+ if (nr == 1) {
+ info->item_compare_ic = true;
+ } else if (argvars[1].v_type != VAR_NUMBER) {
+ info->item_compare_func = tv_get_string(&argvars[1]);
+ } else if (nr != 0) {
+ emsg(_(e_invarg));
+ return FAIL;
+ }
+ if (info->item_compare_func != NULL) {
+ 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) {
+ info->item_compare_func = NULL;
+ info->item_compare_numeric = true;
+ } 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) {
+ info->item_compare_func = NULL;
+ info->item_compare_float = true;
+ } else if (strcmp(info->item_compare_func, "i") == 0) {
+ info->item_compare_func = NULL;
+ info->item_compare_ic = true;
+ } else if (strcmp(info->item_compare_func, "l") == 0) {
+ info->item_compare_func = NULL;
+ info->item_compare_lc = true;
}
}
+ }
+
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ // optional third argument: {dict}
+ if (tv_check_for_dict_arg(argvars, 2) == FAIL) {
+ return FAIL;
+ }
+ info->item_compare_selfdict = argvars[2].vval.v_dict;
+ }
+
+ return OK;
+}
+
+/// "sort()" or "uniq()" function
+static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
+{
+ if (argvars[0].v_type != VAR_LIST) {
+ semsg(_(e_listarg), sort ? "sort()" : "uniq()");
+ return;
+ }
- xfree(ptrs);
+ // Pointer to current info struct used in compare function. Save and restore
+ // the current one for nested calls.
+ sortinfo_T info;
+ sortinfo_T *old_sortinfo = sortinfo;
+ sortinfo = &info;
+
+ const char *const arg_errmsg = (sort ? N_("sort() argument") : N_("uniq() argument"));
+ list_T *const l = argvars[0].vval.v_list;
+ if (value_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) {
+ goto theend;
+ }
+ tv_list_set_ret(rettv, l);
+
+ const int len = tv_list_len(l);
+ if (len <= 1) {
+ goto theend; // short list sorts pretty quickly
+ }
+ if (parse_sort_uniq_args(argvars, &info) == FAIL) {
+ goto theend;
+ }
+
+ if (sort) {
+ do_sort(l, &info);
+ } else {
+ do_uniq(l, &info);
}
theend:
sortinfo = old_sortinfo;
}
-/// "sort"({list})" function
+/// "sort({list})" function
void f_sort(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
do_sort_uniq(argvars, rettv, true);
@@ -1336,7 +1499,6 @@ void tv_list_reverse(list_T *const l)
if (tv_list_len(l) <= 1) {
return;
}
- list_log(l, NULL, NULL, "reverse");
#define SWAP(a, b) \
do { \
tmp = (a); \
@@ -1354,47 +1516,6 @@ void tv_list_reverse(list_T *const l)
l->lv_idx = l->lv_len - l->lv_idx - 1;
}
-// FIXME Add unit tests for tv_list_item_sort().
-
-/// Sort list using libc qsort
-///
-/// @param[in,out] l List to sort, will be sorted in-place.
-/// @param ptrs Preallocated array of items to sort, must have at least
-/// tv_list_len(l) entries. Should not be initialized.
-/// @param[in] item_compare_func Function used to compare list items.
-/// @param errp Location where information about whether error occurred is
-/// saved by item_compare_func. If boolean there appears to be
-/// true list will not be modified. Must be initialized to false
-/// by the caller.
-void tv_list_item_sort(list_T *const l, ListSortItem *const ptrs,
- const ListSorter item_compare_func, const bool *errp)
- FUNC_ATTR_NONNULL_ARG(3, 4)
-{
- const int len = tv_list_len(l);
- if (len <= 1) {
- return;
- }
- list_log(l, NULL, NULL, "sort");
- int i = 0;
- TV_LIST_ITER(l, li, {
- ptrs[i].item = li;
- ptrs[i].idx = i;
- i++;
- });
- // Sort the array with item pointers.
- qsort(ptrs, (size_t)len, sizeof(ListSortItem), item_compare_func);
- if (!(*errp)) {
- // Clear the list and append the items in the sorted order.
- l->lv_first = NULL;
- l->lv_last = NULL;
- l->lv_idx_item = NULL;
- l->lv_len = 0;
- for (i = 0; i < len; i++) {
- tv_list_append(l, ptrs[i].item);
- }
- }
-}
-
//{{{2 Indexing/searching
/// Locate item with a given index in a list and return it
@@ -1463,7 +1584,6 @@ listitem_T *tv_list_find(list_T *const l, int n)
// Cache the used index.
l->lv_idx = idx;
l->lv_idx_item = item;
- list_log(l, l->lv_idx_item, (void *)(uintptr_t)l->lv_idx, "find");
return item;
}
@@ -1501,19 +1621,34 @@ const char *tv_list_find_str(list_T *const l, const int n)
{
const listitem_T *const li = tv_list_find(l, n);
if (li == NULL) {
- semsg(_(e_listidx), (int64_t)n);
+ semsg(_(e_list_index_out_of_range_nr), (int64_t)n);
return NULL;
}
return tv_get_string(TV_LIST_ITEM_TV(li));
}
+/// Like tv_list_find() but when a negative index is used that is not found use
+/// zero and set "idx" to zero. Used for first index of a range.
+static listitem_T *tv_list_find_index(list_T *const l, int *const idx)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ listitem_T *li = tv_list_find(l, *idx);
+ if (li == NULL) {
+ if (*idx < 0) {
+ *idx = 0;
+ li = tv_list_find(l, *idx);
+ }
+ }
+ return li;
+}
+
/// Locate item in a list and return its index
///
/// @param[in] l List to search.
/// @param[in] item Item to search for.
///
/// @return Index of an item or -1 if item is not in the list.
-long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item)
+int tv_list_idx_of_item(const list_T *const l, const listitem_T *const item)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
{
if (l == NULL) {
@@ -1673,7 +1808,7 @@ char *callback_to_string(Callback *cb)
}
const size_t msglen = 100;
- char *msg = (char *)xmallocz(msglen);
+ char *msg = xmallocz(msglen);
switch (cb->type) {
case kCallbackFuncref:
@@ -1883,7 +2018,7 @@ void tv_dict_item_free(dictitem_T *const item)
dictitem_T *tv_dict_item_copy(dictitem_T *const di)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
- dictitem_T *const new_di = tv_dict_item_alloc((const char *)di->di_key);
+ dictitem_T *const new_di = tv_dict_item_alloc(di->di_key);
tv_copy(&di->di_tv, &new_di->di_tv);
return new_di;
}
@@ -1895,7 +2030,7 @@ dictitem_T *tv_dict_item_copy(dictitem_T *const di)
void tv_dict_item_remove(dict_T *const dict, dictitem_T *const item)
FUNC_ATTR_NONNULL_ALL
{
- hashitem_T *const hi = hash_find(&dict->dv_hashtab, (char *)item->di_key);
+ hashitem_T *const hi = hash_find(&dict->dv_hashtab, item->di_key);
if (HASHITEM_EMPTY(hi)) {
semsg(_(e_intern2), "tv_dict_item_remove()");
} else {
@@ -2086,6 +2221,16 @@ varnumber_T tv_dict_get_number_def(const dict_T *const d, const char *const key,
return tv_get_number(&di->di_tv);
}
+varnumber_T tv_dict_get_bool(const dict_T *const d, const char *const key, const int def)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ dictitem_T *const di = tv_dict_find(d, key, -1);
+ if (di == NULL) {
+ return def;
+ }
+ return tv_get_bool(&di->di_tv);
+}
+
/// Converts a dict to an environment
char **tv_dict_to_env(dict_T *denv)
{
@@ -2100,9 +2245,9 @@ char **tv_dict_to_env(dict_T *denv)
TV_DICT_ITER(denv, var, {
const char *str = tv_get_string(&var->di_tv);
assert(str);
- size_t len = strlen((char *)var->di_key) + strlen(str) + strlen("=") + 1;
+ size_t len = strlen(var->di_key) + strlen(str) + strlen("=") + 1;
env[i] = xmalloc(len);
- snprintf(env[i], len, "%s=%s", (char *)var->di_key, str);
+ snprintf(env[i], len, "%s=%s", var->di_key, str);
i++;
});
@@ -2229,10 +2374,10 @@ int tv_dict_wrong_func_name(dict_T *d, typval_T *tv, const char *name)
int tv_dict_add(dict_T *const d, dictitem_T *const item)
FUNC_ATTR_NONNULL_ALL
{
- if (tv_dict_wrong_func_name(d, &item->di_tv, (const char *)item->di_key)) {
+ if (tv_dict_wrong_func_name(d, &item->di_tv, item->di_key)) {
return FAIL;
}
- return hash_add(&d->dv_hashtab, (char *)item->di_key);
+ return hash_add(&d->dv_hashtab, item->di_key);
}
/// Add a list entry to dictionary
@@ -2466,9 +2611,9 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action
HASHTAB_ITER(&d2->dv_hashtab, hi2, {
dictitem_T *const di2 = TV_DICT_HI2DI(hi2);
- dictitem_T *const di1 = tv_dict_find(d1, (const char *)di2->di_key, -1);
+ dictitem_T *const di1 = tv_dict_find(d1, di2->di_key, -1);
// Check the key to be valid when adding to any scope.
- if (d1->dv_scope != VAR_NO_SCOPE && !valid_varname((const char *)di2->di_key)) {
+ if (d1->dv_scope != VAR_NO_SCOPE && !valid_varname(di2->di_key)) {
break;
}
if (di1 == NULL) {
@@ -2478,14 +2623,14 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action
dictitem_T *const new_di = di2;
if (tv_dict_add(d1, new_di) == OK) {
hash_remove(&d2->dv_hashtab, hi2);
- tv_dict_watcher_notify(d1, (const char *)new_di->di_key, &new_di->di_tv, NULL);
+ tv_dict_watcher_notify(d1, new_di->di_key, &new_di->di_tv, NULL);
}
} else {
dictitem_T *const new_di = tv_dict_item_copy(di2);
if (tv_dict_add(d1, new_di) == FAIL) {
tv_dict_item_free(new_di);
} else if (watched) {
- tv_dict_watcher_notify(d1, (const char *)new_di->di_key, &new_di->di_tv, NULL);
+ tv_dict_watcher_notify(d1, new_di->di_key, &new_di->di_tv, NULL);
}
}
} else if (*action == 'e') {
@@ -2499,7 +2644,7 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action
break;
}
// Disallow replacing a builtin function.
- if (tv_dict_wrong_func_name(d1, &di2->di_tv, (const char *)di2->di_key)) {
+ if (tv_dict_wrong_func_name(d1, &di2->di_tv, di2->di_key)) {
break;
}
@@ -2511,8 +2656,7 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action
tv_copy(&di2->di_tv, &di1->di_tv);
if (watched) {
- tv_dict_watcher_notify(d1, (const char *)di1->di_key, &di1->di_tv,
- &oldtv);
+ tv_dict_watcher_notify(d1, di1->di_key, &di1->di_tv, &oldtv);
tv_clear(&oldtv);
}
}
@@ -2547,7 +2691,7 @@ bool tv_dict_equal(dict_T *const d1, dict_T *const d2, const bool ic, const bool
}
TV_DICT_ITER(d1, di1, {
- dictitem_T *const di2 = tv_dict_find(d2, (const char *)di1->di_key, -1);
+ dictitem_T *const di2 = tv_dict_find(d2, di1->di_key, -1);
if (di2 == NULL) {
return false;
}
@@ -2586,12 +2730,12 @@ dict_T *tv_dict_copy(const vimconv_T *const conv, dict_T *const orig, const bool
}
dictitem_T *new_di;
if (conv == NULL || conv->vc_type == CONV_NONE) {
- new_di = tv_dict_item_alloc((const char *)di->di_key);
+ new_di = tv_dict_item_alloc(di->di_key);
} else {
- size_t len = strlen((char *)di->di_key);
- char *const key = (char *)string_convert(conv, (char *)di->di_key, &len);
+ size_t len = strlen(di->di_key);
+ char *const key = string_convert(conv, di->di_key, &len);
if (key == NULL) {
- new_di = tv_dict_item_alloc_len((const char *)di->di_key, len);
+ new_di = tv_dict_item_alloc_len(di->di_key, len);
} else {
new_di = tv_dict_item_alloc_len(key, len);
xfree(key);
@@ -2705,6 +2849,136 @@ bool tv_blob_equal(const blob_T *const b1, const blob_T *const b2)
return true;
}
+/// Returns a slice of "blob" from index "n1" to "n2" in "rettv". The length of
+/// the blob is "len". Returns an empty blob if the indexes are out of range.
+static int tv_blob_slice(const blob_T *blob, int len, varnumber_T n1, varnumber_T n2,
+ bool exclusive, typval_T *rettv)
+{
+ // The resulting variable is a sub-blob. If the indexes
+ // are out of range the result is empty.
+ if (n1 < 0) {
+ n1 = len + n1;
+ if (n1 < 0) {
+ n1 = 0;
+ }
+ }
+ if (n2 < 0) {
+ n2 = len + n2;
+ } else if (n2 >= len) {
+ n2 = len - (exclusive ? 0 : 1);
+ }
+ if (exclusive) {
+ n2--;
+ }
+ if (n1 >= len || n2 < 0 || n1 > n2) {
+ tv_clear(rettv);
+ rettv->v_type = VAR_BLOB;
+ rettv->vval.v_blob = NULL;
+ } else {
+ blob_T *const new_blob = tv_blob_alloc();
+ ga_grow(&new_blob->bv_ga, (int)(n2 - n1 + 1));
+ new_blob->bv_ga.ga_len = (int)(n2 - n1 + 1);
+ for (int i = (int)n1; i <= (int)n2; i++) {
+ tv_blob_set(new_blob, i - (int)n1, tv_blob_get(rettv->vval.v_blob, i));
+ }
+ tv_clear(rettv);
+ tv_blob_set_ret(rettv, new_blob);
+ }
+
+ return OK;
+}
+
+/// Return the byte value in "blob" at index "idx" in "rettv". If the index is
+/// too big or negative that is an error. The length of the blob is "len".
+static int tv_blob_index(const blob_T *blob, int len, varnumber_T idx, typval_T *rettv)
+{
+ // The resulting variable is a byte value.
+ // If the index is too big or negative that is an error.
+ if (idx < 0) {
+ idx = len + idx;
+ }
+ if (idx < len && idx >= 0) {
+ const int v = (int)tv_blob_get(rettv->vval.v_blob, (int)idx);
+ tv_clear(rettv);
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = v;
+ } else {
+ semsg(_(e_blobidx), idx);
+ return FAIL;
+ }
+
+ return OK;
+}
+
+int tv_blob_slice_or_index(const blob_T *blob, int is_range, varnumber_T n1, varnumber_T n2,
+ bool exclusive, typval_T *rettv)
+{
+ int len = tv_blob_len(rettv->vval.v_blob);
+
+ if (is_range) {
+ return tv_blob_slice(blob, len, n1, n2, exclusive, rettv);
+ } else {
+ return tv_blob_index(blob, len, n1, rettv);
+ }
+}
+
+/// Check if "n1" is a valid index for a blob with length "bloblen".
+int tv_blob_check_index(int bloblen, varnumber_T n1, bool quiet)
+{
+ if (n1 < 0 || n1 > bloblen) {
+ if (!quiet) {
+ semsg(_(e_blobidx), n1);
+ }
+ return FAIL;
+ }
+ return OK;
+}
+
+/// Check if "n1"-"n2" is a valid range for a blob with length "bloblen".
+int tv_blob_check_range(int bloblen, varnumber_T n1, varnumber_T n2, bool quiet)
+{
+ if (n2 < 0 || n2 >= bloblen || n2 < n1) {
+ if (!quiet) {
+ semsg(_(e_blobidx), n2);
+ }
+ return FAIL;
+ }
+ return OK;
+}
+
+/// Set bytes "n1" to "n2" (inclusive) in "dest" to the value of "src".
+/// Caller must make sure "src" is a blob.
+/// Returns FAIL if the number of bytes does not match.
+int tv_blob_set_range(blob_T *dest, varnumber_T n1, varnumber_T n2, typval_T *src)
+{
+ if (n2 - n1 + 1 != tv_blob_len(src->vval.v_blob)) {
+ emsg(_("E972: Blob value does not have the right number of bytes"));
+ return FAIL;
+ }
+
+ for (int il = (int)n1, ir = 0; il <= (int)n2; il++) {
+ tv_blob_set(dest, il, tv_blob_get(src->vval.v_blob, ir++));
+ }
+ return OK;
+}
+
+/// Store one byte "byte" in blob "blob" at "idx".
+/// Append one byte if needed.
+void tv_blob_set_append(blob_T *blob, int idx, uint8_t byte)
+{
+ garray_T *gap = &blob->bv_ga;
+
+ // Allow for appending a byte. Setting a byte beyond
+ // the end is an error otherwise.
+ if (idx <= gap->ga_len) {
+ if (idx == gap->ga_len) {
+ ga_grow(gap, 1);
+ gap->ga_len++;
+ }
+ tv_blob_set(blob, idx, byte);
+ }
+}
+
/// "remove({blob})" function
void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
{
@@ -2715,7 +2989,7 @@ void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
}
bool error = false;
- long idx = tv_get_number_chk(&argvars[1], &error);
+ int64_t idx = tv_get_number_chk(&argvars[1], &error);
if (!error) {
const int len = tv_blob_len(b);
@@ -2725,7 +2999,7 @@ void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
idx = len + idx;
}
if (idx < 0 || idx >= len) {
- semsg(_(e_blobidx), (int64_t)idx);
+ semsg(_(e_blobidx), idx);
return;
}
if (argvars[2].v_type == VAR_UNKNOWN) {
@@ -2736,7 +3010,7 @@ void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
b->bv_ga.ga_len--;
} else {
// Remove range of items, return blob with values.
- long end = tv_get_number_chk(&argvars[2], &error);
+ int64_t end = tv_get_number_chk(&argvars[2], &error);
if (error) {
return;
}
@@ -2745,7 +3019,7 @@ void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
end = len + end;
}
if (end >= len || idx > end) {
- semsg(_(e_blobidx), (int64_t)end);
+ semsg(_(e_blobidx), end);
return;
}
blob_T *const blob = tv_blob_alloc();
@@ -2764,6 +3038,51 @@ void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
}
}
+/// blob2list() function
+void f_blob2list(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+
+ if (tv_check_for_blob_arg(argvars, 0) == FAIL) {
+ return;
+ }
+
+ blob_T *const blob = argvars->vval.v_blob;
+ list_T *const l = rettv->vval.v_list;
+ for (int i = 0; i < tv_blob_len(blob); i++) {
+ tv_list_append_number(l, tv_blob_get(blob, i));
+ }
+}
+
+/// list2blob() function
+void f_list2blob(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ tv_blob_alloc_ret(rettv);
+ blob_T *const blob = rettv->vval.v_blob;
+
+ if (tv_check_for_list_arg(argvars, 0) == FAIL) {
+ return;
+ }
+
+ list_T *const l = argvars->vval.v_list;
+ if (l == NULL) {
+ return;
+ }
+
+ TV_LIST_ITER_CONST(l, li, {
+ bool error = false;
+ varnumber_T n = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error);
+ if (error || n < 0 || n > 255) {
+ if (!error) {
+ semsg(_(e_invalid_value_for_blob_nr), (int)n);
+ }
+ ga_clear(&blob->bv_ga);
+ return;
+ }
+ ga_append(&blob->bv_ga, (uint8_t)n);
+ });
+}
+
//{{{1 Generic typval operations
//{{{2 Init/alloc/clear
//{{{3 Alloc
@@ -2774,7 +3093,7 @@ void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
///
/// @param[out] ret_tv Structure where list is saved.
/// @param[in] len Expected number of items to be populated before list
-/// becomes accessible from VimL. It is still valid to
+/// becomes accessible from Vimscript. It is still valid to
/// underpopulate a list, value only controls how many elements
/// will be allocated in advance. @see ListLenSpecials.
///
@@ -2820,19 +3139,20 @@ static void tv_dict_list(typval_T *const tv, typval_T *const rettv, const DictLi
emsg(_(e_dictreq));
return;
}
+
+ tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict));
if (tv->vval.v_dict == NULL) {
+ // NULL dict behaves like an empty dict
return;
}
- tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict));
-
TV_DICT_ITER(tv->vval.v_dict, di, {
typval_T tv_item = { .v_lock = VAR_UNLOCKED };
switch (what) {
case kDictListKeys:
tv_item.v_type = VAR_STRING;
- tv_item.vval.v_string = xstrdup((char *)di->di_key);
+ tv_item.vval.v_string = xstrdup(di->di_key);
break;
case kDictListValues:
tv_copy(&di->di_tv, &tv_item);
@@ -2847,7 +3167,7 @@ static void tv_dict_list(typval_T *const tv, typval_T *const rettv, const DictLi
tv_list_append_owned_tv(sub_l, (typval_T) {
.v_type = VAR_STRING,
.v_lock = VAR_UNLOCKED,
- .vval.v_string = xstrdup((const char *)di->di_key),
+ .vval.v_string = xstrdup(di->di_key),
});
tv_list_append_tv(sub_l, &di->di_tv);
@@ -2881,10 +3201,10 @@ void f_values(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "has_key()" function
void f_has_key(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- if (argvars[0].v_type != VAR_DICT) {
- emsg(_(e_dictreq));
+ if (tv_check_for_dict_arg(argvars, 0) == FAIL) {
return;
}
+
if (argvars[0].vval.v_dict == NULL) {
return;
}
@@ -2936,22 +3256,19 @@ void tv_blob_alloc_ret(typval_T *const ret_tv)
///
/// @param[in] from Blob object to copy from.
/// @param[out] to Blob object to copy to.
-void tv_blob_copy(typval_T *const from, typval_T *const to)
- FUNC_ATTR_NONNULL_ALL
+void tv_blob_copy(blob_T *const from, typval_T *const to)
+ FUNC_ATTR_NONNULL_ARG(2)
{
- assert(from->v_type == VAR_BLOB);
-
to->v_type = VAR_BLOB;
to->v_lock = VAR_UNLOCKED;
- if (from->vval.v_blob == NULL) {
+ if (from == NULL) {
to->vval.v_blob = NULL;
} else {
tv_blob_alloc_ret(to);
- int len = from->vval.v_blob->bv_ga.ga_len;
+ int len = from->bv_ga.ga_len;
if (len > 0) {
- to->vval.v_blob->bv_ga.ga_data
- = xmemdup(from->vval.v_blob->bv_ga.ga_data, (size_t)len);
+ to->vval.v_blob->bv_ga.ga_data = xmemdup(from->bv_ga.ga_data, (size_t)len);
}
to->vval.v_blob->bv_ga.ga_len = len;
to->vval.v_blob->bv_ga.ga_maxlen = len;
@@ -3019,7 +3336,7 @@ static inline int _nothing_conv_func_start(typval_T *const tv, char *const fun)
}
} else {
func_unref(fun);
- if ((const char *)fun != tv_empty_string) {
+ if (fun != tv_empty_string) {
xfree(fun);
}
tv->vval.v_string = NULL;
@@ -3208,55 +3525,59 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, dict_T **const dic
/// @param[in,out] tv Value to free.
void tv_clear(typval_T *const tv)
{
- if (tv != NULL && tv->v_type != VAR_UNKNOWN) {
- // WARNING: do not translate the string here, gettext is slow and function
- // is used *very* often. At the current state encode_vim_to_nothing() does
- // not error out and does not use the argument anywhere.
- //
- // If situation changes and this argument will be used, translate it in the
- // place where it is used.
- const int evn_ret = encode_vim_to_nothing(NULL, tv, "tv_clear() argument");
- (void)evn_ret;
- assert(evn_ret == OK);
+ if (tv == NULL || tv->v_type == VAR_UNKNOWN) {
+ return;
}
+
+ // WARNING: do not translate the string here, gettext is slow and function
+ // is used *very* often. At the current state encode_vim_to_nothing() does
+ // not error out and does not use the argument anywhere.
+ //
+ // If situation changes and this argument will be used, translate it in the
+ // place where it is used.
+ const int evn_ret = encode_vim_to_nothing(NULL, tv, "tv_clear() argument");
+ (void)evn_ret;
+ assert(evn_ret == OK);
}
//{{{3 Free
-/// Free allocated VimL object and value stored inside
+/// Free allocated Vimscript object and value stored inside
///
/// @param tv Object to free.
void tv_free(typval_T *tv)
{
- if (tv != NULL) {
- switch (tv->v_type) {
- case VAR_PARTIAL:
- partial_unref(tv->vval.v_partial);
- break;
- case VAR_FUNC:
- func_unref(tv->vval.v_string);
- FALLTHROUGH;
- case VAR_STRING:
- xfree(tv->vval.v_string);
- break;
- case VAR_BLOB:
- tv_blob_unref(tv->vval.v_blob);
- break;
- case VAR_LIST:
- tv_list_unref(tv->vval.v_list);
- break;
- case VAR_DICT:
- tv_dict_unref(tv->vval.v_dict);
- break;
- case VAR_BOOL:
- case VAR_SPECIAL:
- case VAR_NUMBER:
- case VAR_FLOAT:
- case VAR_UNKNOWN:
- break;
- }
- xfree(tv);
+ if (tv == NULL) {
+ return;
}
+
+ switch (tv->v_type) {
+ case VAR_PARTIAL:
+ partial_unref(tv->vval.v_partial);
+ break;
+ case VAR_FUNC:
+ func_unref(tv->vval.v_string);
+ FALLTHROUGH;
+ case VAR_STRING:
+ xfree(tv->vval.v_string);
+ break;
+ case VAR_BLOB:
+ tv_blob_unref(tv->vval.v_blob);
+ break;
+ case VAR_LIST:
+ tv_list_unref(tv->vval.v_list);
+ break;
+ case VAR_DICT:
+ tv_dict_unref(tv->vval.v_dict);
+ break;
+ case VAR_BOOL:
+ case VAR_SPECIAL:
+ case VAR_NUMBER:
+ case VAR_FLOAT:
+ case VAR_UNKNOWN:
+ break;
+ }
+ xfree(tv);
}
//{{{3 Copy
@@ -3331,7 +3652,7 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock, const boo
static int recurse = 0;
if (recurse >= DICT_MAXNEST) {
- emsg(_("E743: variable nested too deep for (un)lock"));
+ emsg(_(e_variable_nested_too_deep_for_unlock));
return;
}
if (deep == 0) {
@@ -3399,7 +3720,7 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock, const boo
recurse--;
}
-/// Check whether VimL value is locked itself or refers to a locked container
+/// Check whether Vimscript value is locked itself or refers to a locked container
///
/// @warning Fixed container is not the same as locked.
///
@@ -3498,7 +3819,7 @@ bool value_check_lock(VarLockStatus lock, const char *name, size_t name_len)
static int tv_equal_recurse_limit;
-/// Compare two VimL values
+/// Compare two Vimscript values
///
/// Like "==", but strings and numbers are different, as well as floats and
/// numbers.
@@ -3639,13 +3960,13 @@ bool tv_check_str_or_nr(const typval_T *const tv)
#define FUNC_ERROR "E703: Using a Funcref as a Number"
static const char *const num_errors[] = {
- [VAR_PARTIAL]= N_(FUNC_ERROR),
- [VAR_FUNC]= N_(FUNC_ERROR),
- [VAR_LIST]= N_("E745: Using a List as a Number"),
- [VAR_DICT]= N_("E728: Using a Dictionary as a Number"),
- [VAR_FLOAT]= N_("E805: Using a Float as a Number"),
- [VAR_BLOB]= N_("E974: Using a Blob as a Number"),
- [VAR_UNKNOWN]= N_("E685: using an invalid value as a Number"),
+ [VAR_PARTIAL] = N_(FUNC_ERROR),
+ [VAR_FUNC] = N_(FUNC_ERROR),
+ [VAR_LIST] = N_("E745: Using a List as a Number"),
+ [VAR_DICT] = N_("E728: Using a Dictionary as a Number"),
+ [VAR_FLOAT] = N_("E805: Using a Float as a Number"),
+ [VAR_BLOB] = N_("E974: Using a Blob as a Number"),
+ [VAR_UNKNOWN] = N_("E685: using an invalid value as a Number"),
};
#undef FUNC_ERROR
@@ -3681,21 +4002,20 @@ bool tv_check_num(const typval_T *const tv)
return false;
}
-#define FUNC_ERROR "E729: using Funcref as a String"
+#define FUNC_ERROR "E729: Using a Funcref as a String"
static const char *const str_errors[] = {
- [VAR_PARTIAL]= N_(FUNC_ERROR),
- [VAR_FUNC]= N_(FUNC_ERROR),
- [VAR_LIST]= N_("E730: using List as a String"),
- [VAR_DICT]= N_("E731: using Dictionary as a String"),
- [VAR_FLOAT]= ((const char *)e_float_as_string),
- [VAR_BLOB]= N_("E976: using Blob as a String"),
- [VAR_UNKNOWN]= N_("E908: using an invalid value as a String"),
+ [VAR_PARTIAL] = N_(FUNC_ERROR),
+ [VAR_FUNC] = N_(FUNC_ERROR),
+ [VAR_LIST] = N_("E730: Using a List as a String"),
+ [VAR_DICT] = N_("E731: Using a Dictionary as a String"),
+ [VAR_BLOB] = N_("E976: Using a Blob as a String"),
+ [VAR_UNKNOWN] = e_using_invalid_value_as_string,
};
#undef FUNC_ERROR
-/// Check that given value is a VimL String or can be "cast" to it.
+/// Check that given value is a Vimscript String or can be "cast" to it.
///
/// Error messages are compatible with tv_get_string_chk() previously used for
/// the same purpose.
@@ -3711,12 +4031,12 @@ bool tv_check_str(const typval_T *const tv)
case VAR_BOOL:
case VAR_SPECIAL:
case VAR_STRING:
+ case VAR_FLOAT:
return true;
case VAR_PARTIAL:
case VAR_FUNC:
case VAR_LIST:
case VAR_DICT:
- case VAR_FLOAT:
case VAR_BLOB:
case VAR_UNKNOWN:
emsg(_(str_errors[tv->v_type]));
@@ -3728,7 +4048,7 @@ bool tv_check_str(const typval_T *const tv)
//{{{2 Get
-/// Get the number value of a VimL object
+/// Get the number value of a Vimscript object
///
/// @note Use tv_get_number_chk() if you need to determine whether there was an
/// error.
@@ -3744,7 +4064,7 @@ varnumber_T tv_get_number(const typval_T *const tv)
return tv_get_number_chk(tv, &error);
}
-/// Get the number value of a VimL object
+/// Get the number value of a Vimscript object
///
/// @param[in] tv Object to get value from.
/// @param[out] ret_error If type error occurred then `true` will be written
@@ -3773,7 +4093,7 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error)
case VAR_STRING: {
varnumber_T n = 0;
if (tv->vval.v_string != NULL) {
- vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0, false);
+ vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0, false, NULL);
}
return n;
}
@@ -3791,7 +4111,19 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error)
return (ret_error == NULL ? -1 : 0);
}
-/// Get the line number from VimL object
+varnumber_T tv_get_bool(const typval_T *const tv)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return tv_get_number_chk(tv, NULL);
+}
+
+varnumber_T tv_get_bool_chk(const typval_T *const tv, bool *const ret_error)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1)
+{
+ return tv_get_number_chk(tv, ret_error);
+}
+
+/// Get the line number from Vimscript object
///
/// @param[in] tv Object to get value from. Is expected to be a number or
/// a special string like ".", "$", … (works with current buffer
@@ -3814,7 +4146,7 @@ linenr_T tv_get_lnum(const typval_T *const tv)
return lnum;
}
-/// Get the floating-point value of a VimL object
+/// Get the floating-point value of a Vimscript object
///
/// Raises an error if object is not number or floating-point.
///
@@ -3883,6 +4215,14 @@ int tv_check_for_nonempty_string_arg(const typval_T *const args, const int idx)
return OK;
}
+/// Check for an optional string argument at "idx"
+int tv_check_for_opt_string_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ return (args[idx].v_type == VAR_UNKNOWN
+ || tv_check_for_string_arg(args, idx) != FAIL) ? OK : FAIL;
+}
+
/// Give an error and return FAIL unless "args[idx]" is a number.
int tv_check_for_number_arg(const typval_T *const args, const int idx)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
@@ -3902,6 +4242,109 @@ int tv_check_for_opt_number_arg(const typval_T *const args, const int idx)
|| tv_check_for_number_arg(args, idx) != FAIL) ? OK : FAIL;
}
+/// Give an error and return FAIL unless "args[idx]" is a float or a number.
+int tv_check_for_float_or_nr_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ if (args[idx].v_type != VAR_FLOAT && args[idx].v_type != VAR_NUMBER) {
+ semsg(_(e_float_or_number_required_for_argument_nr), idx + 1);
+ return FAIL;
+ }
+ return OK;
+}
+
+/// Give an error and return FAIL unless "args[idx]" is a bool.
+int tv_check_for_bool_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ if (args[idx].v_type != VAR_BOOL
+ && !(args[idx].v_type == VAR_NUMBER
+ && (args[idx].vval.v_number == 0
+ || args[idx].vval.v_number == 1))) {
+ semsg(_(e_bool_required_for_argument_nr), idx + 1);
+ return FAIL;
+ }
+ return OK;
+}
+
+/// Check for an optional bool argument at "idx".
+/// Return FAIL if the type is wrong.
+int tv_check_for_opt_bool_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ if (args[idx].v_type == VAR_UNKNOWN) {
+ return OK;
+ }
+ return tv_check_for_bool_arg(args, idx);
+}
+
+/// Give an error and return FAIL unless "args[idx]" is a blob.
+int tv_check_for_blob_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ if (args[idx].v_type != VAR_BLOB) {
+ semsg(_(e_blob_required_for_argument_nr), idx + 1);
+ return FAIL;
+ }
+ return OK;
+}
+
+/// Give an error and return FAIL unless "args[idx]" is a list.
+int tv_check_for_list_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ if (args[idx].v_type != VAR_LIST) {
+ semsg(_(e_list_required_for_argument_nr), idx + 1);
+ return FAIL;
+ }
+ return OK;
+}
+
+/// Give an error and return FAIL unless "args[idx]" is a dict.
+int tv_check_for_dict_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ if (args[idx].v_type != VAR_DICT) {
+ semsg(_(e_dict_required_for_argument_nr), idx + 1);
+ return FAIL;
+ }
+ return OK;
+}
+
+/// Give an error and return FAIL unless "args[idx]" is a non-NULL dict.
+int tv_check_for_nonnull_dict_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ if (tv_check_for_dict_arg(args, idx) == FAIL) {
+ return FAIL;
+ }
+ if (args[idx].vval.v_dict == NULL) {
+ semsg(_(e_non_null_dict_required_for_argument_nr), idx + 1);
+ return FAIL;
+ }
+ return OK;
+}
+
+/// Check for an optional dict argument at "idx"
+int tv_check_for_opt_dict_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ return (args[idx].v_type == VAR_UNKNOWN
+ || tv_check_for_dict_arg(args, idx) != FAIL) ? OK : FAIL;
+}
+
+/// Give an error and return FAIL unless "args[idx]" is a string or
+/// a number.
+int tv_check_for_string_or_number_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ if (args[idx].v_type != VAR_STRING && args[idx].v_type != VAR_NUMBER) {
+ semsg(_(e_string_or_number_required_for_argument_nr), idx + 1);
+ return FAIL;
+ }
+ return OK;
+}
+
/// Give an error and return FAIL unless "args[idx]" is a string or a list.
int tv_check_for_string_or_list_arg(const typval_T *const args, const int idx)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
@@ -3913,7 +4356,53 @@ int tv_check_for_string_or_list_arg(const typval_T *const args, const int idx)
return OK;
}
-/// Get the string value of a "stringish" VimL object.
+/// Give an error and return FAIL unless "args[idx]" is a string, a list or a blob.
+int tv_check_for_string_or_list_or_blob_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ if (args[idx].v_type != VAR_STRING
+ && args[idx].v_type != VAR_LIST
+ && args[idx].v_type != VAR_BLOB) {
+ semsg(_(e_string_list_or_blob_required_for_argument_nr), idx + 1);
+ return FAIL;
+ }
+ return OK;
+}
+
+/// Check for an optional string or list argument at "idx"
+int tv_check_for_opt_string_or_list_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ return (args[idx].v_type == VAR_UNKNOWN
+ || tv_check_for_string_or_list_arg(args, idx) != FAIL) ? OK : FAIL;
+}
+
+/// Give an error and return FAIL unless "args[idx]" is a string
+/// or a function reference.
+int tv_check_for_string_or_func_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ if (args[idx].v_type != VAR_PARTIAL
+ && args[idx].v_type != VAR_FUNC
+ && args[idx].v_type != VAR_STRING) {
+ semsg(_(e_string_or_function_required_for_argument_nr), idx + 1);
+ return FAIL;
+ }
+ return OK;
+}
+
+/// Give an error and return FAIL unless "args[idx]" is a list or a blob.
+int tv_check_for_list_or_blob_arg(const typval_T *const args, const int idx)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ if (args[idx].v_type != VAR_LIST && args[idx].v_type != VAR_BLOB) {
+ semsg(_(e_list_or_blob_required_for_argument_nr), idx + 1);
+ return FAIL;
+ }
+ return OK;
+}
+
+/// Get the string value of a "stringish" Vimscript object.
///
/// @param[in] tv Object to get value of.
/// @param buf Buffer used to hold numbers and special variables converted to
@@ -3929,11 +4418,14 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf)
{
switch (tv->v_type) {
case VAR_NUMBER:
- snprintf(buf, NUMBUFLEN, "%" PRIdVARNUMBER, tv->vval.v_number); // -V576
+ snprintf(buf, NUMBUFLEN, "%" PRIdVARNUMBER, tv->vval.v_number);
+ return buf;
+ case VAR_FLOAT:
+ vim_snprintf(buf, NUMBUFLEN, "%g", tv->vval.v_float);
return buf;
case VAR_STRING:
if (tv->vval.v_string != NULL) {
- return (const char *)tv->vval.v_string;
+ return tv->vval.v_string;
}
return "";
case VAR_BOOL:
@@ -3946,7 +4438,6 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf)
case VAR_FUNC:
case VAR_LIST:
case VAR_DICT:
- case VAR_FLOAT:
case VAR_BLOB:
case VAR_UNKNOWN:
emsg(_(str_errors[tv->v_type]));
@@ -3956,7 +4447,7 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf)
return NULL;
}
-/// Get the string value of a "stringish" VimL object.
+/// Get the string value of a "stringish" Vimscript object.
///
/// @warning For number and special values it uses a single, static buffer. It
/// may be used only once, next call to tv_get_string may reuse it. Use
@@ -3975,7 +4466,7 @@ const char *tv_get_string_chk(const typval_T *const tv)
return tv_get_string_buf_chk(tv, mybuf);
}
-/// Get the string value of a "stringish" VimL object.
+/// Get the string value of a "stringish" Vimscript object.
///
/// @warning For number and special values it uses a single, static buffer. It
/// may be used only once, next call to tv_get_string may reuse it. Use
@@ -3997,7 +4488,7 @@ const char *tv_get_string(const typval_T *const tv)
return tv_get_string_buf((typval_T *)tv, mybuf);
}
-/// Get the string value of a "stringish" VimL object.
+/// Get the string value of a "stringish" Vimscript object.
///
/// @note tv_get_string_chk() and tv_get_string_buf_chk() are similar, but
/// return NULL on error.
@@ -4019,3 +4510,34 @@ const char *tv_get_string_buf(const typval_T *const tv, char *const buf)
return res != NULL ? res : "";
}
+
+/// Return true when "tv" is not falsy: non-zero, non-empty string, non-empty
+/// list, etc. Mostly like what JavaScript does, except that empty list and
+/// empty dictionary are false.
+bool tv2bool(const typval_T *const tv)
+{
+ switch (tv->v_type) {
+ case VAR_NUMBER:
+ return tv->vval.v_number != 0;
+ case VAR_FLOAT:
+ return tv->vval.v_float != 0.0;
+ case VAR_PARTIAL:
+ return tv->vval.v_partial != NULL;
+ case VAR_FUNC:
+ case VAR_STRING:
+ return tv->vval.v_string != NULL && *tv->vval.v_string != NUL;
+ case VAR_LIST:
+ return tv->vval.v_list != NULL && tv->vval.v_list->lv_len > 0;
+ case VAR_DICT:
+ return tv->vval.v_dict != NULL && tv->vval.v_dict->dv_hashtab.ht_used > 0;
+ case VAR_BOOL:
+ return tv->vval.v_bool == kBoolVarTrue;
+ case VAR_SPECIAL:
+ return tv->vval.v_special != kSpecialVarNull;
+ case VAR_BLOB:
+ return tv->vval.v_blob != NULL && tv->vval.v_blob->bv_ga.ga_len > 0;
+ case VAR_UNKNOWN:
+ break;
+ }
+ return false;
+}
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index 3f59cd3547..58f74a9796 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -1,5 +1,4 @@
-#ifndef NVIM_EVAL_TYPVAL_H
-#define NVIM_EVAL_TYPVAL_H
+#pragma once
#include <assert.h>
#include <stdbool.h>
@@ -7,84 +6,16 @@
#include <stdint.h>
#include <string.h>
-#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/typval_defs.h" // IWYU pragma: export
#include "nvim/func_attr.h"
-#include "nvim/garray.h"
+#include "nvim/garray_defs.h"
#include "nvim/gettext.h"
#include "nvim/hashtab.h"
#include "nvim/lib/queue.h"
-#include "nvim/macros.h"
+#include "nvim/macros_defs.h"
#include "nvim/mbyte_defs.h"
#include "nvim/message.h"
-#include "nvim/types.h"
-
-#ifdef LOG_LIST_ACTIONS
-# include "nvim/memory.h"
-
-extern ListLog *list_log_first; ///< First list log chunk, NULL if missing
-extern ListLog *list_log_last; ///< Last list log chunk
-
-static inline ListLog *list_log_alloc(const size_t size)
- REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT;
-
-/// Allocate a new log chunk and update globals
-///
-/// @param[in] size Number of entries in a new chunk.
-///
-/// @return [allocated] Newly allocated chunk.
-static inline ListLog *list_log_new(const size_t size)
-{
- ListLog *ret = xmalloc(offsetof(ListLog, entries)
- + size * sizeof(ret->entries[0]));
- ret->size = 0;
- ret->capacity = size;
- ret->next = NULL;
- if (list_log_first == NULL) {
- list_log_first = ret;
- } else {
- list_log_last->next = ret;
- }
- list_log_last = ret;
- return ret;
-}
-
-static inline void list_log(const list_T *const l, const listitem_T *const li1,
- const listitem_T *const li2, const char *const action)
- REAL_FATTR_ALWAYS_INLINE;
-
-/// Add new entry to log
-///
-/// If last chunk was filled it uses twice as much memory to allocate the next
-/// chunk.
-///
-/// @param[in] l List to which entry belongs.
-/// @param[in] li1 List item 1.
-/// @param[in] li2 List item 2, often used for integers and not list items.
-/// @param[in] action Logged action.
-static inline void list_log(const list_T *const l, const listitem_T *const li1,
- const listitem_T *const li2, const char *const action)
-{
- ListLog *tgt;
- if (list_log_first == NULL) {
- tgt = list_log_new(128);
- } else if (list_log_last->size == list_log_last->capacity) {
- tgt = list_log_new(list_log_last->capacity * 2);
- } else {
- tgt = list_log_last;
- }
- tgt->entries[tgt->size++] = (ListLogEntry) {
- .l = (uintptr_t)l,
- .li1 = (uintptr_t)li1,
- .li2 = (uintptr_t)li2,
- .len = (l == NULL ? 0 : l->lv_len),
- .action = action,
- };
-}
-#else
-# define list_log(...)
-# define list_write_log(...)
-# define list_free_log()
-#endif
+#include "nvim/types_defs.h"
// In a hashtab item "hi_key" points to "di_key" in a dictitem.
// This avoids adding a pointer to the hashtab item.
@@ -174,7 +105,6 @@ static inline int tv_list_len(const list_T *l)
/// @param[in] l List to check.
static inline int tv_list_len(const list_T *const l)
{
- list_log(l, NULL, NULL, "len");
if (l == NULL) {
return 0;
}
@@ -258,10 +188,8 @@ static inline listitem_T *tv_list_first(const list_T *l)
static inline listitem_T *tv_list_first(const list_T *const l)
{
if (l == NULL) {
- list_log(l, NULL, NULL, "first");
return NULL;
}
- list_log(l, l->lv_first, NULL, "first");
return l->lv_first;
}
@@ -276,10 +204,8 @@ static inline listitem_T *tv_list_last(const list_T *l)
static inline listitem_T *tv_list_last(const list_T *const l)
{
if (l == NULL) {
- list_log(l, NULL, NULL, "last");
return NULL;
}
- list_log(l, l->lv_last, NULL, "last");
return l->lv_last;
}
@@ -308,7 +234,7 @@ static inline long tv_dict_len(const dict_T *d)
static inline long tv_dict_len(const dict_T *const d)
{
if (d == NULL) {
- return 0L;
+ return 0;
}
return (long)d->dv_hashtab.ht_used;
}
@@ -372,7 +298,7 @@ static inline uint8_t tv_blob_get(const blob_T *const b, int idx)
return ((uint8_t *)b->bv_ga.ga_data)[idx];
}
-static inline void tv_blob_set(blob_T *b, int idx, uint8_t c)
+static inline void tv_blob_set(blob_T *blob, int idx, uint8_t c)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL;
/// Store the byte `c` at index `idx` in the blob.
@@ -380,12 +306,12 @@ static inline void tv_blob_set(blob_T *b, int idx, uint8_t c)
/// @param[in] b Blob to index. Cannot be NULL.
/// @param[in] idx Index in a blob. Must be valid.
/// @param[in] c Value to store.
-static inline void tv_blob_set(blob_T *const b, int idx, uint8_t c)
+static inline void tv_blob_set(blob_T *const blob, int idx, uint8_t c)
{
- ((uint8_t *)b->bv_ga.ga_data)[idx] = c;
+ ((uint8_t *)blob->bv_ga.ga_data)[idx] = c;
}
-/// Initialize VimL object
+/// Initialize Vimscript object
///
/// Initializes to unlocked VAR_UNKNOWN object.
///
@@ -413,10 +339,9 @@ extern bool tv_in_free_unref_items;
/// @param[in] l List to iterate over.
/// @param li Name of the variable with current listitem_T entry.
/// @param code Cycle body.
-#define _TV_LIST_ITER_MOD(modifier, l, li, code) \
+#define TV_LIST_ITER_MOD(modifier, l, li, code) \
do { \
modifier list_T *const l_ = (l); \
- list_log(l_, NULL, NULL, "iter" #modifier); \
if (l_ != NULL) { \
for (modifier listitem_T *li = l_->lv_first; \
li != NULL; li = li->li_next) { \
@@ -434,7 +359,7 @@ extern bool tv_in_free_unref_items;
/// @param li Name of the variable with current listitem_T entry.
/// @param code Cycle body.
#define TV_LIST_ITER(l, li, code) \
- _TV_LIST_ITER_MOD( , l, li, code) // NOLINT(whitespace/parens)
+ TV_LIST_ITER_MOD( , l, li, code) // NOLINT(whitespace/parens)
/// Iterate over a list
///
@@ -445,7 +370,7 @@ extern bool tv_in_free_unref_items;
/// @param li Name of the variable with current listitem_T entry.
/// @param code Cycle body.
#define TV_LIST_ITER_CONST(l, li, code) \
- _TV_LIST_ITER_MOD(const, l, li, code)
+ TV_LIST_ITER_MOD(const, l, li, code)
// Below macros are macros to avoid duplicating code for functionally identical
// const and non-const function variants.
@@ -498,7 +423,7 @@ static inline bool tv_get_float_chk(const typval_T *tv, float_T *ret_f)
///
/// Raises an error if object is not number or floating-point.
///
-/// @param[in] tv VimL object to get value from.
+/// @param[in] tv Vimscript object to get value from.
/// @param[out] ret_f Location where resulting float is stored.
///
/// @return true in case of success, false if tv is not a number or float.
@@ -518,13 +443,15 @@ static inline bool tv_get_float_chk(const typval_T *const tv, float_T *const ret
static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q)
REAL_FATTR_NONNULL_ALL REAL_FATTR_NONNULL_RET REAL_FATTR_PURE
- REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE;
+ REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE
+ FUNC_ATTR_NO_SANITIZE_ADDRESS;
/// Compute the `DictWatcher` address from a QUEUE node.
///
/// This only exists for .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer
/// arithmetic).
static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q)
+ FUNC_ATTR_NO_SANITIZE_ADDRESS
{
return QUEUE_DATA(q, DictWatcher, node);
}
@@ -557,16 +484,10 @@ static inline bool tv_is_func(const typval_T tv)
#ifdef UNIT_TESTING
// Do not use enum constants, see commit message.
-EXTERN const size_t kTVCstring INIT(= TV_CSTRING);
-EXTERN const size_t kTVTranslate INIT(= TV_TRANSLATE);
+EXTERN const size_t kTVCstring INIT( = TV_CSTRING);
+EXTERN const size_t kTVTranslate INIT( = TV_TRANSLATE);
#endif
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/typval.h.generated.h"
#endif
-
-#define tv_get_bool tv_get_number
-#define tv_get_bool_chk tv_get_number_chk
-#define tv_dict_get_bool tv_dict_get_number_def
-
-#endif // NVIM_EVAL_TYPVAL_H
diff --git a/src/nvim/eval/typval_defs.h b/src/nvim/eval/typval_defs.h
index 4615198441..c6bd11ccdb 100644
--- a/src/nvim/eval/typval_defs.h
+++ b/src/nvim/eval/typval_defs.h
@@ -1,16 +1,15 @@
-#ifndef NVIM_EVAL_TYPVAL_DEFS_H
-#define NVIM_EVAL_TYPVAL_DEFS_H
+#pragma once
#include <inttypes.h>
#include <limits.h>
-#include "nvim/garray.h"
-#include "nvim/hashtab.h"
+#include "nvim/garray_defs.h"
+#include "nvim/hashtab_defs.h"
#include "nvim/lib/queue.h"
-#include "nvim/pos.h"
-#include "nvim/types.h"
+#include "nvim/pos_defs.h"
+#include "nvim/types_defs.h"
-/// Type used for VimL VAR_NUMBER values
+/// Type used for Vimscript VAR_NUMBER values
typedef int64_t varnumber_T;
typedef uint64_t uvarnumber_T;
@@ -100,7 +99,7 @@ typedef enum {
VAR_FIXED = 2, ///< Locked forever.
} VarLockStatus;
-/// VimL variable types, for use in typval_T.v_type
+/// Vimscript variable types, for use in typval_T.v_type
typedef enum {
VAR_UNKNOWN = 0, ///< Unknown (unspecified) value.
VAR_NUMBER, ///< Number, .v_number is used.
@@ -115,6 +114,19 @@ typedef enum {
VAR_BLOB, ///< Blob, .v_blob is used.
} VarType;
+/// Type values for type().
+enum {
+ VAR_TYPE_NUMBER = 0,
+ VAR_TYPE_STRING = 1,
+ VAR_TYPE_FUNC = 2,
+ VAR_TYPE_LIST = 3,
+ VAR_TYPE_DICT = 4,
+ VAR_TYPE_FLOAT = 5,
+ VAR_TYPE_BOOL = 6,
+ VAR_TYPE_SPECIAL = 7,
+ VAR_TYPE_BLOB = 10,
+};
+
/// Structure that holds an internal variable value
typedef struct {
VarType v_type; ///< Variable type.
@@ -206,7 +218,7 @@ typedef struct {
struct { \
typval_T di_tv; /* Structure that holds scope dictionary itself. */ \
uint8_t di_flags; /* Flags. */ \
- char_u di_key[__VA_ARGS__]; /* Key value. */ /* NOLINT(runtime/arrays)*/ \
+ char di_key[__VA_ARGS__]; /* Key value. */ /* NOLINT(runtime/arrays)*/ \
}
/// Structure to hold a scope dictionary
@@ -273,6 +285,12 @@ typedef struct {
linenr_T sc_lnum; ///< line number
} sctx_T;
+/// Stores an identifier of a script or channel that last set an option.
+typedef struct {
+ sctx_T script_ctx; /// script context where the option was last set
+ uint64_t channel_id; /// Only used when script_id is SID_API_CLIENT.
+} LastSet;
+
/// Maximum number of function arguments
enum { MAX_FUNC_ARGS = 20, };
/// Short variable name length
@@ -284,27 +302,27 @@ enum { FIXVAR_CNT = 12, };
typedef struct funccall_S funccall_T;
struct funccall_S {
- 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; or next funccal in
- ///< list pointed to by previous_funccal.
- 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".
+ ufunc_T *fc_func; ///< Function being called.
+ int fc_linenr; ///< Next line to be executed.
+ int fc_returned; ///< ":return" used.
+ TV_DICTITEM_STRUCT(VAR_SHORT_LEN + 1) fc_fixvar[FIXVAR_CNT]; ///< Fixed variables for arguments.
+ dict_T fc_l_vars; ///< l: local function variables.
+ ScopeDictDictItem fc_l_vars_var; ///< Variable for l: scope.
+ dict_T fc_l_avars; ///< a: argument variables.
+ ScopeDictDictItem fc_l_avars_var; ///< Variable for a: scope.
+ list_T fc_l_varlist; ///< List for a:000.
+ listitem_T fc_l_listitems[MAX_FUNC_ARGS]; ///< List items for a:000.
+ typval_T *fc_rettv; ///< Return value.
+ linenr_T fc_breakpoint; ///< Next line with breakpoint or zero.
+ int fc_dbg_tick; ///< "debug_tick" when breakpoint was set.
+ int fc_level; ///< Top nesting level of executed function.
+ garray_T fc_defer; ///< Functions to be called on return.
+ proftime_T fc_prof_child; ///< Time spent in a child.
+ funccall_T *fc_caller; ///< Calling function or NULL; or next funccal in
+ ///< list pointed to by previous_funccal.
+ int fc_refcount; ///< Number of user functions that reference this funccall.
+ int fc_copyID; ///< CopyID used for garbage collection.
+ garray_T fc_ufuncs; ///< List of ufunc_T* which keep a reference to "fc_func".
};
/// Structure to hold info for a user function.
@@ -366,34 +384,3 @@ typedef struct list_stack_S {
list_T *list;
struct list_stack_S *prev;
} list_stack_T;
-
-/// Structure representing one list item, used for sort array.
-typedef struct {
- listitem_T *item; ///< Sorted list item.
- int idx; ///< Sorted list item index.
-} ListSortItem;
-
-typedef int (*ListSorter)(const void *, const void *);
-
-#ifdef LOG_LIST_ACTIONS
-/// List actions log entry
-typedef struct {
- uintptr_t l; ///< List log entry belongs to.
- uintptr_t li1; ///< First list item log entry belongs to, if applicable.
- uintptr_t li2; ///< Second list item log entry belongs to, if applicable.
- int len; ///< List length when log entry was created.
- const char *action; ///< Logged action.
-} ListLogEntry;
-
-typedef struct list_log ListLog;
-
-/// List actions log
-struct list_log {
- ListLog *next; ///< Next chunk or NULL.
- size_t capacity; ///< Number of entries in current chunk.
- size_t size; ///< Current chunk size.
- ListLogEntry entries[]; ///< Actual log entries.
-};
-#endif
-
-#endif // NVIM_EVAL_TYPVAL_DEFS_H
diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h
index 6d29286a58..2e0b68d486 100644
--- a/src/nvim/eval/typval_encode.c.h
+++ b/src/nvim/eval/typval_encode.c.h
@@ -252,15 +252,13 @@
#include "nvim/func_attr.h"
#include "klib/kvec.h"
-// -V::1063
-
/// Dummy variable used because some macros need lvalue
///
/// Must not be written to, if needed one must check that address of the
/// macros argument is (not) equal to `&TYPVAL_ENCODE_NODICT_VAR`.
const dict_T *const TYPVAL_ENCODE_NODICT_VAR = NULL;
-static inline int _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
+static inline int TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
void *const val, int *const val_copyID,
const MPConvStack *const mpstack, const int copyID,
@@ -283,7 +281,7 @@ static inline int _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
/// @param[in] objname Object name, used for error reporting.
///
/// @return NOTDONE in case of success, what to return in case of failure.
-static inline int _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
+static inline int TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME, void *const val, int *const val_copyID,
const MPConvStack *const mpstack, const int copyID, const MPConvStackValType conv_type,
const char *const objname)
@@ -296,7 +294,7 @@ static inline int _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
return NOTDONE;
}
-static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
+static int TYPVAL_ENCODE_CONVERT_ONE_VALUE(
TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
MPConvStack *const mpstack, MPConvStackVal *const cur_mpsv,
typval_T *const tv, const int copyID,
@@ -320,7 +318,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
/// @param[in] objname Object name, used for error reporting.
///
/// @return OK in case of success, FAIL in case of failure.
-static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
+static int TYPVAL_ENCODE_CONVERT_ONE_VALUE(
TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME, MPConvStack *const mpstack,
MPConvStackVal *const cur_mpsv, typval_T *const tv, const int copyID, const char *const objname)
{
@@ -347,8 +345,8 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
case VAR_PARTIAL: {
partial_T *const pt = tv->vval.v_partial;
(void)pt;
- TYPVAL_ENCODE_CONV_FUNC_START(tv, (pt == NULL ? NULL : partial_name(pt))); // -V547
- _mp_push(*mpstack, ((MPConvStackVal) { // -V779
+ TYPVAL_ENCODE_CONV_FUNC_START(tv, (pt == NULL ? NULL : partial_name(pt)));
+ kvi_push(*mpstack, ((MPConvStackVal) {
.type = kMPConvPartial,
.tv = tv,
.saved_copyID = copyID - 1,
@@ -367,11 +365,11 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
break;
}
const int saved_copyID = tv_list_copyid(tv->vval.v_list);
- _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID,
- kMPConvList);
+ TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID,
+ kMPConvList);
TYPVAL_ENCODE_CONV_LIST_START(tv, tv_list_len(tv->vval.v_list));
assert(saved_copyID != copyID);
- _mp_push(*mpstack, ((MPConvStackVal) {
+ kvi_push(*mpstack, ((MPConvStackVal) {
.type = kMPConvList,
.tv = tv,
.saved_copyID = saved_copyID,
@@ -382,7 +380,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
},
},
}));
- TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, _mp_last(*mpstack));
+ TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, kv_last(*mpstack));
break;
}
case VAR_BOOL:
@@ -396,7 +394,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
case VAR_SPECIAL:
switch (tv->vval.v_special) {
case kSpecialVarNull:
- TYPVAL_ENCODE_CONV_NIL(tv); // -V1037
+ TYPVAL_ENCODE_CONV_NIL(tv);
break;
}
break;
@@ -509,7 +507,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
}
if (is_string) {
TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len);
- } else { // -V523
+ } else {
TYPVAL_ENCODE_CONV_STRING(tv, buf, len);
}
xfree(buf);
@@ -520,12 +518,12 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
goto _convert_one_value_regular_dict;
}
const int saved_copyID = tv_list_copyid(val_di->di_tv.vval.v_list);
- _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list,
- lv_copyID, copyID,
- kMPConvList);
+ TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list,
+ lv_copyID, copyID,
+ kMPConvList);
TYPVAL_ENCODE_CONV_LIST_START(tv, tv_list_len(val_di->di_tv.vval.v_list));
assert(saved_copyID != copyID && saved_copyID != copyID - 1);
- _mp_push(*mpstack, ((MPConvStackVal) {
+ kvi_push(*mpstack, ((MPConvStackVal) {
.tv = tv,
.type = kMPConvList,
.saved_copyID = saved_copyID,
@@ -544,8 +542,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
}
list_T *const val_list = val_di->di_tv.vval.v_list;
if (val_list == NULL || tv_list_len(val_list) == 0) {
- TYPVAL_ENCODE_CONV_EMPTY_DICT( // -V501
- tv, TYPVAL_ENCODE_NODICT_VAR);
+ TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, TYPVAL_ENCODE_NODICT_VAR);
break;
}
TV_LIST_ITER_CONST(val_list, li, {
@@ -555,12 +552,12 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
}
});
const int saved_copyID = tv_list_copyid(val_di->di_tv.vval.v_list);
- _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID,
- kMPConvPairs);
+ TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID,
+ kMPConvPairs);
TYPVAL_ENCODE_CONV_DICT_START(tv, TYPVAL_ENCODE_NODICT_VAR,
tv_list_len(val_list));
assert(saved_copyID != copyID && saved_copyID != copyID - 1);
- _mp_push(*mpstack, ((MPConvStackVal) {
+ kvi_push(*mpstack, ((MPConvStackVal) {
.tv = tv,
.type = kMPConvPairs,
.saved_copyID = saved_copyID,
@@ -603,12 +600,12 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
}
_convert_one_value_regular_dict: {}
const int saved_copyID = tv->vval.v_dict->dv_copyID;
- _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, copyID,
- kMPConvDict);
+ TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, copyID,
+ kMPConvDict);
TYPVAL_ENCODE_CONV_DICT_START(tv, tv->vval.v_dict,
tv->vval.v_dict->dv_hashtab.ht_used);
assert(saved_copyID != copyID);
- _mp_push(*mpstack, ((MPConvStackVal) {
+ kvi_push(*mpstack, ((MPConvStackVal) {
.tv = tv,
.type = kMPConvDict,
.saved_copyID = saved_copyID,
@@ -622,20 +619,20 @@ _convert_one_value_regular_dict: {}
},
}));
TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, tv->vval.v_dict,
- _mp_last(*mpstack));
+ kv_last(*mpstack));
break;
}
case VAR_UNKNOWN:
- internal_error(STR(_TYPVAL_ENCODE_CONVERT_ONE_VALUE) "()");
+ internal_error(STR(TYPVAL_ENCODE_CONVERT_ONE_VALUE) "()");
return FAIL;
}
typval_encode_stop_converting_one_item:
return OK;
// Prevent “unused label” warnings.
- goto typval_encode_stop_converting_one_item; // -V779
+ goto typval_encode_stop_converting_one_item;
}
-TYPVAL_ENCODE_SCOPE int _TYPVAL_ENCODE_ENCODE(
+TYPVAL_ENCODE_SCOPE int TYPVAL_ENCODE_ENCODE(
TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
typval_T *const tv, const char *const objname)
REAL_FATTR_NONNULL_ARG(2, 3) REAL_FATTR_WARN_UNUSED_RESULT;
@@ -649,29 +646,29 @@ TYPVAL_ENCODE_SCOPE int _TYPVAL_ENCODE_ENCODE(
/// @param[in] objname Object name, used for error reporting.
///
/// @return OK in case of success, FAIL in case of failure.
-TYPVAL_ENCODE_SCOPE int _TYPVAL_ENCODE_ENCODE(
+TYPVAL_ENCODE_SCOPE int TYPVAL_ENCODE_ENCODE(
TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME, typval_T *const top_tv,
const char *const objname)
{
const int copyID = get_copyID();
MPConvStack mpstack;
- _mp_init(mpstack);
- if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack,
- NULL,
- top_tv, copyID, objname)
+ kvi_init(mpstack);
+ if (TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack,
+ NULL,
+ top_tv, copyID, objname)
== FAIL) {
goto encode_vim_to__error_ret;
}
/// Label common for this and convert_one_value functions, used for escaping
/// from macros like TYPVAL_ENCODE_CONV_DICT_START.
typval_encode_stop_converting_one_item:
- while (_mp_size(mpstack)) {
- MPConvStackVal *cur_mpsv = &_mp_last(mpstack);
+ while (kv_size(mpstack)) {
+ MPConvStackVal *cur_mpsv = &kv_last(mpstack);
typval_T *tv = NULL;
switch (cur_mpsv->type) {
case kMPConvDict: {
if (!cur_mpsv->data.d.todo) {
- (void)_mp_pop(mpstack);
+ (void)kv_pop(mpstack);
cur_mpsv->data.d.dict->dv_copyID = cur_mpsv->saved_copyID;
TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, *cur_mpsv->data.d.dictp);
continue;
@@ -695,7 +692,7 @@ typval_encode_stop_converting_one_item:
}
case kMPConvList:
if (cur_mpsv->data.l.li == NULL) {
- (void)_mp_pop(mpstack);
+ (void)kv_pop(mpstack);
tv_list_set_copyid(cur_mpsv->data.l.list, cur_mpsv->saved_copyID);
TYPVAL_ENCODE_CONV_LIST_END(cur_mpsv->tv);
continue;
@@ -709,7 +706,7 @@ typval_encode_stop_converting_one_item:
break;
case kMPConvPairs: {
if (cur_mpsv->data.l.li == NULL) {
- (void)_mp_pop(mpstack);
+ (void)kv_pop(mpstack);
tv_list_set_copyid(cur_mpsv->data.l.list, cur_mpsv->saved_copyID);
TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR);
continue;
@@ -722,8 +719,8 @@ typval_encode_stop_converting_one_item:
TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(encode_vim_to__error_ret,
*TV_LIST_ITEM_TV(tv_list_first(kv_pair)));
if (
- _TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack, cur_mpsv,
- TV_LIST_ITEM_TV(tv_list_first(kv_pair)), copyID, objname)
+ TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack, cur_mpsv,
+ TV_LIST_ITEM_TV(tv_list_first(kv_pair)), copyID, objname)
== FAIL) {
goto encode_vim_to__error_ret;
}
@@ -745,7 +742,7 @@ typval_encode_stop_converting_one_item:
cur_mpsv->data.p.stage = kMPConvPartialSelf;
if (pt != NULL && pt->pt_argc > 0) {
TYPVAL_ENCODE_CONV_LIST_START(NULL, pt->pt_argc);
- _mp_push(mpstack, ((MPConvStackVal) {
+ kvi_push(mpstack, ((MPConvStackVal) {
.type = kMPConvPartialList,
.tv = NULL,
.saved_copyID = copyID - 1,
@@ -769,10 +766,10 @@ typval_encode_stop_converting_one_item:
continue;
}
const int saved_copyID = dict->dv_copyID;
- const int te_csr_ret = _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(TYPVAL_ENCODE_FIRST_ARG_NAME,
- dict, &dict->dv_copyID,
- &mpstack, copyID, kMPConvDict,
- objname);
+ const int te_csr_ret = TYPVAL_ENCODE_CHECK_SELF_REFERENCE(TYPVAL_ENCODE_FIRST_ARG_NAME,
+ dict, &dict->dv_copyID,
+ &mpstack, copyID, kMPConvDict,
+ objname);
if (te_csr_ret != NOTDONE) {
if (te_csr_ret == FAIL) {
goto encode_vim_to__error_ret;
@@ -783,7 +780,7 @@ typval_encode_stop_converting_one_item:
TYPVAL_ENCODE_CONV_DICT_START(NULL, pt->pt_dict,
dict->dv_hashtab.ht_used);
assert(saved_copyID != copyID && saved_copyID != copyID - 1);
- _mp_push(mpstack, ((MPConvStackVal) {
+ kvi_push(mpstack, ((MPConvStackVal) {
.type = kMPConvDict,
.tv = NULL,
.saved_copyID = saved_copyID,
@@ -797,7 +794,7 @@ typval_encode_stop_converting_one_item:
},
}));
TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(NULL, pt->pt_dict,
- _mp_last(mpstack));
+ kv_last(mpstack));
} else {
TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, -1);
}
@@ -805,14 +802,14 @@ typval_encode_stop_converting_one_item:
}
case kMPConvPartialEnd:
TYPVAL_ENCODE_CONV_FUNC_END(tv);
- (void)_mp_pop(mpstack);
+ (void)kv_pop(mpstack);
break;
}
continue;
}
case kMPConvPartialList:
if (!cur_mpsv->data.a.todo) {
- (void)_mp_pop(mpstack);
+ (void)kv_pop(mpstack);
TYPVAL_ENCODE_CONV_LIST_END(NULL);
continue;
} else if (cur_mpsv->data.a.argv != cur_mpsv->data.a.arg) {
@@ -823,17 +820,17 @@ typval_encode_stop_converting_one_item:
break;
}
assert(tv != NULL);
- if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack,
- cur_mpsv, tv, copyID, objname)
+ if (TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack,
+ cur_mpsv, tv, copyID, objname)
== FAIL) {
goto encode_vim_to__error_ret;
}
}
- _mp_destroy(mpstack);
+ kvi_destroy(mpstack);
return OK;
encode_vim_to__error_ret:
- _mp_destroy(mpstack);
+ kvi_destroy(mpstack);
return FAIL;
// Prevent “unused label” warnings.
- goto typval_encode_stop_converting_one_item; // -V779
+ goto typval_encode_stop_converting_one_item;
}
diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h
index 2f19144da3..a6e0bd4b2b 100644
--- a/src/nvim/eval/typval_encode.h
+++ b/src/nvim/eval/typval_encode.h
@@ -2,8 +2,7 @@
///
/// Contains common definitions for eval/typval_encode.c.h. Most of time should
/// not be included directly.
-#ifndef NVIM_EVAL_TYPVAL_ENCODE_H
-#define NVIM_EVAL_TYPVAL_ENCODE_H
+#pragma once
#include <assert.h>
#include <inttypes.h>
@@ -11,7 +10,7 @@
#include <string.h>
#include "klib/kvec.h"
-#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/func_attr.h"
/// Type of the stack entry
@@ -30,7 +29,7 @@ typedef enum {
kMPConvPartialEnd, ///< Already converted everything.
} MPConvPartialStage;
-/// Structure representing current VimL to messagepack conversion state
+/// Structure representing current Vimscript to messagepack conversion state
typedef struct {
MPConvStackValType type; ///< Type of the stack entry.
typval_T *tv; ///< Currently converted typval_T.
@@ -60,17 +59,9 @@ typedef struct {
} data; ///< Data to convert.
} MPConvStackVal;
-/// Stack used to convert VimL values to messagepack.
+/// Stack used to convert Vimscript values to messagepack.
typedef kvec_withinit_t(MPConvStackVal, 8) MPConvStack;
-// Defines for MPConvStack
-#define _mp_size kv_size
-#define _mp_init kvi_init
-#define _mp_destroy kvi_destroy
-#define _mp_push kvi_push
-#define _mp_pop kv_pop
-#define _mp_last kv_last
-
static inline size_t tv_strlen(const typval_T *tv)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT
REAL_FATTR_NONNULL_ALL;
@@ -96,21 +87,21 @@ static inline size_t tv_strlen(const typval_T *const tv)
/// copyID (variable) it is set to copyID.
/// @param[in] copyID CopyID used by the caller.
/// @param conv_type Type of the conversion, @see MPConvStackValType.
-#define _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val, copyID_attr, copyID, \
- conv_type) \
+#define TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val, copyID_attr, copyID, \
+ conv_type) \
do { \
- const int te_csr_ret = _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(TYPVAL_ENCODE_FIRST_ARG_NAME, \
- (val), &(val)->copyID_attr, mpstack, \
- copyID, conv_type, objname); \
+ const int te_csr_ret = TYPVAL_ENCODE_CHECK_SELF_REFERENCE(TYPVAL_ENCODE_FIRST_ARG_NAME, \
+ (val), &(val)->copyID_attr, mpstack, \
+ copyID, conv_type, objname); \
if (te_csr_ret != NOTDONE) { \
return te_csr_ret; \
} \
} while (0)
-#define _TYPVAL_ENCODE_FUNC_NAME_INNER_2(pref, name, suf) \
+#define TYPVAL_ENCODE_FUNC_NAME_INNER_2(pref, name, suf) \
pref##name##suf
-#define _TYPVAL_ENCODE_FUNC_NAME_INNER(pref, name, suf) \
- _TYPVAL_ENCODE_FUNC_NAME_INNER_2(pref, name, suf)
+#define TYPVAL_ENCODE_FUNC_NAME_INNER(pref, name, suf) \
+ TYPVAL_ENCODE_FUNC_NAME_INNER_2(pref, name, suf)
/// Construct function name, possibly using macros
///
@@ -122,23 +113,21 @@ static inline size_t tv_strlen(const typval_T *const tv)
/// @param[in] suf Suffix.
///
/// @return Concat: pref + #TYPVAL_ENCODE_NAME + suf.
-#define _TYPVAL_ENCODE_FUNC_NAME(pref, suf) \
- _TYPVAL_ENCODE_FUNC_NAME_INNER(pref, TYPVAL_ENCODE_NAME, suf)
+#define TYPVAL_ENCODE_FUNC_NAME(pref, suf) \
+ TYPVAL_ENCODE_FUNC_NAME_INNER(pref, TYPVAL_ENCODE_NAME, suf)
/// Self reference checker function name
-#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE \
- _TYPVAL_ENCODE_FUNC_NAME(_typval_encode_, _check_self_reference)
+#define TYPVAL_ENCODE_CHECK_SELF_REFERENCE \
+ TYPVAL_ENCODE_FUNC_NAME(_typval_encode_, _check_self_reference)
/// Entry point function name
-#define _TYPVAL_ENCODE_ENCODE \
- _TYPVAL_ENCODE_FUNC_NAME(encode_vim_to_, )
+#define TYPVAL_ENCODE_ENCODE \
+ TYPVAL_ENCODE_FUNC_NAME(encode_vim_to_, )
/// Name of the …convert_one_value function
-#define _TYPVAL_ENCODE_CONVERT_ONE_VALUE \
- _TYPVAL_ENCODE_FUNC_NAME(_typval_encode_, _convert_one_value)
+#define TYPVAL_ENCODE_CONVERT_ONE_VALUE \
+ TYPVAL_ENCODE_FUNC_NAME(_typval_encode_, _convert_one_value)
/// Name of the dummy const dict_T *const variable
#define TYPVAL_ENCODE_NODICT_VAR \
- _TYPVAL_ENCODE_FUNC_NAME(_typval_encode_, _nodict_var)
-
-#endif // NVIM_EVAL_TYPVAL_ENCODE_H
+ TYPVAL_ENCODE_FUNC_NAME(_typval_encode_, _nodict_var)
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 22c5b1954d..23b3c4e1b2 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -1,20 +1,17 @@
-// 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
-
// User defined function support
#include <assert.h>
#include <ctype.h>
#include <inttypes.h>
+#include <lauxlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "lauxlib.h"
-#include "nvim/ascii.h"
+#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
-#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/debugger.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
@@ -26,18 +23,20 @@
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
+#include "nvim/func_attr.h"
+#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
+#include "nvim/hashtab.h"
#include "nvim/insexpand.h"
#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
-#include "nvim/macros.h"
+#include "nvim/macros_defs.h"
#include "nvim/mbyte.h"
-#include "nvim/memline_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/option_defs.h"
+#include "nvim/option_vars.h"
#include "nvim/os/input.h"
#include "nvim/path.h"
#include "nvim/profile.h"
@@ -45,14 +44,21 @@
#include "nvim/runtime.h"
#include "nvim/search.h"
#include "nvim/strings.h"
-#include "nvim/types.h"
+#include "nvim/types_defs.h"
#include "nvim/ui.h"
-#include "nvim/vim.h"
+#include "nvim/vim_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/userfunc.c.generated.h"
#endif
+/// structure used as item in "fc_defer"
+typedef struct {
+ char *dr_name; ///< function name, allocated
+ typval_T dr_argvars[MAX_FUNC_ARGS + 1];
+ int dr_argcount;
+} defer_T;
+
static hashtab_T func_hashtab;
// Used by get_func_tv()
@@ -65,12 +71,21 @@ static funccall_T *current_funccal = NULL;
// item in it is still being used.
static funccall_T *previous_funccal = NULL;
-static char *e_funcexts = N_("E122: Function %s already exists, add ! to replace it");
-static char *e_funcdict = N_("E717: Dictionary entry already exists");
-static char *e_funcref = N_("E718: Funcref required");
-static char *e_nofunc = N_("E130: Unknown function: %s");
-static char e_no_white_space_allowed_before_str_str[]
+static const char *e_unknown_function_str = N_("E117: Unknown function: %s");
+static const char *e_funcexts = N_("E122: Function %s already exists, add ! to replace it");
+static const char *e_funcdict = N_("E717: Dictionary entry already exists");
+static const char *e_funcref = N_("E718: Funcref required");
+static const char *e_nofunc = N_("E130: Unknown function: %s");
+static const char e_function_list_was_modified[]
+ = N_("E454: Function list was modified");
+static const char e_function_nesting_too_deep[]
+ = N_("E1058: Function nesting too deep");
+static const char e_no_white_space_allowed_before_str_str[]
= N_("E1068: No white space allowed before '%s': %s");
+static const char e_missing_heredoc_end_marker_str[]
+ = N_("E1145: Missing heredoc end marker: %s");
+static const char e_cannot_use_partial_with_dictionary_for_defer[]
+ = N_("E1300: Cannot use a partial with dictionary for :defer");
void func_init(void)
{
@@ -90,7 +105,7 @@ static int get_function_args(char **argp, char endchar, garray_T *newargs, int *
bool mustend = false;
char *arg = *argp;
char *p = arg;
- char_u c;
+ uint8_t c;
int i;
if (newargs != NULL) {
@@ -128,7 +143,7 @@ static int get_function_args(char **argp, char endchar, garray_T *newargs, int *
}
if (newargs != NULL) {
ga_grow(newargs, 1);
- c = (char_u)(*p);
+ c = (uint8_t)(*p);
*p = NUL;
arg = xstrdup(arg);
@@ -152,14 +167,14 @@ static int get_function_args(char **argp, char endchar, garray_T *newargs, int *
p = skipwhite(p) + 1;
p = skipwhite(p);
char *expr = p;
- if (eval1(&p, &rettv, false) != FAIL) {
+ if (eval1(&p, &rettv, NULL) != FAIL) {
ga_grow(default_args, 1);
// trim trailing whitespace
while (p > expr && ascii_iswhite(p[-1])) {
p--;
}
- c = (char_u)(*p);
+ c = (uint8_t)(*p);
*p = NUL;
expr = xstrdup(expr);
((char **)(default_args->ga_data))[default_args->ga_len] = expr;
@@ -223,9 +238,9 @@ static void register_closure(ufunc_T *fp)
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;
+ ga_grow(&current_funccal->fc_ufuncs, 1);
+ ((ufunc_T **)current_funccal->fc_ufuncs.ga_data)
+ [current_funccal->fc_ufuncs.ga_len++] = fp;
}
/// @return a name for a lambda. Returned in static memory.
@@ -252,22 +267,22 @@ static void set_ufunc_name(ufunc_T *fp, char *name)
/// Parse a lambda expression and get a Funcref from "*arg".
///
/// @return OK or FAIL. Returns NOTDONE for dict or {expr}.
-int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
+int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg)
{
+ const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
garray_T newargs = GA_EMPTY_INIT_VALUE;
garray_T *pnewargs;
ufunc_T *fp = NULL;
partial_T *pt = NULL;
int varargs;
- int ret;
- char *start = skipwhite(*arg + 1);
- char *s, *e;
bool *old_eval_lavars = eval_lavars_used;
bool eval_lavars = false;
+ char *tofree = NULL;
// First, check if this is a lambda expression. "->" must exists.
- ret = get_function_args(&start, '-', NULL, NULL, NULL, true);
- if (ret == FAIL || *start != '>') {
+ char *s = skipwhite(*arg + 1);
+ int ret = get_function_args(&s, '-', NULL, NULL, NULL, true);
+ if (ret == FAIL || *s != '>') {
return NOTDONE;
}
@@ -290,12 +305,18 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
// Get the start and the end of the expression.
*arg = skipwhite((*arg) + 1);
- s = *arg;
- ret = skip_expr(arg);
+ char *start = *arg;
+ ret = skip_expr(arg, evalarg);
+ char *end = *arg;
if (ret == FAIL) {
goto errret;
}
- e = *arg;
+ if (evalarg != NULL) {
+ // avoid that the expression gets freed when another line break follows
+ tofree = evalarg->eval_tofree;
+ evalarg->eval_tofree = NULL;
+ }
+
*arg = skipwhite(*arg);
if (**arg != '}') {
semsg(_("E451: Expected }: %s"), *arg);
@@ -317,11 +338,11 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
ga_grow(&newlines, 1);
// Add "return " before the expression.
- size_t len = (size_t)(7 + e - s + 1);
+ size_t len = (size_t)(7 + end - start + 1);
p = xmalloc(len);
((char **)(newlines.ga_data))[newlines.ga_len++] = p;
STRCPY(p, "return ");
- xstrlcpy(p + 7, s, (size_t)(e - s) + 1);
+ xstrlcpy(p + 7, start, (size_t)(end - start) + 1);
if (strstr(p + 7, "a:") == NULL) {
// No a: variables are used for sure.
flags |= FC_NOARGS;
@@ -359,12 +380,22 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
}
eval_lavars_used = old_eval_lavars;
+ if (evalarg != NULL && evalarg->eval_tofree == NULL) {
+ evalarg->eval_tofree = tofree;
+ } else {
+ xfree(tofree);
+ }
return OK;
errret:
ga_clear_strings(&newargs);
xfree(fp);
xfree(pt);
+ if (evalarg != NULL && evalarg->eval_tofree == NULL) {
+ evalarg->eval_tofree = tofree;
+ } else {
+ xfree(tofree);
+ }
eval_lavars_used = old_eval_lavars;
return FAIL;
}
@@ -382,9 +413,11 @@ errret:
/// is not needed.
/// @param[in] no_autoload If true, do not source autoload scripts if function
/// was not found.
+/// @param[out] found_var If not NULL and a variable was found set it to true.
///
/// @return name of the function.
-char *deref_func_name(const char *name, int *lenp, partial_T **const partialp, bool no_autoload)
+char *deref_func_name(const char *name, int *lenp, partial_T **const partialp, bool no_autoload,
+ bool *found_var)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
if (partialp != NULL) {
@@ -392,18 +425,25 @@ char *deref_func_name(const char *name, int *lenp, partial_T **const partialp, b
}
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) { // just in case
+ if (v == NULL) {
+ return (char *)name;
+ }
+ typval_T *const tv = &v->di_tv;
+ if (found_var != NULL) {
+ *found_var = true;
+ }
+
+ if (tv->v_type == VAR_FUNC) {
+ if (tv->vval.v_string == NULL) { // just in case
*lenp = 0;
return "";
}
- *lenp = (int)strlen(v->di_tv.vval.v_string);
- return v->di_tv.vval.v_string;
+ *lenp = (int)strlen(tv->vval.v_string);
+ return tv->vval.v_string;
}
- if (v != NULL && v->di_tv.v_type == VAR_PARTIAL) {
- partial_T *const pt = v->di_tv.vval.v_partial;
-
+ if (tv->v_type == VAR_PARTIAL) {
+ partial_T *const pt = tv->vval.v_partial;
if (pt == NULL) { // just in case
*lenp = 0;
return "";
@@ -421,63 +461,82 @@ char *deref_func_name(const char *name, int *lenp, partial_T **const partialp, b
/// Give an error message with a function name. Handle <SNR> things.
///
-/// @param ermsg must be passed without translation (use N_() instead of _()).
+/// @param errmsg must be passed without translation (use N_() instead of _()).
/// @param name function name
-void emsg_funcname(char *ermsg, const char *name)
+void emsg_funcname(const char *errmsg, const char *name)
{
- char *p;
+ char *p = (char *)name;
- if ((uint8_t)(*name) == K_SPECIAL) {
+ if ((uint8_t)name[0] == K_SPECIAL && name[1] != NUL && name[2] != NUL) {
p = concat_str("<SNR>", name + 3);
- } else {
- p = (char *)name;
}
- semsg(_(ermsg), p);
+ semsg(_(errmsg), p);
if (p != name) {
xfree(p);
}
}
-/// Allocate a variable for the result of a function.
-///
-/// @param name name of the function
-/// @param len length of "name" or -1 to use strlen()
-/// @param arg argument, pointing to the '('
-/// @param funcexe various values
-///
-/// @return OK or FAIL.
-int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, funcexe_T *funcexe)
+/// Get function arguments at "*arg" and advance it.
+/// Return them in "*argvars[MAX_FUNC_ARGS + 1]" and the count in "argcount".
+/// On failure FAIL is returned but the "argvars[argcount]" are still set.
+static int get_func_arguments(char **arg, evalarg_T *const evalarg, int partial_argc,
+ typval_T *argvars, int *argcount)
{
- char *argp;
+ char *argp = *arg;
int ret = OK;
- typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments
- int argcount = 0; // number of arguments found
// Get the arguments.
- argp = *arg;
- while (argcount < MAX_FUNC_ARGS
- - (funcexe->fe_partial == NULL ? 0 : funcexe->fe_partial->pt_argc)) {
+ while (*argcount < MAX_FUNC_ARGS - partial_argc) {
argp = skipwhite(argp + 1); // skip the '(' or ','
+
if (*argp == ')' || *argp == ',' || *argp == NUL) {
break;
}
- if (eval1(&argp, &argvars[argcount], funcexe->fe_evaluate) == FAIL) {
+ if (eval1(&argp, &argvars[*argcount], evalarg) == FAIL) {
ret = FAIL;
break;
}
- argcount++;
+ (*argcount)++;
if (*argp != ',') {
break;
}
}
+
+ argp = skipwhite(argp);
if (*argp == ')') {
argp++;
} else {
ret = FAIL;
}
+ *arg = argp;
+ return ret;
+}
+
+/// Call a function and put the result in "rettv".
+///
+/// @param name name of the function
+/// @param len length of "name" or -1 to use strlen()
+/// @param arg argument, pointing to the '('
+/// @param funcexe various values
+///
+/// @return OK or FAIL.
+int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, evalarg_T *const evalarg,
+ funcexe_T *funcexe)
+{
+ typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments
+ int argcount = 0; // number of arguments found
+ const bool evaluate = evalarg == NULL ? false : (evalarg->eval_flags & EVAL_EVALUATE);
+
+ char *argp = *arg;
+ int ret = get_func_arguments(&argp, evalarg,
+ (funcexe->fe_partial == NULL
+ ? 0
+ : funcexe->fe_partial->pt_argc),
+ argvars, &argcount);
+ assert(ret == OK || ret == FAIL); // suppress clang false positive
if (ret == OK) {
int i = 0;
@@ -495,7 +554,7 @@ int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, funcexe_
ret = call_func(name, len, rettv, argcount, argvars, funcexe);
funcargs.ga_len -= i;
- } else if (!aborting()) {
+ } else if (!aborting() && evaluate) {
if (argcount == MAX_FUNC_ARGS) {
emsg_funcname(N_("E740: Too many arguments for function %s"), name);
} else {
@@ -594,24 +653,28 @@ ufunc_T *find_func(const char *name)
/// Copy the function name of "fp" to buffer "buf".
/// "buf" must be able to hold the function name plus three bytes.
/// Takes care of script-local function names.
-static void cat_func_name(char *buf, ufunc_T *fp)
+static void cat_func_name(char *buf, size_t buflen, ufunc_T *fp)
{
- if ((uint8_t)fp->uf_name[0] == K_SPECIAL) {
- STRCPY(buf, "<SNR>");
- STRCAT(buf, fp->uf_name + 3);
+ int len = -1;
+ size_t uflen = strlen(fp->uf_name);
+ assert(uflen > 0);
+
+ if ((uint8_t)fp->uf_name[0] == K_SPECIAL && uflen > 3) {
+ len = snprintf(buf, buflen, "<SNR>%s", fp->uf_name + 3);
} else {
- STRCPY(buf, fp->uf_name);
+ len = snprintf(buf, buflen, "%s", fp->uf_name);
}
+
+ (void)len; // Avoid unused warning on release builds
+ assert(len > 0);
}
/// Add a number variable "name" to dict "dp" with value "nr".
static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr)
{
-#ifndef __clang_analyzer__
STRCPY(v->di_key, name);
-#endif
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- hash_add(&dp->dv_hashtab, (char *)v->di_key);
+ hash_add(&dp->dv_hashtab, v->di_key);
v->di_tv.v_type = VAR_NUMBER;
v->di_tv.v_lock = VAR_FIXED;
v->di_tv.vval.v_number = nr;
@@ -620,8 +683,8 @@ static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr)
/// Free "fc"
static void free_funccal(funccall_T *fc)
{
- for (int i = 0; i < fc->fc_funcs.ga_len; i++) {
- ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i];
+ for (int i = 0; i < fc->fc_ufuncs.ga_len; i++) {
+ ufunc_T *fp = ((ufunc_T **)(fc->fc_ufuncs.ga_data))[i];
// When garbage collecting a funccall_T may be freed before the
// function that references it, clear its uf_scoped field.
@@ -631,9 +694,9 @@ static void free_funccal(funccall_T *fc)
fp->uf_scoped = NULL;
}
}
- ga_clear(&fc->fc_funcs);
+ ga_clear(&fc->fc_ufuncs);
- func_ptr_unref(fc->func);
+ func_ptr_unref(fc->fc_func);
xfree(fc);
}
@@ -643,13 +706,13 @@ static void free_funccal(funccall_T *fc)
static void free_funccal_contents(funccall_T *fc)
{
// Free all l: variables.
- vars_clear(&fc->l_vars.dv_hashtab);
+ vars_clear(&fc->fc_l_vars.dv_hashtab);
// Free all a: variables.
- vars_clear(&fc->l_avars.dv_hashtab);
+ vars_clear(&fc->fc_l_avars.dv_hashtab);
// Free the a:000 variables.
- TV_LIST_ITER(&fc->l_varlist, li, {
+ TV_LIST_ITER(&fc->fc_l_varlist, li, {
tv_clear(TV_LIST_ITEM_TV(li));
});
@@ -663,11 +726,11 @@ static void cleanup_function_call(funccall_T *fc)
bool may_free_fc = fc->fc_refcount <= 0;
bool free_fc = true;
- current_funccal = fc->caller;
+ current_funccal = fc->fc_caller;
// Free all l: variables if not referred.
- if (may_free_fc && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT) {
- vars_clear(&fc->l_vars.dv_hashtab);
+ if (may_free_fc && fc->fc_l_vars.dv_refcount == DO_NOT_FREE_CNT) {
+ vars_clear(&fc->fc_l_vars.dv_hashtab);
} else {
free_fc = false;
}
@@ -675,25 +738,25 @@ static void cleanup_function_call(funccall_T *fc)
// 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 (may_free_fc && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT) {
- vars_clear_ext(&fc->l_avars.dv_hashtab, false);
+ if (may_free_fc && fc->fc_l_avars.dv_refcount == DO_NOT_FREE_CNT) {
+ vars_clear_ext(&fc->fc_l_avars.dv_hashtab, false);
} else {
free_fc = false;
// Make a copy of the a: variables, since we didn't do that above.
- TV_DICT_ITER(&fc->l_avars, di, {
+ TV_DICT_ITER(&fc->fc_l_avars, di, {
tv_copy(&di->di_tv, &di->di_tv);
});
}
- if (may_free_fc && fc->l_varlist.lv_refcount // NOLINT(runtime/deprecated)
+ if (may_free_fc && fc->fc_l_varlist.lv_refcount // NOLINT(runtime/deprecated)
== DO_NOT_FREE_CNT) {
- fc->l_varlist.lv_first = NULL; // NOLINT(runtime/deprecated)
+ fc->fc_l_varlist.lv_first = NULL; // NOLINT(runtime/deprecated)
} else {
free_fc = false;
// Make a copy of the a:000 items, since we didn't do that above.
- TV_LIST_ITER(&fc->l_varlist, li, {
+ TV_LIST_ITER(&fc->fc_l_varlist, li, {
tv_copy(TV_LIST_ITEM_TV(li), TV_LIST_ITEM_TV(li));
});
}
@@ -706,7 +769,7 @@ static void cleanup_function_call(funccall_T *fc)
// "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;
+ fc->fc_caller = previous_funccal;
previous_funccal = fc;
if (want_garbage_collect) {
@@ -729,26 +792,23 @@ static void cleanup_function_call(funccall_T *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;
}
fc->fc_refcount--;
if (force ? fc->fc_refcount <= 0 : !fc_referenced(fc)) {
- for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) {
+ for (funccall_T **pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->fc_caller) {
if (fc == *pfc) {
- *pfc = fc->caller;
+ *pfc = fc->fc_caller;
free_funccal_contents(fc);
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;
+ for (int i = 0; i < fc->fc_ufuncs.ga_len; i++) {
+ if (((ufunc_T **)(fc->fc_ufuncs.ga_data))[i] == fp) {
+ ((ufunc_T **)(fc->fc_ufuncs.ga_data))[i] = NULL;
}
}
}
@@ -759,14 +819,13 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force)
/// @return true if the entry was deleted, false if it wasn't found.
static bool func_remove(ufunc_T *fp)
{
- hashitem_T *hi = hash_find(&func_hashtab, (char *)UF2HIKEY(fp));
-
- if (!HASHITEM_EMPTY(hi)) {
- hash_remove(&func_hashtab, hi);
- return true;
+ hashitem_T *hi = hash_find(&func_hashtab, UF2HIKEY(fp));
+ if (HASHITEM_EMPTY(hi)) {
+ return false;
}
- return false;
+ hash_remove(&func_hashtab, hi);
+ return true;
}
static void func_clear_items(ufunc_T *fp)
@@ -825,6 +884,27 @@ static void func_clear_free(ufunc_T *fp, bool force)
func_free(fp);
}
+/// Allocate a funccall_T, link it in current_funccal and fill in "fp" and "rettv".
+/// Must be followed by one call to remove_funccal() or cleanup_function_call().
+funccall_T *create_funccal(ufunc_T *fp, typval_T *rettv)
+{
+ funccall_T *fc = xcalloc(1, sizeof(funccall_T));
+ fc->fc_caller = current_funccal;
+ current_funccal = fc;
+ fc->fc_func = fp;
+ func_ptr_ref(fp);
+ fc->fc_rettv = rettv;
+ return fc;
+}
+
+/// Restore current_funccal.
+void remove_funccal(void)
+{
+ funccall_T *fc = current_funccal;
+ current_funccal = fc->fc_caller;
+ free_funccal(fc);
+}
+
/// Call a user function
///
/// @param fp Function to call.
@@ -839,11 +919,10 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
FUNC_ATTR_NONNULL_ARG(1, 3, 4)
{
bool using_sandbox = false;
- funccall_T *fc;
int save_did_emsg;
static int depth = 0;
dictitem_T *v;
- int fixvar_idx = 0; // index in fixvar[]
+ int fixvar_idx = 0; // index in fc_fixvar[]
int ai;
bool islambda = false;
char numbuf[NUMBUFLEN];
@@ -874,40 +953,32 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// check for CTRL-C hit
line_breakcheck();
// prepare the funccall_T structure
- fc = xcalloc(1, sizeof(funccall_T));
- fc->caller = current_funccal;
- current_funccal = fc;
- fc->func = fp;
- fc->rettv = rettv;
- fc->level = ex_nesting_level;
+ funccall_T *fc = create_funccal(fp, rettv);
+ fc->fc_level = ex_nesting_level;
// Check if this function has a breakpoint.
- fc->breakpoint = dbg_find_breakpoint(false, (char *)fp->uf_name, (linenr_T)0);
- fc->dbg_tick = debug_tick;
-
+ fc->fc_breakpoint = dbg_find_breakpoint(false, fp->uf_name, 0);
+ fc->fc_dbg_tick = debug_tick;
// Set up fields for closure.
- ga_init(&fc->fc_funcs, sizeof(ufunc_T *), 1);
- func_ptr_ref(fp);
+ ga_init(&fc->fc_ufuncs, sizeof(ufunc_T *), 1);
if (strncmp(fp->uf_name, "<lambda>", 8) == 0) {
islambda = true;
}
- // Note about using fc->fixvar[]: This is an array of FIXVAR_CNT variables
+ // Note about using fc->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);
+ init_var_dict(&fc->fc_l_vars, &fc->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 = (dictitem_T *)&fc->fixvar[fixvar_idx++];
-#ifndef __clang_analyzer__
+ v = (dictitem_T *)&fc->fc_fixvar[fixvar_idx++];
name = (char *)v->di_key;
STRCPY(name, "self");
-#endif
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- hash_add(&fc->l_vars.dv_hashtab, (char *)v->di_key);
+ hash_add(&fc->fc_l_vars.dv_hashtab, v->di_key);
v->di_tv.v_type = VAR_DICT;
v->di_tv.v_lock = VAR_UNLOCKED;
v->di_tv.vval.v_dict = selfdict;
@@ -917,38 +988,36 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// Init a: variables, unless none found (in lambda).
// Set a:0 to "argcount" less number of named arguments, if >= 0.
// Set a:000 to a list with room for the "..." arguments.
- init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE);
+ init_var_dict(&fc->fc_l_avars, &fc->fc_l_avars_var, VAR_SCOPE);
if ((fp->uf_flags & FC_NOARGS) == 0) {
- add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], "0",
+ add_nr_var(&fc->fc_l_avars, (dictitem_T *)&fc->fc_fixvar[fixvar_idx++], "0",
(varnumber_T)(argcount >= fp->uf_args.ga_len
? argcount - fp->uf_args.ga_len : 0));
}
- fc->l_avars.dv_lock = VAR_FIXED;
+ fc->fc_l_avars.dv_lock = VAR_FIXED;
if ((fp->uf_flags & FC_NOARGS) == 0) {
// Use "name" to avoid a warning from some compiler that checks the
// destination size.
- v = (dictitem_T *)&fc->fixvar[fixvar_idx++];
-#ifndef __clang_analyzer__
+ v = (dictitem_T *)&fc->fc_fixvar[fixvar_idx++];
name = (char *)v->di_key;
STRCPY(name, "000");
-#endif
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- hash_add(&fc->l_avars.dv_hashtab, (char *)v->di_key);
+ hash_add(&fc->fc_l_avars.dv_hashtab, v->di_key);
v->di_tv.v_type = VAR_LIST;
v->di_tv.v_lock = VAR_FIXED;
- v->di_tv.vval.v_list = &fc->l_varlist;
+ v->di_tv.vval.v_list = &fc->fc_l_varlist;
}
- tv_list_init_static(&fc->l_varlist);
- tv_list_set_lock(&fc->l_varlist, VAR_FIXED);
+ tv_list_init_static(&fc->fc_l_varlist);
+ tv_list_set_lock(&fc->fc_l_varlist, VAR_FIXED);
// Set a:firstline to "firstline" and a:lastline to "lastline".
// Set a:name to named arguments.
// Set a:N to the "..." arguments.
// Skipped when no a: variables used (in lambda).
if ((fp->uf_flags & FC_NOARGS) == 0) {
- add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++],
+ add_nr_var(&fc->fc_l_avars, (dictitem_T *)&fc->fc_fixvar[fixvar_idx++],
"firstline", (varnumber_T)firstline);
- add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++],
+ add_nr_var(&fc->fc_l_avars, (dictitem_T *)&fc->fc_fixvar[fixvar_idx++],
"lastline", (varnumber_T)lastline);
}
bool default_arg_err = false;
@@ -974,7 +1043,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
default_expr = ((char **)(fp->uf_def_args.ga_data))
[ai + fp->uf_def_args.ga_len];
- if (eval1(&default_expr, &def_rettv, true) == FAIL) {
+ if (eval1(&default_expr, &def_rettv, &EVALARG_EVALUATE) == FAIL) {
default_arg_err = true;
break;
}
@@ -989,7 +1058,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
name = numbuf;
}
if (fixvar_idx < FIXVAR_CNT && strlen(name) <= VAR_SHORT_LEN) {
- v = (dictitem_T *)&fc->fixvar[fixvar_idx++];
+ v = (dictitem_T *)&fc->fc_fixvar[fixvar_idx++];
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
} else {
v = xmalloc(sizeof(dictitem_T) + strlen(name));
@@ -1011,17 +1080,17 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// 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);
- hash_add(&fc->l_vars.dv_hashtab, (char *)v->di_key);
+ hash_add(&fc->fc_l_vars.dv_hashtab, v->di_key);
} else {
- hash_add(&fc->l_avars.dv_hashtab, (char *)v->di_key);
+ hash_add(&fc->fc_l_avars.dv_hashtab, v->di_key);
}
if (ai >= 0 && ai < MAX_FUNC_ARGS) {
- listitem_T *li = &fc->l_listitems[ai];
+ listitem_T *li = &fc->fc_l_listitems[ai];
*TV_LIST_ITEM_TV(li) = argvars[i];
TV_LIST_ITEM_TV(li)->v_lock = VAR_FIXED;
- tv_list_append(&fc->l_varlist, li);
+ tv_list_append(&fc->fc_l_varlist, li);
}
}
@@ -1038,7 +1107,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
no_wait_return++;
verbose_enter_scroll();
- smsg(_("calling %s"), SOURCING_NAME);
+ smsg(0, _("calling %s"), SOURCING_NAME);
if (p_verbose >= 14) {
msg_puts("(");
for (int i = 0; i < argcount; i++) {
@@ -1046,7 +1115,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
msg_puts(", ");
}
if (argvars[i].v_type == VAR_NUMBER) {
- msg_outnum((long)argvars[i].vval.v_number);
+ msg_outnum((int)argvars[i].vval.v_number);
} else {
// Do not want errors such as E724 here.
emsg_off++;
@@ -1076,7 +1145,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
bool func_not_yet_profiling_but_should =
do_profiling_yes
- && !fp->uf_profiling && has_profiling(false, (char *)fp->uf_name, NULL);
+ && !fp->uf_profiling && has_profiling(false, fp->uf_name, NULL);
if (func_not_yet_profiling_but_should) {
started_profiling = true;
@@ -1086,7 +1155,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
bool func_or_func_caller_profiling =
do_profiling_yes
&& (fp->uf_profiling
- || (fc->caller != NULL && fc->caller->func->uf_profiling));
+ || (fc->fc_caller != NULL && fc->fc_caller->fc_func->uf_profiling));
if (func_or_func_caller_profiling) {
fp->uf_tm_count++;
@@ -1111,7 +1180,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// A Lambda always has the command "return {expr}". It is much faster
// to evaluate {expr} directly.
ex_nesting_level++;
- (void)eval1(&p, rettv, true);
+ (void)eval1(&p, rettv, &EVALARG_EVALUATE);
ex_nesting_level--;
} else {
// call do_cmdline() to execute the lines
@@ -1119,6 +1188,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
}
+ // Invoke functions added with ":defer".
+ handle_defer_one(current_funccal);
+
RedrawingDisabled--;
// when the function was aborted because of an error, return -1
@@ -1131,15 +1203,15 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
if (func_or_func_caller_profiling) {
call_start = profile_end(call_start);
- call_start = profile_sub_wait(wait_start, call_start); // -V614
+ call_start = profile_sub_wait(wait_start, call_start);
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);
- if (fc->caller != NULL && fc->caller->func->uf_profiling) {
- fc->caller->func->uf_tm_children =
- profile_add(fc->caller->func->uf_tm_children, call_start);
- fc->caller->func->uf_tml_children =
- profile_add(fc->caller->func->uf_tml_children, call_start);
+ if (fc->fc_caller != NULL && fc->fc_caller->fc_func->uf_profiling) {
+ fc->fc_caller->fc_func->uf_tm_children =
+ profile_add(fc->fc_caller->fc_func->uf_tm_children, call_start);
+ fc->fc_caller->fc_func->uf_tml_children =
+ profile_add(fc->fc_caller->fc_func->uf_tml_children, call_start);
}
if (started_profiling) {
// make a ":profdel func" stop profiling the function
@@ -1153,10 +1225,10 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
verbose_enter_scroll();
if (aborting()) {
- smsg(_("%s aborted"), SOURCING_NAME);
- } else if (fc->rettv->v_type == VAR_NUMBER) {
- smsg(_("%s returning #%" PRId64 ""),
- SOURCING_NAME, (int64_t)fc->rettv->vval.v_number);
+ smsg(0, _("%s aborted"), SOURCING_NAME);
+ } else if (fc->fc_rettv->v_type == VAR_NUMBER) {
+ smsg(0, _("%s returning #%" PRId64 ""),
+ SOURCING_NAME, (int64_t)fc->fc_rettv->vval.v_number);
} else {
char buf[MSG_BUF_LEN];
@@ -1164,7 +1236,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// have some idea how it starts and ends. smsg() would always
// truncate it at the end. Don't want errors such as E724 here.
emsg_off++;
- char *s = encode_tv2string(fc->rettv, NULL);
+ char *s = encode_tv2string(fc->fc_rettv, NULL);
char *tofree = s;
emsg_off--;
if (s != NULL) {
@@ -1172,7 +1244,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN);
s = buf;
}
- smsg(_("%s returning %s"), SOURCING_NAME, s);
+ smsg(0, _("%s returning %s"), SOURCING_NAME, s);
xfree(tofree);
}
}
@@ -1195,7 +1267,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
no_wait_return++;
verbose_enter_scroll();
- smsg(_("continuing in %s"), SOURCING_NAME);
+ smsg(0, _("continuing in %s"), SOURCING_NAME);
msg_puts("\n"); // don't overwrite this either
verbose_leave_scroll();
@@ -1231,6 +1303,21 @@ static bool func_name_refcount(const char *name)
return isdigit((uint8_t)(*name)) || *name == '<';
}
+/// Check the argument count for user function "fp".
+/// @return FCERR_UNKNOWN if OK, FCERR_TOOFEW or FCERR_TOOMANY otherwise.
+static int check_user_func_argcount(ufunc_T *fp, int argcount)
+ FUNC_ATTR_NONNULL_ALL
+{
+ const int regular_args = fp->uf_args.ga_len;
+
+ if (argcount < regular_args - fp->uf_def_args.ga_len) {
+ return FCERR_TOOFEW;
+ } else if (!fp->uf_varargs && argcount > regular_args) {
+ return FCERR_TOOMANY;
+ }
+ return FCERR_UNKNOWN;
+}
+
/// Call a user function after checking the arguments.
static int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv,
funcexe_T *funcexe, dict_T *selfdict)
@@ -1243,12 +1330,11 @@ static int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, ty
if ((fp->uf_flags & FC_RANGE) && funcexe->fe_doesrange != NULL) {
*funcexe->fe_doesrange = true;
}
- int error;
- if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len) {
- error = FCERR_TOOFEW;
- } else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len) {
- error = FCERR_TOOMANY;
- } else if ((fp->uf_flags & FC_DICT) && selfdict == NULL) {
+ int error = check_user_func_argcount(fp, argcount);
+ if (error != FCERR_UNKNOWN) {
+ return error;
+ }
+ if ((fp->uf_flags & FC_DICT) && selfdict == NULL) {
error = FCERR_DICT;
} else {
// Call the user function.
@@ -1302,8 +1388,8 @@ void free_all_functions(void)
// Clean up the current_funccal chain and the funccal stack.
while (current_funccal != NULL) {
- tv_clear(current_funccal->rettv);
- cleanup_function_call(current_funccal); // -V595
+ tv_clear(current_funccal->fc_rettv);
+ cleanup_function_call(current_funccal);
if (current_funccal == NULL && funccal_stack != NULL) {
restore_funccal();
}
@@ -1435,12 +1521,16 @@ varnumber_T callback_call_retnr(Callback *callback, int argcount, typval_T *argv
/// Give an error message for the result of a function.
/// Nothing if "error" is FCERR_NONE.
-static void user_func_error(int error, const char *name)
- FUNC_ATTR_NONNULL_ALL
+static void user_func_error(int error, const char *name, funcexe_T *funcexe)
+ FUNC_ATTR_NONNULL_ARG(2)
{
switch (error) {
case FCERR_UNKNOWN:
- emsg_funcname(N_("E117: Unknown function: %s"), name);
+ if (funcexe->fe_found_var) {
+ semsg(_(e_not_callable_type_str), name);
+ } else {
+ emsg_funcname(e_unknown_function_str, name);
+ }
break;
case FCERR_NOTMETHOD:
emsg_funcname(N_("E276: Cannot use function as a method: %s"), name);
@@ -1452,7 +1542,7 @@ static void user_func_error(int error, const char *name)
emsg_funcname(_(e_toomanyarg), name);
break;
case FCERR_TOOFEW:
- emsg_funcname(N_("E119: Not enough arguments for function: %s"), name);
+ emsg_funcname(_(e_toofewarg), name);
break;
case FCERR_SCRIPT:
emsg_funcname(N_("E120: Using <SID> not in a script context: %s"), name);
@@ -1511,7 +1601,7 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
int argv_base = 0;
partial_T *partial = funcexe->fe_partial;
- // Initialize rettv so that it is safe for caller to invoke clear_tv(rettv)
+ // Initialize rettv so that it is safe for caller to invoke tv_clear(rettv)
// even when call_func() returns FAIL.
rettv->v_type = VAR_UNKNOWN;
@@ -1524,7 +1614,7 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
if (fp == NULL) {
// Make a copy of the name, if it comes from a funcref variable it could
// be changed or deleted in the called function.
- name = xstrnsave(funcname, (size_t)len);
+ name = xmemdupz(funcname, (size_t)len);
fname = fname_trans_sid(name, fname_buf, &tofree, &error);
}
@@ -1577,7 +1667,7 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
XFREE_CLEAR(name);
funcname = "v:lua";
}
- } else if (fp != NULL || !builtin_function((const char *)rfname, -1)) {
+ } else if (fp != NULL || !builtin_function(rfname, -1)) {
// User defined function.
if (fp == NULL) {
fp = find_func(rfname);
@@ -1591,8 +1681,7 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
fp = find_func(rfname);
}
// Try loading a package.
- if (fp == NULL && script_autoload((const char *)rfname, strlen(rfname),
- true) && !aborting()) {
+ if (fp == NULL && script_autoload(rfname, strlen(rfname), true) && !aborting()) {
// Loaded a package, search for the function again.
fp = find_func(rfname);
}
@@ -1636,7 +1725,7 @@ theend:
// Report an error unless the argument evaluation or function call has been
// cancelled due to an aborting error, an interrupt, or an exception.
if (!aborting()) {
- user_func_error(error, (name != NULL) ? name : funcname);
+ user_func_error(error, (name != NULL) ? name : funcname, funcexe);
}
// clear the copies made from the partial
@@ -1655,22 +1744,41 @@ char *printable_func_name(ufunc_T *fp)
return fp->uf_name_exp != NULL ? fp->uf_name_exp : fp->uf_name;
}
+/// When "prev_ht_changed" does not equal "ht_changed" give an error and return
+/// true. Otherwise return false.
+static int function_list_modified(const int prev_ht_changed)
+{
+ if (prev_ht_changed != func_hashtab.ht_changed) {
+ emsg(_(e_function_list_was_modified));
+ return true;
+ }
+ return false;
+}
+
/// List the head of the function: "name(arg1, arg2)".
///
/// @param[in] fp Function pointer.
/// @param[in] indent Indent line.
/// @param[in] force Include bang "!" (i.e.: "function!").
-static void list_func_head(ufunc_T *fp, int indent, bool force)
+static int list_func_head(ufunc_T *fp, bool indent, bool force)
{
+ const int prev_ht_changed = func_hashtab.ht_changed;
+
msg_start();
+
+ // a callback at the more prompt may have deleted the function
+ if (function_list_modified(prev_ht_changed)) {
+ return FAIL;
+ }
+
if (indent) {
msg_puts(" ");
}
msg_puts(force ? "function! " : "function ");
if (fp->uf_name_exp != NULL) {
- msg_puts((const char *)fp->uf_name_exp);
+ msg_puts(fp->uf_name_exp);
} else {
- msg_puts((const char *)fp->uf_name);
+ msg_puts(fp->uf_name);
}
msg_putchar('(');
int j;
@@ -1678,7 +1786,7 @@ static void list_func_head(ufunc_T *fp, int indent, bool force)
if (j) {
msg_puts(", ");
}
- msg_puts((const char *)FUNCARG(fp, j));
+ msg_puts(FUNCARG(fp, j));
if (j >= fp->uf_args.ga_len - fp->uf_def_args.ga_len) {
msg_puts(" = ");
msg_puts(((char **)(fp->uf_def_args.ga_data))
@@ -1708,6 +1816,8 @@ static void list_func_head(ufunc_T *fp, int indent, bool force)
if (p_verbose > 0) {
last_set_msg(fp->uf_script_ctx);
}
+
+ return OK;
}
/// Get a function name, translating "<SID>" and "<SNR>".
@@ -1728,21 +1838,17 @@ char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, part
FUNC_ATTR_NONNULL_ARG(1)
{
char *name = NULL;
- const char *start;
- const char *end;
- int lead;
int len;
lval_T lv;
if (fdp != NULL) {
CLEAR_POINTER(fdp);
}
- start = *pp;
+ const char *start = *pp;
// Check for hard coded <SNR>: already translated function ID (from a user
// command).
- if ((unsigned char)(*pp)[0] == K_SPECIAL && (unsigned char)(*pp)[1] == KS_EXTRA
- && (*pp)[2] == KE_SNR) {
+ if ((uint8_t)(*pp)[0] == K_SPECIAL && (uint8_t)(*pp)[1] == KS_EXTRA && (*pp)[2] == KE_SNR) {
*pp += 3;
len = get_id_len((const char **)pp) + 3;
return xmemdupz(start, (size_t)len);
@@ -1750,14 +1856,14 @@ char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, part
// 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);
+ int lead = eval_fname_script(start);
if (lead > 2) {
start += lead;
}
// Note that TFN_ flags use the same values as GLV_ flags.
- end = get_lval((char *)start, NULL, &lv, false, skip, flags | GLV_READ_ONLY,
- lead > 2 ? 0 : FNE_CHECK_START);
+ const char *end = get_lval((char *)start, NULL, &lv, false, skip, flags | GLV_READ_ONLY,
+ lead > 2 ? 0 : FNE_CHECK_START);
if (end == start) {
if (!skip) {
emsg(_("E129: Function name required"));
@@ -1773,7 +1879,7 @@ char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, part
semsg(_(e_invarg2), start);
}
} else {
- *pp = (char *)find_name_end((char *)start, NULL, NULL, FNE_INCL_BR);
+ *pp = (char *)find_name_end(start, NULL, NULL, FNE_INCL_BR);
}
goto theend;
}
@@ -1828,14 +1934,13 @@ char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, part
// 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);
- name = deref_func_name(lv.ll_exp_name, &len, partial,
- flags & TFN_NO_AUTOLOAD);
- if ((const char *)name == lv.ll_exp_name) {
+ name = deref_func_name(lv.ll_exp_name, &len, partial, flags & TFN_NO_AUTOLOAD, NULL);
+ if (name == lv.ll_exp_name) {
name = NULL;
}
} 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(*pp, &len, partial, flags & TFN_NO_AUTOLOAD, NULL);
if (name == *pp) {
name = NULL;
}
@@ -1883,8 +1988,7 @@ char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, part
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((const char *)(*pp))) {
+ if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name)) || eval_fname_sid(*pp)) {
// It's "s:" or "<SID>".
if (current_sctx.sc_sid <= 0) {
emsg(_(e_usingsid));
@@ -1971,7 +2075,7 @@ char *save_function_name(char **name, bool skip, int flags, funcdict_T *fudi)
if (strncmp(p, "<lambda>", 8) == 0) {
p += 8;
(void)getdigits(&p, false, 0);
- saved = xstrndup(*name, (size_t)(p - *name));
+ saved = xmemdupz(*name, (size_t)(p - *name));
if (fudi != NULL) {
CLEAR_POINTER(fudi);
}
@@ -1990,7 +2094,7 @@ char *save_function_name(char **name, bool skip, int flags, funcdict_T *fudi)
/// Otherwise functions matching "regmatch".
static void list_functions(regmatch_T *regmatch)
{
- const int changed = func_hashtab.ht_changed;
+ const int prev_ht_changed = func_hashtab.ht_changed;
size_t todo = func_hashtab.ht_used;
const hashitem_T *const ht_array = func_hashtab.ht_array;
@@ -1998,15 +2102,15 @@ static void list_functions(regmatch_T *regmatch)
if (!HASHITEM_EMPTY(hi)) {
ufunc_T *fp = HI2UF(hi);
todo--;
- if ((fp->uf_flags & FC_DEAD) == 0
- && (regmatch == NULL
- ? (!message_filtered((char *)fp->uf_name)
- && !func_name_refcount(fp->uf_name))
- : (!isdigit((uint8_t)(*fp->uf_name))
- && vim_regexec(regmatch, (char *)fp->uf_name, 0)))) {
- list_func_head(fp, false, false);
- if (changed != func_hashtab.ht_changed) {
- emsg(_("E454: function list was modified"));
+ if (regmatch == NULL
+ ? (!message_filtered(fp->uf_name)
+ && !func_name_refcount(fp->uf_name))
+ : (!isdigit((uint8_t)(*fp->uf_name))
+ && vim_regexec(regmatch, fp->uf_name, 0))) {
+ if (list_func_head(fp, false, false) == FAIL) {
+ return;
+ }
+ if (function_list_modified(prev_ht_changed)) {
return;
}
}
@@ -2019,11 +2123,7 @@ void ex_function(exarg_T *eap)
{
char *theline;
char *line_to_free = NULL;
- char c;
- int saved_did_emsg;
bool saved_wait_return = need_wait_return;
- char *name = NULL;
- char *p;
char *arg;
char *line_arg = NULL;
garray_T newargs;
@@ -2033,16 +2133,9 @@ void ex_function(exarg_T *eap)
int flags = 0;
ufunc_T *fp;
bool overwrite = false;
- int indent;
- int nesting;
- dictitem_T *v;
funcdict_T fudi;
static int func_nr = 0; // number for nameless function
- int paren;
hashtab_T *ht;
- hashitem_T *hi;
- linenr_T sourcing_lnum_off;
- linenr_T sourcing_lnum_top;
bool is_heredoc = false;
char *skip_until = NULL;
char *heredoc_trimmed = NULL;
@@ -2060,11 +2153,11 @@ void ex_function(exarg_T *eap)
// ":function /pat": list functions matching pattern.
if (*eap->arg == '/') {
- p = skip_regexp(eap->arg + 1, '/', true);
+ char *p = skip_regexp(eap->arg + 1, '/', true);
if (!eap->skip) {
regmatch_T regmatch;
- c = *p;
+ char c = *p;
*p = NUL;
regmatch.regprog = vim_regcomp(eap->arg + 1, RE_MAGIC);
*p = c;
@@ -2095,9 +2188,9 @@ void ex_function(exarg_T *eap)
// "fudi.fd_di" set, "fudi.fd_newkey" == NULL
// s:func script-local function name
// g:func global function name, same as "func"
- p = eap->arg;
- name = save_function_name(&p, eap->skip, TFN_NO_AUTOLOAD, &fudi);
- paren = (vim_strchr(p, '(') != NULL);
+ char *p = eap->arg;
+ char *name = save_function_name(&p, eap->skip, TFN_NO_AUTOLOAD, &fudi);
+ int paren = (vim_strchr(p, '(') != NULL);
if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip) {
// Return on an invalid expression in braces, unless the expression
// evaluation has been cancelled due to an aborting error, an
@@ -2114,7 +2207,7 @@ void ex_function(exarg_T *eap)
// An error in a function call during evaluation of an expression in magic
// braces should not cause the function not to be defined.
- saved_did_emsg = did_emsg;
+ const int saved_did_emsg = did_emsg;
did_emsg = false;
//
@@ -2135,28 +2228,37 @@ void ex_function(exarg_T *eap)
if (!eap->skip && !got_int) {
fp = find_func(name);
if (fp != NULL) {
- list_func_head(fp, !eap->forceit, eap->forceit);
- for (int j = 0; j < fp->uf_lines.ga_len && !got_int; j++) {
- if (FUNCLINE(fp, j) == NULL) {
- continue;
- }
- msg_putchar('\n');
- if (!eap->forceit) {
- msg_outnum((long)j + 1);
- if (j < 9) {
- msg_putchar(' ');
+ // Check no function was added or removed from a callback, e.g. at
+ // the more prompt. "fp" may then be invalid.
+ const int prev_ht_changed = func_hashtab.ht_changed;
+
+ if (list_func_head(fp, !eap->forceit, eap->forceit) == OK) {
+ for (int j = 0; j < fp->uf_lines.ga_len && !got_int; j++) {
+ if (FUNCLINE(fp, j) == NULL) {
+ continue;
}
- if (j < 99) {
- msg_putchar(' ');
+ msg_putchar('\n');
+ if (!eap->forceit) {
+ msg_outnum(j + 1);
+ if (j < 9) {
+ msg_putchar(' ');
+ }
+ if (j < 99) {
+ msg_putchar(' ');
+ }
+ if (function_list_modified(prev_ht_changed)) {
+ break;
+ }
+ }
+ msg_prt_line(FUNCLINE(fp, j), false);
+ line_breakcheck(); // show multiple lines at a time!
+ }
+ if (!got_int) {
+ msg_putchar('\n');
+ if (!function_list_modified(prev_ht_changed)) {
+ msg_puts(eap->forceit ? "endfunction" : " endfunction");
}
}
- msg_prt_line(FUNCLINE(fp, j), false);
- ui_flush(); // show a line at a time
- os_breakcheck();
- }
- if (!got_int) {
- msg_putchar('\n');
- msg_puts(eap->forceit ? "endfunction" : " endfunction");
}
} else {
emsg_funcname(N_("E123: Undefined function: %s"), name);
@@ -2196,7 +2298,7 @@ void ex_function(exarg_T *eap)
j++;
}
if (arg[j] != NUL) {
- emsg_funcname((char *)e_invarg2, arg);
+ emsg_funcname(e_invarg2, arg);
}
}
// Disallow using the g: dict.
@@ -2212,11 +2314,11 @@ void ex_function(exarg_T *eap)
if (KeyTyped && ui_has(kUICmdline)) {
show_block = true;
- ui_ext_cmdline_block_append(0, (const char *)eap->cmd);
+ ui_ext_cmdline_block_append(0, eap->cmd);
}
// find extra arguments "range", "dict", "abort" and "closure"
- for (;;) {
+ while (true) {
p = skipwhite(p);
if (strncmp(p, "range", 5) == 0) {
flags |= FC_RANGE;
@@ -2272,11 +2374,11 @@ void ex_function(exarg_T *eap)
}
// Save the starting line number.
- sourcing_lnum_top = SOURCING_LNUM;
+ linenr_T sourcing_lnum_top = SOURCING_LNUM;
- indent = 2;
- nesting = 0;
- for (;;) {
+ int indent = 2;
+ int nesting = 0;
+ while (true) {
if (KeyTyped) {
msg_scroll = true;
saved_wait_return = false;
@@ -2296,7 +2398,7 @@ void ex_function(exarg_T *eap)
} else {
xfree(line_to_free);
if (eap->getline == NULL) {
- theline = getcmdline(':', 0L, indent, do_concat);
+ theline = getcmdline(':', 0, indent, do_concat);
} else {
theline = eap->getline(':', eap->cookie, indent, do_concat);
}
@@ -2306,16 +2408,20 @@ void ex_function(exarg_T *eap)
lines_left = Rows - 1;
}
if (theline == NULL) {
- emsg(_("E126: Missing :endfunction"));
+ if (skip_until != NULL) {
+ semsg(_(e_missing_heredoc_end_marker_str), skip_until);
+ } else {
+ emsg(_("E126: Missing :endfunction"));
+ }
goto erret;
}
if (show_block) {
assert(indent >= 0);
- ui_ext_cmdline_block_append((size_t)indent, (const char *)theline);
+ ui_ext_cmdline_block_append((size_t)indent, theline);
}
// Detect line continuation: SOURCING_LNUM increased more than one.
- sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
+ linenr_T sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
if (SOURCING_LNUM < sourcing_lnum_off) {
sourcing_lnum_off -= SOURCING_LNUM;
} else {
@@ -2335,7 +2441,7 @@ void ex_function(exarg_T *eap)
p = theline;
} else if (is_heredoc) {
p = skipwhite(theline) == theline
- ? theline : theline + strlen(heredoc_trimmed);
+ ? theline : theline + strlen(heredoc_trimmed);
} else {
p = theline + strlen(heredoc_trimmed);
}
@@ -2361,7 +2467,7 @@ void ex_function(exarg_T *eap)
} else if (line_arg != NULL && *skipwhite(line_arg) != NUL) {
nextcmd = line_arg;
} else if (*p != NUL && *p != '"' && p_verbose > 0) {
- give_warning2(_("W22: Text found after :endfunction: %s"), p, true);
+ swmsg(true, _("W22: Text found after :endfunction: %s"), p);
}
if (nextcmd != NULL) {
// Another command follows. If the line came from "eap" we
@@ -2393,11 +2499,11 @@ void ex_function(exarg_T *eap)
if (*p == '!') {
p = skipwhite(p + 1);
}
- p += eval_fname_script((const char *)p);
+ p += eval_fname_script(p);
xfree(trans_function_name(&p, true, 0, NULL, NULL));
if (*skipwhite(p) == '(') {
if (nesting == MAX_FUNC_NESTING - 1) {
- emsg(_("E1058: function nesting too deep"));
+ emsg(_(e_function_nesting_too_deep));
} else {
nesting++;
indent += 2;
@@ -2440,35 +2546,45 @@ void ex_function(exarg_T *eap)
&& (!ASCII_ISALPHA(p[2]) || p[2] == 's')))) {
// ":python <<" continues until a dot, like ":append"
p = skipwhite(arg + 2);
+ if (strncmp(p, "trim", 4) == 0) {
+ // Ignore leading white space.
+ p = skipwhite(p + 4);
+ heredoc_trimmed = xmemdupz(theline, (size_t)(skipwhite(theline) - theline));
+ }
if (*p == NUL) {
skip_until = xstrdup(".");
} else {
- skip_until = xstrdup(p);
+ skip_until = xmemdupz(p, (size_t)(skiptowhite(p) - p));
}
+ do_concat = false;
+ is_heredoc = true;
}
// Check for ":let v =<< [trim] EOF"
// and ":let [a, b] =<< [trim] EOF"
- arg = skipwhite(skiptowhite(p));
- if (*arg == '[') {
- arg = vim_strchr(arg, ']');
- }
- if (arg != NULL) {
- arg = skipwhite(skiptowhite(arg));
- if (arg[0] == '='
- && arg[1] == '<'
- && arg[2] == '<'
- && (p[0] == 'l'
- && p[1] == 'e'
- && (!ASCII_ISALNUM(p[2])
- || (p[2] == 't' && !ASCII_ISALNUM(p[3]))))) {
+ arg = p;
+ if (checkforcmd(&arg, "let", 2)) {
+ while (vim_strchr("$@&", *arg) != NULL) {
+ arg++;
+ }
+ arg = skipwhite(find_name_end(arg, NULL, NULL, FNE_INCL_BR));
+ if (arg[0] == '=' && arg[1] == '<' && arg[2] == '<') {
p = skipwhite(arg + 3);
- if (strncmp(p, "trim", 4) == 0) {
- // Ignore leading white space.
- p = skipwhite(p + 4);
- heredoc_trimmed = xstrnsave(theline, (size_t)(skipwhite(theline) - theline));
+ while (true) {
+ if (strncmp(p, "trim", 4) == 0) {
+ // Ignore leading white space.
+ p = skipwhite(p + 4);
+ heredoc_trimmed = xmemdupz(theline, (size_t)(skipwhite(theline) - theline));
+ continue;
+ }
+ if (strncmp(p, "eval", 4) == 0) {
+ // Ignore leading white space.
+ p = skipwhite(p + 4);
+ continue;
+ }
+ break;
}
- skip_until = xstrnsave(p, (size_t)(skiptowhite(p) - p));
+ skip_until = xmemdupz(p, (size_t)(skiptowhite(p) - p));
do_concat = false;
is_heredoc = true;
}
@@ -2504,7 +2620,7 @@ void ex_function(exarg_T *eap)
// If there are no errors, add the function
if (fudi.fd_dict == NULL) {
- v = find_var((const char *)name, strlen(name), &ht, false);
+ dictitem_T *v = find_var(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);
goto erret;
@@ -2551,13 +2667,11 @@ void ex_function(exarg_T *eap)
goto erret;
}
if (fudi.fd_di == NULL) {
- if (value_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg,
- TV_CSTRING)) {
+ if (value_check_lock(fudi.fd_dict->dv_lock, eap->arg, TV_CSTRING)) {
// Can't add a function to a locked dictionary
goto erret;
}
- } else if (value_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg,
- TV_CSTRING)) {
+ } else if (value_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, TV_CSTRING)) {
// Can't change an existing function if it is locked
goto erret;
}
@@ -2565,22 +2679,19 @@ void ex_function(exarg_T *eap)
// Give the function a sequential number. Can only be used with a
// Funcref!
xfree(name);
- sprintf(numbuf, "%d", ++func_nr); // NOLINT(runtime/printf)
+ snprintf(numbuf, sizeof(numbuf), "%d", ++func_nr);
name = xstrdup(numbuf);
}
if (fp == NULL) {
if (fudi.fd_dict == NULL && vim_strchr(name, AUTOLOAD_CHAR) != NULL) {
- int slen, plen;
- char *scriptname;
-
// Check that the autoload name matches the script name.
int j = FAIL;
if (SOURCING_NAME != NULL) {
- scriptname = autoload_name(name, strlen(name));
+ char *scriptname = autoload_name(name, strlen(name));
p = vim_strchr(scriptname, '/');
- plen = (int)strlen(p);
- slen = (int)strlen(SOURCING_NAME);
+ int plen = (int)strlen(p);
+ int slen = (int)strlen(SOURCING_NAME);
if (slen > plen && path_fnamecmp(p, SOURCING_NAME + slen - plen) == 0) {
j = OK;
}
@@ -2598,7 +2709,7 @@ void ex_function(exarg_T *eap)
if (fudi.fd_dict != NULL) {
if (fudi.fd_di == NULL) {
// Add new dict entry
- fudi.fd_di = tv_dict_item_alloc((const char *)fudi.fd_newkey);
+ fudi.fd_di = tv_dict_item_alloc(fudi.fd_newkey);
if (tv_dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) {
xfree(fudi.fd_di);
xfree(fp);
@@ -2618,8 +2729,8 @@ void ex_function(exarg_T *eap)
// insert the new function in the function list
set_ufunc_name(fp, name);
if (overwrite) {
- hi = hash_find(&func_hashtab, name);
- hi->hi_key = (char *)UF2HIKEY(fp);
+ hashitem_T *hi = hash_find(&func_hashtab, name);
+ hi->hi_key = UF2HIKEY(fp);
} else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) {
xfree(fp);
goto erret;
@@ -2688,7 +2799,7 @@ int eval_fname_script(const char *const p)
bool translated_function_exists(const char *name)
{
if (builtin_function(name, -1)) {
- return find_internal_func((char *)name) != NULL;
+ return find_internal_func(name) != NULL;
}
return find_func(name) != NULL;
}
@@ -2727,7 +2838,6 @@ char *get_user_func_name(expand_T *xp, int idx)
static size_t done;
static int changed;
static hashitem_T *hi;
- ufunc_T *fp;
if (idx == 0) {
done = 0;
@@ -2742,7 +2852,7 @@ char *get_user_func_name(expand_T *xp, int idx)
while (HASHITEM_EMPTY(hi)) {
hi++;
}
- fp = HI2UF(hi);
+ ufunc_T *fp = HI2UF(hi);
if ((fp->uf_flags & FC_DICT)
|| strncmp(fp->uf_name, "<lambda>", 8) == 0) {
@@ -2750,14 +2860,14 @@ char *get_user_func_name(expand_T *xp, int idx)
}
if (strlen(fp->uf_name) + 4 >= IOSIZE) {
- return (char *)fp->uf_name; // Prevent overflow.
+ return fp->uf_name; // Prevent overflow.
}
- cat_func_name(IObuff, fp);
+ cat_func_name(IObuff, IOSIZE, fp);
if (xp->xp_context != EXPAND_USER_FUNC) {
- STRCAT(IObuff, "(");
+ xstrlcat(IObuff, "(", IOSIZE);
if (!fp->uf_varargs && GA_EMPTY(&fp->uf_args)) {
- STRCAT(IObuff, ")");
+ xstrlcat(IObuff, ")", IOSIZE);
}
}
return IObuff;
@@ -2769,12 +2879,10 @@ char *get_user_func_name(expand_T *xp, int idx)
void ex_delfunction(exarg_T *eap)
{
ufunc_T *fp = NULL;
- char *p;
- char *name;
funcdict_T fudi;
- p = eap->arg;
- name = trans_function_name(&p, eap->skip, 0, &fudi, NULL);
+ char *p = eap->arg;
+ char *name = trans_function_name(&p, eap->skip, 0, &fudi, NULL);
xfree(fudi.fd_newkey);
if (name == NULL) {
if (fudi.fd_dict != NULL && !eap->skip) {
@@ -2851,13 +2959,11 @@ void ex_delfunction(exarg_T *eap)
/// becomes zero.
void func_unref(char *name)
{
- ufunc_T *fp = NULL;
-
if (name == NULL || !func_name_refcount(name)) {
return;
}
- fp = find_func(name);
+ ufunc_T *fp = find_func(name);
if (fp == NULL && isdigit((uint8_t)(*name))) {
#ifdef EXITFREE
if (!entered_free_all_mem) {
@@ -2893,12 +2999,10 @@ void func_ptr_unref(ufunc_T *fp)
/// Count a reference to a Function.
void func_ref(char *name)
{
- ufunc_T *fp;
-
if (name == NULL || !func_name_refcount(name)) {
return;
}
- fp = find_func(name);
+ ufunc_T *fp = find_func(name);
if (fp != NULL) {
(fp->uf_refcount)++;
} else if (isdigit((uint8_t)(*name))) {
@@ -2925,10 +3029,10 @@ static inline bool fc_referenced(const funccall_T *const fc)
FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
FUNC_ATTR_NONNULL_ALL
{
- return ((fc->l_varlist.lv_refcount // NOLINT(runtime/deprecated)
+ return ((fc->fc_l_varlist.lv_refcount // NOLINT(runtime/deprecated)
!= DO_NOT_FREE_CNT)
- || fc->l_vars.dv_refcount != DO_NOT_FREE_CNT
- || fc->l_avars.dv_refcount != DO_NOT_FREE_CNT
+ || fc->fc_l_vars.dv_refcount != DO_NOT_FREE_CNT
+ || fc->fc_l_avars.dv_refcount != DO_NOT_FREE_CNT
|| fc->fc_refcount > 0);
}
@@ -2936,9 +3040,9 @@ static inline bool fc_referenced(const funccall_T *const fc)
/// 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
+ return fc->fc_l_varlist.lv_copyID != copyID
+ && fc->fc_l_vars.dv_copyID != copyID
+ && fc->fc_l_avars.dv_copyID != copyID
&& fc->fc_copyID != copyID;
}
@@ -2954,13 +3058,15 @@ void ex_return(exarg_T *eap)
return;
}
+ evalarg_T evalarg = { .eval_flags = eap->skip ? 0 : EVAL_EVALUATE };
+
if (eap->skip) {
emsg_skip++;
}
eap->nextcmd = NULL;
if ((*arg != NUL && *arg != '|' && *arg != '\n')
- && eval0(arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) {
+ && eval0(arg, &rettv, eap, &evalarg) != FAIL) {
if (!eap->skip) {
returning = do_return(eap, false, true, &rettv);
} else {
@@ -2989,36 +3095,234 @@ void ex_return(exarg_T *eap)
if (eap->skip) {
emsg_skip--;
}
+ clear_evalarg(&evalarg, eap);
+}
+
+/// Lower level implementation of "call". Only called when not skipping.
+static int ex_call_inner(exarg_T *eap, char *name, char **arg, char *startarg,
+ const funcexe_T *const funcexe_init, evalarg_T *const evalarg)
+{
+ bool doesrange;
+ bool failed = false;
+
+ for (linenr_T lnum = eap->line1; lnum <= eap->line2; lnum++) {
+ if (eap->addr_count > 0) {
+ if (lnum > curbuf->b_ml.ml_line_count) {
+ // If the function deleted lines or switched to another buffer
+ // the line number may become invalid.
+ emsg(_(e_invrange));
+ break;
+ }
+ curwin->w_cursor.lnum = lnum;
+ curwin->w_cursor.col = 0;
+ curwin->w_cursor.coladd = 0;
+ }
+ *arg = startarg;
+
+ funcexe_T funcexe = *funcexe_init;
+ funcexe.fe_doesrange = &doesrange;
+ typval_T rettv;
+ rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this
+ if (get_func_tv(name, -1, &rettv, arg, evalarg, &funcexe) == FAIL) {
+ failed = true;
+ break;
+ }
+
+ // Handle a function returning a Funcref, Dictionary or List.
+ if (handle_subscript((const char **)arg, &rettv, &EVALARG_EVALUATE, true) == FAIL) {
+ failed = true;
+ break;
+ }
+
+ tv_clear(&rettv);
+ if (doesrange) {
+ break;
+ }
+
+ // Stop when immediately aborting on error, or when an interrupt
+ // occurred or an exception was thrown but not caught.
+ // get_func_tv() returned OK, so that the check for trailing
+ // characters below is executed.
+ if (aborting()) {
+ break;
+ }
+ }
+
+ return failed;
+}
+
+/// Core part of ":defer func(arg)". "arg" points to the "(" and is advanced.
+///
+/// @return FAIL or OK.
+static int ex_defer_inner(char *name, char **arg, const partial_T *const partial,
+ evalarg_T *const evalarg)
+{
+ typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments
+ int partial_argc = 0; // number of partial arguments
+ int argcount = 0; // number of arguments found
+
+ if (current_funccal == NULL) {
+ semsg(_(e_str_not_inside_function), "defer");
+ return FAIL;
+ }
+ if (partial != NULL) {
+ if (partial->pt_dict != NULL) {
+ emsg(_(e_cannot_use_partial_with_dictionary_for_defer));
+ return FAIL;
+ }
+ if (partial->pt_argc > 0) {
+ partial_argc = partial->pt_argc;
+ for (int i = 0; i < partial_argc; i++) {
+ tv_copy(&partial->pt_argv[i], &argvars[i]);
+ }
+ }
+ }
+ int r = get_func_arguments(arg, evalarg, false, argvars + partial_argc, &argcount);
+ argcount += partial_argc;
+
+ if (r == OK) {
+ if (builtin_function(name, -1)) {
+ const EvalFuncDef *const fdef = find_internal_func(name);
+ if (fdef == NULL) {
+ emsg_funcname(e_unknown_function_str, name);
+ r = FAIL;
+ } else if (check_internal_func(fdef, argcount) == -1) {
+ r = FAIL;
+ }
+ } else {
+ ufunc_T *ufunc = find_func(name);
+ // we tolerate an unknown function here, it might be defined later
+ if (ufunc != NULL) {
+ int error = check_user_func_argcount(ufunc, argcount);
+ if (error != FCERR_UNKNOWN) {
+ user_func_error(error, name, NULL);
+ r = FAIL;
+ }
+ }
+ }
+ }
+
+ if (r == FAIL) {
+ while (--argcount >= 0) {
+ tv_clear(&argvars[argcount]);
+ }
+ return FAIL;
+ }
+ add_defer(name, argcount, argvars);
+ return OK;
+}
+
+/// Return true if currently inside a function call.
+/// Give an error message and return false when not.
+bool can_add_defer(void)
+{
+ if (get_current_funccal() == NULL) {
+ semsg(_(e_str_not_inside_function), "defer");
+ return false;
+ }
+ return true;
+}
+
+/// Add a deferred call for "name" with arguments "argvars[argcount]".
+/// Consumes "argvars[]".
+/// Caller must check that current_funccal is not NULL.
+void add_defer(char *name, int argcount_arg, typval_T *argvars)
+{
+ char *saved_name = xstrdup(name);
+ int argcount = argcount_arg;
+
+ if (current_funccal->fc_defer.ga_itemsize == 0) {
+ ga_init(&current_funccal->fc_defer, sizeof(defer_T), 10);
+ }
+ defer_T *dr = GA_APPEND_VIA_PTR(defer_T, &current_funccal->fc_defer);
+ dr->dr_name = saved_name;
+ dr->dr_argcount = argcount;
+ while (argcount > 0) {
+ argcount--;
+ dr->dr_argvars[argcount] = argvars[argcount];
+ }
+}
+
+/// Invoked after a function has finished: invoke ":defer" functions.
+static void handle_defer_one(funccall_T *funccal)
+{
+ for (int idx = funccal->fc_defer.ga_len - 1; idx >= 0; idx--) {
+ defer_T *dr = ((defer_T *)funccal->fc_defer.ga_data) + idx;
+
+ if (dr->dr_name == NULL) {
+ // already being called, can happen if function does ":qa"
+ continue;
+ }
+
+ funcexe_T funcexe = { .fe_evaluate = true };
+
+ typval_T rettv;
+ rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this
+
+ char *name = dr->dr_name;
+ dr->dr_name = NULL;
+
+ // If the deferred function is called after an exception, then only the
+ // first statement in the function will be executed (because of the
+ // exception). So save and restore the try/catch/throw exception
+ // state.
+ exception_state_T estate;
+ exception_state_save(&estate);
+ exception_state_clear();
+
+ call_func(name, -1, &rettv, dr->dr_argcount, dr->dr_argvars, &funcexe);
+
+ exception_state_restore(&estate);
+
+ tv_clear(&rettv);
+ xfree(name);
+ for (int i = dr->dr_argcount - 1; i >= 0; i--) {
+ tv_clear(&dr->dr_argvars[i]);
+ }
+ }
+ ga_clear(&funccal->fc_defer);
+}
+
+/// Called when exiting: call all defer functions.
+void invoke_all_defer(void)
+{
+ for (funccall_T *fc = current_funccal; fc != NULL; fc = fc->fc_caller) {
+ handle_defer_one(fc);
+ }
+
+ for (funccal_entry_T *fce = funccal_stack; fce != NULL; fce = fce->next) {
+ for (funccall_T *fc = fce->top_funccal; fc != NULL; fc = fc->fc_caller) {
+ handle_defer_one(fc);
+ }
+ }
}
/// ":1,25call func(arg1, arg2)" function call.
+/// ":defer func(arg1, arg2)" deferred function call.
void ex_call(exarg_T *eap)
{
char *arg = eap->arg;
- char *startarg;
- char *name;
- char *tofree;
- int len;
- typval_T rettv;
- linenr_T lnum;
- bool doesrange;
bool failed = false;
funcdict_T fudi;
partial_T *partial = NULL;
+ evalarg_T evalarg;
+ fill_evalarg_from_eap(&evalarg, eap, eap->skip);
if (eap->skip) {
+ typval_T rettv;
// 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) {
+ if (eval0(eap->arg, &rettv, eap, &evalarg) != FAIL) {
tv_clear(&rettv);
}
emsg_skip--;
+ clear_evalarg(&evalarg, eap);
return;
}
- tofree = trans_function_name(&arg, false, TFN_INT, &fudi, &partial);
+ char *tofree = trans_function_name(&arg, false, TFN_INT, &fudi, &partial);
if (fudi.fd_newkey != NULL) {
// Still need to give an error message for missing key.
semsg(_(e_dictkey), fudi.fd_newkey);
@@ -3037,66 +3341,31 @@ void ex_call(exarg_T *eap)
// If it is the name of a variable of type VAR_FUNC or VAR_PARTIAL use its
// 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, partial != NULL ? NULL : &partial, false);
+ int len = (int)strlen(tofree);
+ bool found_var = false;
+ char *name = deref_func_name(tofree, &len, partial != NULL ? NULL : &partial, false, &found_var);
// Skip white space to allow ":call func ()". Not good, but required for
// backward compatibility.
- startarg = skipwhite(arg);
- rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this.
+ char *startarg = skipwhite(arg);
if (*startarg != '(') {
semsg(_(e_missingparen), eap->arg);
goto end;
}
- lnum = eap->line1;
- for (; lnum <= eap->line2; lnum++) {
- if (eap->addr_count > 0) { // -V560
- if (lnum > curbuf->b_ml.ml_line_count) {
- // If the function deleted lines or switched to another buffer
- // the line number may become invalid.
- emsg(_(e_invrange));
- break;
- }
- curwin->w_cursor.lnum = lnum;
- curwin->w_cursor.col = 0;
- curwin->w_cursor.coladd = 0;
- }
+ if (eap->cmdidx == CMD_defer) {
arg = startarg;
-
+ failed = ex_defer_inner(name, &arg, partial, &evalarg) == FAIL;
+ } else {
funcexe_T funcexe = FUNCEXE_INIT;
+ funcexe.fe_partial = partial;
+ funcexe.fe_selfdict = fudi.fd_dict;
funcexe.fe_firstline = eap->line1;
funcexe.fe_lastline = eap->line2;
- funcexe.fe_doesrange = &doesrange;
+ funcexe.fe_found_var = found_var;
funcexe.fe_evaluate = true;
- funcexe.fe_partial = partial;
- funcexe.fe_selfdict = fudi.fd_dict;
- if (get_func_tv(name, -1, &rettv, &arg, &funcexe) == FAIL) {
- failed = true;
- break;
- }
-
- // Handle a function returning a Funcref, Dictionary or List.
- if (handle_subscript((const char **)&arg, &rettv, true, true,
- (const char *)name, (const char **)&name)
- == FAIL) {
- failed = true;
- break;
- }
-
- tv_clear(&rettv);
- if (doesrange) {
- break;
- }
-
- // Stop when immediately aborting on error, or when an interrupt
- // occurred or an exception was thrown but not caught.
- // get_func_tv() returned OK, so that the check for trailing
- // characters below is executed.
- if (aborting()) {
- break;
- }
+ failed = ex_call_inner(eap, name, &arg, startarg, &funcexe, &evalarg);
}
// When inside :try we need to check for following "| catch" or "| endtry".
@@ -3112,6 +3381,7 @@ void ex_call(exarg_T *eap)
eap->nextcmd = check_nextcmd(arg);
}
}
+ clear_evalarg(&evalarg, eap);
end:
tv_dict_unref(fudi.fd_dict);
@@ -3130,19 +3400,18 @@ end:
/// false when the return gets pending.
int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv)
{
- int idx;
cstack_T *const cstack = eap->cstack;
if (reanimate) {
// Undo the return.
- current_funccal->returned = false;
+ current_funccal->fc_returned = false;
}
// Cleanup (and deactivate) conditionals, but stop when a try conditional
// not in its finally clause (which then is to be executed next) is found.
// In this case, make the ":return" pending for execution at the ":endtry".
// Otherwise, return normally.
- idx = cleanup_conditionals(eap->cstack, 0, true);
+ int idx = cleanup_conditionals(eap->cstack, 0, true);
if (idx >= 0) {
cstack->cs_pending[idx] = CSTP_RETURN;
@@ -3155,8 +3424,8 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv)
// When undoing a return in order to make it pending, get the stored
// return rettv.
if (reanimate) {
- assert(current_funccal->rettv);
- rettv = current_funccal->rettv;
+ assert(current_funccal->fc_rettv);
+ rettv = current_funccal->fc_rettv;
}
if (rettv != NULL) {
@@ -3171,20 +3440,20 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv)
// The pending return value could be overwritten by a ":return"
// without argument in a finally clause; reset the default
// return value.
- current_funccal->rettv->v_type = VAR_NUMBER;
- current_funccal->rettv->vval.v_number = 0;
+ current_funccal->fc_rettv->v_type = VAR_NUMBER;
+ current_funccal->fc_rettv->vval.v_number = 0;
}
}
report_make_pending(CSTP_RETURN, rettv);
} else {
- current_funccal->returned = true;
+ current_funccal->fc_returned = true;
// If the return is carried out now, store the return value. For
// a return immediately after reanimation, the value is already
// there.
if (!reanimate && rettv != NULL) {
- tv_clear(current_funccal->rettv);
- *current_funccal->rettv = *(typval_T *)rettv;
+ tv_clear(current_funccal->fc_rettv);
+ *current_funccal->fc_rettv = *(typval_T *)rettv;
if (!is_cmd) {
xfree(rettv);
}
@@ -3208,7 +3477,7 @@ char *get_return_cmd(void *rettv)
s = "";
}
- STRCPY(IObuff, ":return ");
+ xstrlcpy(IObuff, ":return ", IOSIZE);
xstrlcpy(IObuff + 8, s, IOSIZE - 8);
if (strlen(s) + 8 >= IOSIZE) {
STRCPY(IObuff + IOSIZE - 4, "...");
@@ -3224,34 +3493,33 @@ char *get_return_cmd(void *rettv)
char *get_func_line(int c, void *cookie, int indent, bool do_concat)
{
funccall_T *fcp = (funccall_T *)cookie;
- ufunc_T *fp = fcp->func;
+ ufunc_T *fp = fcp->fc_func;
char *retval;
- garray_T *gap; // growarray with function lines
// If breakpoints have been added/deleted need to check for it.
- if (fcp->dbg_tick != debug_tick) {
- fcp->breakpoint = dbg_find_breakpoint(false, (char *)fp->uf_name, SOURCING_LNUM);
- fcp->dbg_tick = debug_tick;
+ if (fcp->fc_dbg_tick != debug_tick) {
+ fcp->fc_breakpoint = dbg_find_breakpoint(false, fp->uf_name, SOURCING_LNUM);
+ fcp->fc_dbg_tick = debug_tick;
}
if (do_profiling == PROF_YES) {
func_line_end(cookie);
}
- gap = &fp->uf_lines;
+ garray_T *gap = &fp->uf_lines; // growarray with function lines
if (((fp->uf_flags & FC_ABORT) && did_emsg && !aborted_in_try())
- || fcp->returned) {
+ || fcp->fc_returned) {
retval = NULL;
} else {
// Skip NULL lines (continuation lines).
- while (fcp->linenr < gap->ga_len
- && ((char **)(gap->ga_data))[fcp->linenr] == NULL) {
- fcp->linenr++;
+ while (fcp->fc_linenr < gap->ga_len
+ && ((char **)(gap->ga_data))[fcp->fc_linenr] == NULL) {
+ fcp->fc_linenr++;
}
- if (fcp->linenr >= gap->ga_len) {
+ if (fcp->fc_linenr >= gap->ga_len) {
retval = NULL;
} else {
- retval = xstrdup(((char **)(gap->ga_data))[fcp->linenr++]);
- SOURCING_LNUM = fcp->linenr;
+ retval = xstrdup(((char **)(gap->ga_data))[fcp->fc_linenr++]);
+ SOURCING_LNUM = fcp->fc_linenr;
if (do_profiling == PROF_YES) {
func_line_start(cookie);
}
@@ -3259,11 +3527,11 @@ char *get_func_line(int c, void *cookie, int indent, bool do_concat)
}
// Did we encounter a breakpoint?
- if (fcp->breakpoint != 0 && fcp->breakpoint <= SOURCING_LNUM) {
- dbg_breakpoint((char *)fp->uf_name, SOURCING_LNUM);
+ if (fcp->fc_breakpoint != 0 && fcp->fc_breakpoint <= SOURCING_LNUM) {
+ dbg_breakpoint(fp->uf_name, SOURCING_LNUM);
// Find next breakpoint.
- fcp->breakpoint = dbg_find_breakpoint(false, (char *)fp->uf_name, SOURCING_LNUM);
- fcp->dbg_tick = debug_tick;
+ fcp->fc_breakpoint = dbg_find_breakpoint(false, fp->uf_name, SOURCING_LNUM);
+ fcp->fc_dbg_tick = debug_tick;
}
return retval;
@@ -3277,21 +3545,20 @@ int func_has_ended(void *cookie)
// Ignore the "abort" flag if the abortion behavior has been changed due to
// an error inside a try conditional.
- return ((fcp->func->uf_flags & FC_ABORT) && did_emsg && !aborted_in_try())
- || fcp->returned;
+ return ((fcp->fc_func->uf_flags & FC_ABORT) && did_emsg && !aborted_in_try())
+ || fcp->fc_returned;
}
/// @return true if cookie indicates a function which "abort"s on errors.
int func_has_abort(void *cookie)
{
- return ((funccall_T *)cookie)->func->uf_flags & FC_ABORT;
+ return ((funccall_T *)cookie)->fc_func->uf_flags & FC_ABORT;
}
/// Turn "dict.Func" into a partial for "Func" bound to "dict".
/// Changes "rettv" in-place.
void make_partial(dict_T *const selfdict, typval_T *const rettv)
{
- char *fname;
char *tofree = NULL;
ufunc_T *fp;
char fname_buf[FLEN_FIXED + 1];
@@ -3300,9 +3567,9 @@ void make_partial(dict_T *const selfdict, typval_T *const rettv)
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;
+ char *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);
@@ -3321,7 +3588,6 @@ void make_partial(dict_T *const selfdict, typval_T *const rettv)
pt->pt_name = rettv->vval.v_string;
} else {
partial_T *ret_pt = rettv->vval.v_partial;
- int i;
// Partial: copy the function name, use selfdict and copy
// args. Can't take over name or args, the partial might
@@ -3337,7 +3603,7 @@ void make_partial(dict_T *const selfdict, typval_T *const rettv)
size_t arg_size = sizeof(typval_T) * (size_t)ret_pt->pt_argc;
pt->pt_argv = (typval_T *)xmalloc(arg_size);
pt->pt_argc = ret_pt->pt_argc;
- for (i = 0; i < pt->pt_argc; i++) {
+ for (int i = 0; i < pt->pt_argc; i++) {
tv_copy(&ret_pt->pt_argv[i], &pt->pt_argv[i]);
}
}
@@ -3351,31 +3617,31 @@ void make_partial(dict_T *const selfdict, typval_T *const rettv)
/// @return the name of the executed function.
char *func_name(void *cookie)
{
- return ((funccall_T *)cookie)->func->uf_name;
+ return ((funccall_T *)cookie)->fc_func->uf_name;
}
/// @return the address holding the next breakpoint line for a funccall cookie.
linenr_T *func_breakpoint(void *cookie)
{
- return &((funccall_T *)cookie)->breakpoint;
+ return &((funccall_T *)cookie)->fc_breakpoint;
}
/// @return the address holding the debug tick for a funccall cookie.
int *func_dbg_tick(void *cookie)
{
- return &((funccall_T *)cookie)->dbg_tick;
+ return &((funccall_T *)cookie)->fc_dbg_tick;
}
/// @return the nesting level for a funccall cookie.
int func_level(void *cookie)
{
- return ((funccall_T *)cookie)->level;
+ return ((funccall_T *)cookie)->fc_level;
}
/// @return true when a function was ended by a ":return" command.
int current_func_returned(void)
{
- return current_funccal->returned;
+ return current_funccal->fc_returned;
}
bool free_unref_funccal(int copyID, int testing)
@@ -3386,12 +3652,12 @@ bool free_unref_funccal(int copyID, int testing)
for (funccall_T **pfc = &previous_funccal; *pfc != NULL;) {
if (can_free_funccal(*pfc, copyID)) {
funccall_T *fc = *pfc;
- *pfc = fc->caller;
+ *pfc = fc->fc_caller;
free_funccal_contents(fc);
did_free = true;
did_free_funccal = true;
} else {
- pfc = &(*pfc)->caller;
+ pfc = &(*pfc)->fc_caller;
}
}
if (did_free_funccal) {
@@ -3408,7 +3674,7 @@ funccall_T *get_funccal(void)
funccall_T *funccal = current_funccal;
if (debug_backtrace_level > 0) {
for (int i = 0; i < debug_backtrace_level; i++) {
- funccall_T *temp_funccal = funccal->caller;
+ funccall_T *temp_funccal = funccal->fc_caller;
if (temp_funccal) {
funccal = temp_funccal;
} else {
@@ -3428,7 +3694,7 @@ hashtab_T *get_funccal_local_ht(void)
if (current_funccal == NULL) {
return NULL;
}
- return &get_funccal()->l_vars.dv_hashtab;
+ return &get_funccal()->fc_l_vars.dv_hashtab;
}
/// @return the l: scope variable or
@@ -3438,7 +3704,7 @@ dictitem_T *get_funccal_local_var(void)
if (current_funccal == NULL) {
return NULL;
}
- return (dictitem_T *)&get_funccal()->l_vars_var;
+ return (dictitem_T *)&get_funccal()->fc_l_vars_var;
}
/// @return the hashtable used for argument in the current funccal or
@@ -3448,7 +3714,7 @@ hashtab_T *get_funccal_args_ht(void)
if (current_funccal == NULL) {
return NULL;
}
- return &get_funccal()->l_avars.dv_hashtab;
+ return &get_funccal()->fc_l_avars.dv_hashtab;
}
/// @return the a: scope variable or
@@ -3458,14 +3724,14 @@ dictitem_T *get_funccal_args_var(void)
if (current_funccal == NULL) {
return NULL;
}
- return (dictitem_T *)&current_funccal->l_avars_var;
+ return (dictitem_T *)&current_funccal->fc_l_avars_var;
}
/// List function variables, if there is a function.
void list_func_vars(int *first)
{
if (current_funccal != NULL) {
- list_hashtable_vars(&current_funccal->l_vars.dv_hashtab, "l:", false,
+ list_hashtable_vars(&current_funccal->fc_l_vars.dv_hashtab, "l:", false,
first);
}
}
@@ -3474,8 +3740,8 @@ void list_func_vars(int *first)
/// funccal, return the dict that contains it. Otherwise return NULL.
dict_T *get_current_funccal_dict(hashtab_T *ht)
{
- if (current_funccal != NULL && ht == &current_funccal->l_vars.dv_hashtab) {
- return &current_funccal->l_vars;
+ if (current_funccal != NULL && ht == &current_funccal->fc_l_vars.dv_hashtab) {
+ return &current_funccal->fc_l_vars;
}
return NULL;
}
@@ -3483,7 +3749,7 @@ dict_T *get_current_funccal_dict(hashtab_T *ht)
/// 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) {
+ if (current_funccal == NULL || current_funccal->fc_func->uf_scoped == NULL) {
return NULL;
}
@@ -3493,7 +3759,7 @@ hashitem_T *find_hi_in_scoped_ht(const char *name, hashtab_T **pht)
const char *varname;
// Search in parent scope which is possible to reference from lambda
- current_funccal = current_funccal->func->uf_scoped;
+ current_funccal = current_funccal->fc_func->uf_scoped;
while (current_funccal != NULL) {
hashtab_T *ht = find_var_ht(name, namelen, &varname);
if (ht != NULL && *varname != NUL) {
@@ -3503,10 +3769,10 @@ hashitem_T *find_hi_in_scoped_ht(const char *name, hashtab_T **pht)
break;
}
}
- if (current_funccal == current_funccal->func->uf_scoped) {
+ if (current_funccal == current_funccal->fc_func->uf_scoped) {
break;
}
- current_funccal = current_funccal->func->uf_scoped;
+ current_funccal = current_funccal->fc_func->uf_scoped;
}
current_funccal = old_current_funccal;
@@ -3516,7 +3782,7 @@ hashitem_T *find_hi_in_scoped_ht(const char *name, hashtab_T **pht)
/// 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) {
+ if (current_funccal == NULL || current_funccal->fc_func->uf_scoped == NULL) {
return NULL;
}
@@ -3525,7 +3791,7 @@ dictitem_T *find_var_in_scoped_ht(const char *name, const size_t namelen, int no
const char *varname;
// Search in parent scope which is possible to reference from lambda
- current_funccal = current_funccal->func->uf_scoped;
+ current_funccal = current_funccal->fc_func->uf_scoped;
while (current_funccal) {
hashtab_T *ht = find_var_ht(name, namelen, &varname);
if (ht != NULL && *varname != NUL) {
@@ -3535,10 +3801,10 @@ dictitem_T *find_var_in_scoped_ht(const char *name, const size_t namelen, int no
break;
}
}
- if (current_funccal == current_funccal->func->uf_scoped) {
+ if (current_funccal == current_funccal->fc_func->uf_scoped) {
break;
}
- current_funccal = current_funccal->func->uf_scoped;
+ current_funccal = current_funccal->fc_func->uf_scoped;
}
current_funccal = old_current_funccal;
@@ -3549,11 +3815,11 @@ dictitem_T *find_var_in_scoped_ht(const char *name, const size_t namelen, int no
bool set_ref_in_previous_funccal(int copyID)
{
for (funccall_T *fc = previous_funccal; fc != NULL;
- fc = fc->caller) {
+ fc = fc->fc_caller) {
fc->fc_copyID = copyID + 1;
- if (set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1, NULL)
- || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1, NULL)
- || set_ref_in_list(&fc->l_varlist, copyID + 1, NULL)) {
+ if (set_ref_in_ht(&fc->fc_l_vars.dv_hashtab, copyID + 1, NULL)
+ || set_ref_in_ht(&fc->fc_l_avars.dv_hashtab, copyID + 1, NULL)
+ || set_ref_in_list(&fc->fc_l_varlist, copyID + 1, NULL)) {
return true;
}
}
@@ -3564,10 +3830,10 @@ static bool set_ref_in_funccal(funccall_T *fc, int copyID)
{
if (fc->fc_copyID != copyID) {
fc->fc_copyID = copyID;
- if (set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL)
- || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL)
- || set_ref_in_list(&fc->l_varlist, copyID, NULL)
- || set_ref_in_func(NULL, fc->func, copyID)) {
+ if (set_ref_in_ht(&fc->fc_l_vars.dv_hashtab, copyID, NULL)
+ || set_ref_in_ht(&fc->fc_l_avars.dv_hashtab, copyID, NULL)
+ || set_ref_in_list(&fc->fc_l_varlist, copyID, NULL)
+ || set_ref_in_func(NULL, fc->fc_func, copyID)) {
return true;
}
}
@@ -3578,7 +3844,7 @@ static bool set_ref_in_funccal(funccall_T *fc, int copyID)
bool set_ref_in_call_stack(int copyID)
{
for (funccall_T *fc = current_funccal; fc != NULL;
- fc = fc->caller) {
+ fc = fc->fc_caller) {
if (set_ref_in_funccal(fc, copyID)) {
return true;
}
@@ -3588,7 +3854,7 @@ bool set_ref_in_call_stack(int copyID)
for (funccal_entry_T *entry = funccal_stack; entry != NULL;
entry = entry->next) {
for (funccall_T *fc = entry->top_funccal; fc != NULL;
- fc = fc->caller) {
+ fc = fc->fc_caller) {
if (set_ref_in_funccal(fc, copyID)) {
return true;
}
@@ -3601,15 +3867,11 @@ bool set_ref_in_call_stack(int copyID)
/// Set "copyID" in all functions available by name.
bool set_ref_in_functions(int copyID)
{
- int todo;
- hashitem_T *hi = NULL;
- ufunc_T *fp;
-
- todo = (int)func_hashtab.ht_used;
- for (hi = func_hashtab.ht_array; todo > 0 && !got_int; hi++) {
+ int todo = (int)func_hashtab.ht_used;
+ for (hashitem_T *hi = func_hashtab.ht_array; todo > 0 && !got_int; hi++) {
if (!HASHITEM_EMPTY(hi)) {
todo--;
- fp = HI2UF(hi);
+ ufunc_T *fp = HI2UF(hi);
if (!func_name_refcount(fp->uf_name)
&& set_ref_in_func(NULL, fp, copyID)) {
return true;
@@ -3639,22 +3901,20 @@ bool set_ref_in_func_args(int copyID)
bool set_ref_in_func(char *name, ufunc_T *fp_in, int copyID)
{
ufunc_T *fp = fp_in;
- funccall_T *fc;
int error = FCERR_NONE;
char fname_buf[FLEN_FIXED + 1];
char *tofree = NULL;
- char *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);
+ char *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) {
+ for (funccall_T *fc = fp->uf_scoped; fc != NULL; fc = fc->fc_func->uf_scoped) {
abort = abort || set_ref_in_funccal(fc, copyID);
}
}
diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h
index c8583f232c..8050caab2b 100644
--- a/src/nvim/eval/userfunc.h
+++ b/src/nvim/eval/userfunc.h
@@ -1,22 +1,21 @@
-#ifndef NVIM_EVAL_USERFUNC_H
-#define NVIM_EVAL_USERFUNC_H
+#pragma once
#include <stdbool.h>
#include <stddef.h>
-#include "nvim/eval/typval.h"
+#include "nvim/cmdexpand_defs.h" // IWYU pragma: keep
+#include "nvim/eval.h"
#include "nvim/eval/typval_defs.h"
-#include "nvim/ex_cmds_defs.h"
-#include "nvim/garray.h"
-#include "nvim/hashtab.h"
-#include "nvim/pos.h"
-#include "nvim/types.h"
+#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep
+#include "nvim/hashtab_defs.h" // IWYU pragma: keep
+#include "nvim/pos_defs.h"
+#include "nvim/types_defs.h" // IWYU pragma: keep
struct funccal_entry;
// From user function to hashitem and back.
#define UF2HIKEY(fp) ((fp)->uf_name)
-#define HIKEY2UF(p) ((ufunc_T *)(p - offsetof(ufunc_T, uf_name)))
+#define HIKEY2UF(p) ((ufunc_T *)((p) - offsetof(ufunc_T, uf_name)))
#define HI2UF(hi) HIKEY2UF((hi)->hi_key)
// flags used in uf_flags
@@ -27,10 +26,10 @@ struct funccal_entry;
#define FC_DELETED 0x10 // :delfunction used while uf_refcount > 0
#define FC_REMOVED 0x20 // function redefined while uf_refcount > 0
#define FC_SANDBOX 0x40 // function defined in the sandbox
-#define FC_DEAD 0x80 // function kept only for reference to dfunc
-#define FC_EXPORT 0x100 // "export def Func()"
+// #define FC_DEAD 0x80 // function kept only for reference to dfunc
+// #define FC_EXPORT 0x100 // "export def Func()"
#define FC_NOARGS 0x200 // no a: variables in lambda
-#define FC_VIM9 0x400 // defined in vim9 script file
+// #define FC_VIM9 0x400 // defined in vim9 script file
#define FC_LUAREF 0x800 // luaref callback
/// Structure used by trans_function_name()
@@ -74,6 +73,8 @@ typedef struct {
partial_T *fe_partial; ///< for extra arguments
dict_T *fe_selfdict; ///< Dictionary for "self"
typval_T *fe_basetv; ///< base for base->method()
+ bool fe_found_var; ///< if the function is not found then give an
+ ///< error that a variable is not callable.
} funcexe_T;
#define FUNCEXE_INIT (funcexe_T) { \
@@ -85,6 +86,7 @@ typedef struct {
.fe_partial = NULL, \
.fe_selfdict = NULL, \
.fe_basetv = NULL, \
+ .fe_found_var = false, \
}
#define FUNCARG(fp, j) ((char **)(fp->uf_args.ga_data))[j]
@@ -93,4 +95,3 @@ typedef struct {
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/userfunc.h.generated.h"
#endif
-#endif // NVIM_EVAL_USERFUNC_H
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index 58fb2211fc..10a2f40800 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -1,6 +1,3 @@
-// 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/vars.c: functions for dealing with variables
#include <assert.h>
@@ -8,9 +5,11 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
+#include <sys/types.h>
-#include "nvim/ascii.h"
+#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
@@ -19,7 +18,6 @@
#include "nvim/eval/encode.h"
#include "nvim/eval/funcs.h"
#include "nvim/eval/typval.h"
-#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
#include "nvim/eval/window.h"
@@ -27,19 +25,23 @@
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
+#include "nvim/func_attr.h"
+#include "nvim/garray.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/hashtab.h"
-#include "nvim/macros.h"
+#include "nvim/macros_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/option_defs.h"
+#include "nvim/option_vars.h"
#include "nvim/os/os.h"
#include "nvim/search.h"
#include "nvim/strings.h"
-#include "nvim/types.h"
-#include "nvim/vim.h"
+#include "nvim/types_defs.h"
+#include "nvim/vim_defs.h"
#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -50,8 +52,102 @@
#define DICT_MAXNEST 100 // maximum nesting of lists and dicts
-static char *e_letunexp = N_("E18: Unexpected characters in :let");
-static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s");
+static const char *e_letunexp = N_("E18: Unexpected characters in :let");
+static const char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s");
+static const char e_setting_v_str_to_value_with_wrong_type[]
+ = N_("E963: Setting v:%s to value with wrong type");
+static const char e_cannot_use_heredoc_here[]
+ = N_("E991: Cannot use =<< here");
+
+/// Evaluate one Vim expression {expr} in string "p" and append the
+/// resulting string to "gap". "p" points to the opening "{".
+/// When "evaluate" is false only skip over the expression.
+/// Return a pointer to the character after "}", NULL for an error.
+char *eval_one_expr_in_str(char *p, garray_T *gap, bool evaluate)
+{
+ char *block_start = skipwhite(p + 1); // skip the opening {
+ char *block_end = block_start;
+
+ if (*block_start == NUL) {
+ semsg(_(e_missing_close_curly_str), p);
+ return NULL;
+ }
+ if (skip_expr(&block_end, NULL) == FAIL) {
+ return NULL;
+ }
+ block_end = skipwhite(block_end);
+ if (*block_end != '}') {
+ semsg(_(e_missing_close_curly_str), p);
+ return NULL;
+ }
+ if (evaluate) {
+ *block_end = NUL;
+ char *expr_val = eval_to_string(block_start, true);
+ *block_end = '}';
+ if (expr_val == NULL) {
+ return NULL;
+ }
+ ga_concat(gap, expr_val);
+ xfree(expr_val);
+ }
+
+ return block_end + 1;
+}
+
+/// Evaluate all the Vim expressions {expr} in "str" and return the resulting
+/// string in allocated memory. "{{" is reduced to "{" and "}}" to "}".
+/// Used for a heredoc assignment.
+/// Returns NULL for an error.
+char *eval_all_expr_in_str(char *str)
+{
+ garray_T ga;
+ ga_init(&ga, 1, 80);
+ char *p = str;
+
+ while (*p != NUL) {
+ bool escaped_brace = false;
+
+ // Look for a block start.
+ char *lit_start = p;
+ while (*p != '{' && *p != '}' && *p != NUL) {
+ p++;
+ }
+
+ if (*p != NUL && *p == p[1]) {
+ // Escaped brace, unescape and continue.
+ // Include the brace in the literal string.
+ p++;
+ escaped_brace = true;
+ } else if (*p == '}') {
+ semsg(_(e_stray_closing_curly_str), str);
+ ga_clear(&ga);
+ return NULL;
+ }
+
+ // Append the literal part.
+ ga_concat_len(&ga, lit_start, (size_t)(p - lit_start));
+
+ if (*p == NUL) {
+ break;
+ }
+
+ if (escaped_brace) {
+ // Skip the second brace.
+ p++;
+ continue;
+ }
+
+ // Evaluate the expression and append the result.
+ p = eval_one_expr_in_str(p, &ga, true);
+ if (p == NULL) {
+ ga_clear(&ga);
+ return NULL;
+ }
+ }
+ ga_append(&ga, NUL);
+
+ return ga.ga_data;
+}
/// Get a list of lines from a HERE document. The here document is a list of
/// lines surrounded by a marker.
@@ -65,64 +161,91 @@ static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s");
/// marker, then the leading indentation before the lines (matching the
/// indentation in the 'cmd' line) is stripped.
///
-/// @return a List with {lines} or NULL.
-static list_T *heredoc_get(exarg_T *eap, char *cmd)
+/// When getting lines for an embedded script (e.g. python, lua, perl, ruby,
+/// tcl, mzscheme), "script_get" is set to true. In this case, if the marker is
+/// missing, then '.' is accepted as a marker.
+///
+/// @return a List with {lines} or NULL on failure.
+list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get)
{
char *marker;
- char *p;
int marker_indent_len = 0;
int text_indent_len = 0;
char *text_indent = NULL;
+ char dot[] = ".";
if (eap->getline == NULL) {
- emsg(_("E991: cannot use =<< here"));
+ emsg(_(e_cannot_use_heredoc_here));
return NULL;
}
// Check for the optional 'trim' word before the marker
cmd = skipwhite(cmd);
- if (strncmp(cmd, "trim", 4) == 0
- && (cmd[4] == NUL || ascii_iswhite(cmd[4]))) {
- cmd = skipwhite(cmd + 4);
-
- // Trim the indentation from all the lines in the here document.
- // The amount of indentation trimmed is the same as the indentation of
- // the first line after the :let command line. To find the end marker
- // the indent of the :let command line is trimmed.
- p = *eap->cmdlinep;
- while (ascii_iswhite(*p)) {
- p++;
- marker_indent_len++;
+ bool evalstr = false;
+ bool eval_failed = false;
+ while (true) {
+ if (strncmp(cmd, "trim", 4) == 0
+ && (cmd[4] == NUL || ascii_iswhite(cmd[4]))) {
+ cmd = skipwhite(cmd + 4);
+
+ // Trim the indentation from all the lines in the here document.
+ // The amount of indentation trimmed is the same as the indentation
+ // of the first line after the :let command line. To find the end
+ // marker the indent of the :let command line is trimmed.
+ char *p = *eap->cmdlinep;
+ while (ascii_iswhite(*p)) {
+ p++;
+ marker_indent_len++;
+ }
+ text_indent_len = -1;
+
+ continue;
+ }
+ if (strncmp(cmd, "eval", 4) == 0
+ && (cmd[4] == NUL || ascii_iswhite(cmd[4]))) {
+ cmd = skipwhite(cmd + 4);
+ evalstr = true;
+ continue;
}
- text_indent_len = -1;
+ break;
}
// The marker is the next word.
if (*cmd != NUL && *cmd != '"') {
marker = skipwhite(cmd);
- p = skiptowhite(marker);
+ char *p = skiptowhite(marker);
if (*skipwhite(p) != NUL && *skipwhite(p) != '"') {
semsg(_(e_trailing_arg), p);
return NULL;
}
*p = NUL;
- if (islower((uint8_t)(*marker))) {
+ if (!script_get && islower((uint8_t)(*marker))) {
emsg(_("E221: Marker cannot start with lower case letter"));
return NULL;
}
} else {
- emsg(_("E172: Missing marker"));
- return NULL;
+ // When getting lines for an embedded script, if the marker is missing,
+ // accept '.' as the marker.
+ if (script_get) {
+ marker = dot;
+ } else {
+ emsg(_("E172: Missing marker"));
+ return NULL;
+ }
}
+ char *theline = NULL;
list_T *l = tv_list_alloc(0);
- for (;;) {
+ while (true) {
int mi = 0;
int ti = 0;
- char *theline = eap->getline(NUL, eap->cookie, 0, false);
+ xfree(theline);
+ theline = eap->getline(NUL, eap->cookie, 0, false);
if (theline == NULL) {
- semsg(_("E990: Missing end marker '%s'"), marker);
+ if (!script_get) {
+ semsg(_("E990: Missing end marker '%s'"), marker);
+ }
break;
}
@@ -133,18 +256,24 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd)
mi = marker_indent_len;
}
if (strcmp(marker, theline + mi) == 0) {
- xfree(theline);
break;
}
+
+ // If expression evaluation failed in the heredoc, then skip till the
+ // end marker.
+ if (eval_failed) {
+ continue;
+ }
+
if (text_indent_len == -1 && *theline != NUL) {
// set the text indent from the first line.
- p = theline;
+ char *p = theline;
text_indent_len = 0;
while (ascii_iswhite(*p)) {
p++;
text_indent_len++;
}
- text_indent = xstrnsave(theline, (size_t)text_indent_len);
+ text_indent = xmemdupz(theline, (size_t)text_indent_len);
}
// with "trim": skip the indent matching the first line
if (text_indent != NULL) {
@@ -155,11 +284,28 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd)
}
}
- tv_list_append_string(l, theline + ti, -1);
- xfree(theline);
+ char *str = theline + ti;
+ if (evalstr && !eap->skip) {
+ str = eval_all_expr_in_str(str);
+ if (str == NULL) {
+ // expression evaluation failed
+ eval_failed = true;
+ continue;
+ }
+ xfree(theline);
+ theline = str;
+ }
+
+ tv_list_append_string(l, str, -1);
}
+ xfree(theline);
xfree(text_indent);
+ if (eval_failed) {
+ // expression evaluation in the heredoc failed
+ tv_list_free(l);
+ return NULL;
+ }
return l;
}
@@ -175,32 +321,23 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd)
/// ":let var ..= expr" assignment command.
/// ":let [var1, var2] = expr" unpack list.
/// ":let [name, ..., ; lastname] = expr" unpack list.
-void ex_let(exarg_T *eap)
-{
- ex_let_const(eap, false);
-}
-
+///
/// ":cons[t] var = expr1" define constant
/// ":cons[t] [name1, name2, ...] = expr1" define constants unpacking list
/// ":cons[t] [name, ..., ; lastname] = expr" define constants unpacking list
-void ex_const(exarg_T *eap)
-{
- ex_let_const(eap, true);
-}
-
-static void ex_let_const(exarg_T *eap, const bool is_const)
+void ex_let(exarg_T *eap)
{
+ const bool is_const = eap->cmdidx == CMD_const;
char *arg = eap->arg;
char *expr = NULL;
typval_T rettv;
- int i;
int var_count = 0;
int semicolon = 0;
char op[2];
- char *argend;
+ const char *argend;
int first = true;
- argend = (char *)skip_var_list(arg, &var_count, &semicolon);
+ argend = skip_var_list(arg, &var_count, &semicolon);
if (argend == NULL) {
return;
}
@@ -208,14 +345,16 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
argend--;
}
expr = skipwhite(argend);
- if (*expr != '=' && !((vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL
- && expr[1] == '=') || strncmp(expr, "..=", 3) == 0)) {
+ bool concat = strncmp(expr, "..=", 3) == 0;
+ bool has_assign = *expr == '=' || (vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL
+ && expr[1] == '=');
+ if (!has_assign && !concat) {
// ":let" without "=": list variables
if (*arg == '[') {
emsg(_(e_invarg));
} else if (!ends_excmd(*arg)) {
// ":let var1 var2"
- arg = (char *)list_arg_vars(eap, (const char *)arg, &first);
+ arg = (char *)list_arg_vars(eap, arg, &first);
} else if (!eap->skip) {
// ":let"
list_glob_vars(&first);
@@ -227,50 +366,58 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
list_vim_vars(&first);
}
eap->nextcmd = check_nextcmd(arg);
- } else if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') {
+ return;
+ }
+
+ if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') {
// HERE document
- list_T *l = heredoc_get(eap, expr + 3);
+ list_T *l = heredoc_get(eap, expr + 3, false);
if (l != NULL) {
tv_list_set_ret(&rettv, l);
if (!eap->skip) {
op[0] = '=';
op[1] = NUL;
- (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count,
- is_const, (char *)op);
+ (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op);
}
tv_clear(&rettv);
}
- } else {
- op[0] = '=';
- op[1] = NUL;
- if (*expr != '=') {
- if (vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL) {
- op[0] = *expr; // +=, -=, *=, /=, %= or .=
- if (expr[0] == '.' && expr[1] == '.') { // ..=
- expr++;
- }
- }
- expr += 2;
- } else {
- expr += 1;
- }
+ return;
+ }
- expr = skipwhite(expr);
+ rettv.v_type = VAR_UNKNOWN;
- if (eap->skip) {
- emsg_skip++;
- }
- i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip);
- if (eap->skip) {
- if (i != FAIL) {
- tv_clear(&rettv);
+ op[0] = '=';
+ op[1] = NUL;
+ if (*expr != '=') {
+ if (vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL) {
+ op[0] = *expr; // +=, -=, *=, /=, %= or .=
+ if (expr[0] == '.' && expr[1] == '.') { // ..=
+ expr++;
}
- emsg_skip--;
- } else if (i != FAIL) {
- (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count,
- is_const, (char *)op);
- tv_clear(&rettv);
}
+ expr += 2;
+ } else {
+ expr += 1;
+ }
+
+ expr = skipwhite(expr);
+
+ if (eap->skip) {
+ emsg_skip++;
+ }
+ evalarg_T evalarg;
+ fill_evalarg_from_eap(&evalarg, eap, eap->skip);
+ int eval_res = eval0(expr, &rettv, eap, &evalarg);
+ if (eap->skip) {
+ emsg_skip--;
+ }
+ clear_evalarg(&evalarg, eap);
+
+ if (!eap->skip && eval_res != FAIL) {
+ (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op);
+ }
+ if (eval_res != FAIL) {
+ tv_clear(&rettv);
}
}
@@ -369,15 +516,13 @@ int ex_let_vars(char *arg_start, typval_T *tv, int copy, int semicolon, int var_
/// @return NULL for an error.
const char *skip_var_list(const char *arg, int *var_count, int *semicolon)
{
- const char *p;
- const char *s;
-
if (*arg == '[') {
+ const char *s;
// "[var, var]": find the matching ']'.
- p = arg;
- for (;;) {
+ const char *p = arg;
+ while (true) {
p = skipwhite(p + 1); // skip whites after '[', ';' or ','
- s = skip_var_one((char *)p);
+ s = skip_var_one(p);
if (s == p) {
semsg(_(e_invarg2), p);
return NULL;
@@ -400,7 +545,7 @@ const char *skip_var_list(const char *arg, int *var_count, int *semicolon)
}
return p + 1;
}
- return skip_var_one((char *)arg);
+ return skip_var_one(arg);
}
/// Skip one (assignable) variable name, including @r, $VAR, &option, d.key,
@@ -410,8 +555,8 @@ static const char *skip_var_one(const char *arg)
if (*arg == '@' && arg[1] != NUL) {
return arg + 1 + utfc_ptr2len(arg + 1);
}
- return (char *)find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg,
- NULL, NULL, FNE_INCL_BR | FNE_CHECK_START);
+ return find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg,
+ NULL, NULL, FNE_INCL_BR | FNE_CHECK_START);
}
/// List variables for hashtab "ht" with prefix "prefix".
@@ -432,7 +577,7 @@ void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, int *firs
// apply :filter /pat/ to variable name
xstrlcpy(buf, prefix, IOSIZE);
- xstrlcat(buf, (char *)di->di_key, IOSIZE);
+ xstrlcat(buf, di->di_key, IOSIZE);
if (message_filtered(buf)) {
continue;
}
@@ -504,13 +649,12 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
if (tofree != NULL) {
name = tofree;
}
- if (get_var_tv(name, len, &tv, NULL, true, false)
- == FAIL) {
+ if (eval_variable(name, len, &tv, NULL, true, false) == FAIL) {
error = true;
} else {
// handle d.key, l[idx], f(expr)
const char *const arg_subsc = arg;
- if (handle_subscript(&arg, &tv, true, true, name, &name) == FAIL) {
+ if (handle_subscript(&arg, &tv, &EVALARG_EVALUATE, true) == FAIL) {
error = true;
} else {
if (arg == arg_subsc && len == 2 && name[1] == ':') {
@@ -553,12 +697,196 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
xfree(tofree);
}
- arg = (const char *)skipwhite(arg);
+ arg = skipwhite(arg);
}
return arg;
}
+/// Set an environment variable, part of ex_let_one().
+static char *ex_let_env(char *arg, typval_T *const tv, const bool is_const,
+ const char *const endchars, const char *const op)
+ FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (is_const) {
+ emsg(_("E996: Cannot lock an environment variable"));
+ return NULL;
+ }
+
+ // Find the end of the name.
+ char *arg_end = NULL;
+ arg++;
+ char *name = arg;
+ int len = get_env_len((const char **)&arg);
+ if (len == 0) {
+ semsg(_(e_invarg2), name - 1);
+ } else {
+ if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) {
+ semsg(_(e_letwrong), op);
+ } else if (endchars != NULL
+ && vim_strchr(endchars, (uint8_t)(*skipwhite(arg))) == NULL) {
+ emsg(_(e_letunexp));
+ } else if (!check_secure()) {
+ char *tofree = NULL;
+ const char c1 = name[len];
+ name[len] = NUL;
+ const char *p = tv_get_string_chk(tv);
+ if (p != NULL && op != NULL && *op == '.') {
+ char *s = vim_getenv(name);
+ if (s != NULL) {
+ tofree = concat_str(s, p);
+ p = tofree;
+ xfree(s);
+ }
+ }
+ if (p != NULL) {
+ vim_setenv_ext(name, p);
+ arg_end = arg;
+ }
+ name[len] = c1;
+ xfree(tofree);
+ }
+ }
+ return arg_end;
+}
+
+/// Set an option, part of ex_let_one().
+static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const,
+ const char *const endchars, const char *const op)
+ FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (is_const) {
+ emsg(_("E996: Cannot lock an option"));
+ return NULL;
+ }
+
+ // Find the end of the name.
+ char *arg_end = NULL;
+ int scope;
+ char *const p = (char *)find_option_end((const char **)&arg, &scope);
+ if (p == NULL
+ || (endchars != NULL
+ && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) {
+ emsg(_(e_letunexp));
+ return NULL;
+ }
+
+ const char c1 = *p;
+ *p = NUL;
+
+ uint32_t opt_p_flags;
+ bool hidden;
+ OptVal curval = get_option_value(arg, &opt_p_flags, scope, &hidden);
+ OptVal newval = NIL_OPTVAL;
+ if (curval.type == kOptValTypeNil && arg[0] != 't' && arg[1] != '_') {
+ semsg(_(e_unknown_option2), arg);
+ goto theend;
+ }
+ if (op != NULL && *op != '='
+ && ((curval.type != kOptValTypeString && *op == '.')
+ || (curval.type == kOptValTypeString && *op != '.'))) {
+ semsg(_(e_letwrong), op);
+ goto theend;
+ }
+
+ bool error;
+ newval = tv_to_optval(tv, arg, opt_p_flags, &error);
+ if (error) {
+ goto theend;
+ }
+
+ // Don't assume current and new values are of the same type in order to future-proof the code for
+ // when an option can have multiple types.
+ const bool is_num = ((curval.type == kOptValTypeNumber || curval.type == kOptValTypeBoolean)
+ && (newval.type == kOptValTypeNumber || newval.type == kOptValTypeBoolean));
+ const bool is_string = curval.type == kOptValTypeString && newval.type == kOptValTypeString;
+
+ if (op != NULL && *op != '=') {
+ if (!hidden && is_num) { // number or bool
+ OptInt cur_n = curval.type == kOptValTypeNumber ? curval.data.number : curval.data.boolean;
+ OptInt new_n = newval.type == kOptValTypeNumber ? newval.data.number : newval.data.boolean;
+
+ switch (*op) {
+ case '+':
+ new_n = cur_n + new_n; break;
+ case '-':
+ new_n = cur_n - new_n; break;
+ case '*':
+ new_n = cur_n * new_n; break;
+ case '/':
+ new_n = num_divide(cur_n, new_n); break;
+ case '%':
+ new_n = num_modulus(cur_n, new_n); break;
+ }
+
+ if (curval.type == kOptValTypeNumber) {
+ newval = NUMBER_OPTVAL(new_n);
+ } else {
+ newval = BOOLEAN_OPTVAL(TRISTATE_FROM_INT(new_n));
+ }
+ } else if (!hidden && is_string
+ && curval.data.string.data != NULL && newval.data.string.data != NULL) { // string
+ OptVal newval_old = newval;
+ newval = CSTR_AS_OPTVAL(concat_str(curval.data.string.data, newval.data.string.data));
+ optval_free(newval_old);
+ }
+ }
+
+ const char *err = set_option_value(arg, newval, scope);
+ arg_end = p;
+ if (err != NULL) {
+ emsg(_(err));
+ }
+
+theend:
+ *p = c1;
+ optval_free(curval);
+ optval_free(newval);
+ return arg_end;
+}
+
+/// Set a register, part of ex_let_one().
+static char *ex_let_register(char *arg, typval_T *const tv, const bool is_const,
+ const char *const endchars, const char *const op)
+ FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (is_const) {
+ emsg(_("E996: Cannot lock a register"));
+ return NULL;
+ }
+
+ char *arg_end = NULL;
+ arg++;
+
+ int regname = utf_ptr2char(arg);
+ int mblen = utf_ptr2len(arg);
+
+ if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) {
+ semsg(_(e_letwrong), op);
+ } else if (endchars != NULL
+ && vim_strchr(endchars, (uint8_t)(*skipwhite(arg + mblen))) == NULL) {
+ emsg(_(e_letunexp));
+ } else {
+ char *ptofree = NULL;
+ const char *p = tv_get_string_chk(tv);
+ if (p != NULL && op != NULL && *op == '.') {
+ char *s = get_reg_contents(*arg == '@' ? '"' : regname, kGRegExprSrc);
+ if (s != NULL) {
+ ptofree = concat_str(s, p);
+ p = ptofree;
+ xfree(s);
+ }
+ }
+ if (p != NULL) {
+ write_reg_contents(*arg == '@' ? '"' : regname,
+ p, (ssize_t)strlen(p), false);
+ arg_end = arg + mblen;
+ }
+ xfree(ptofree);
+ }
+ return arg_end;
+}
+
/// Set one item of `:let var = expr` or `:let [v1, v2] = list` to its value
///
/// @param[in] arg Start of the variable name.
@@ -575,181 +903,22 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT
{
char *arg_end = NULL;
- int len;
- // ":let $VAR = expr": Set environment variable.
if (*arg == '$') {
- if (is_const) {
- emsg(_("E996: Cannot lock an environment variable"));
- return NULL;
- }
- // Find the end of the name.
- arg++;
- char *name = arg;
- len = get_env_len((const char **)&arg);
- if (len == 0) {
- semsg(_(e_invarg2), name - 1);
- } else {
- if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) {
- semsg(_(e_letwrong), op);
- } else if (endchars != NULL
- && vim_strchr(endchars, (uint8_t)(*skipwhite(arg))) == NULL) {
- emsg(_(e_letunexp));
- } else if (!check_secure()) {
- char *tofree = NULL;
- const char c1 = name[len];
- name[len] = NUL;
- const char *p = tv_get_string_chk(tv);
- if (p != NULL && op != NULL && *op == '.') {
- char *s = vim_getenv(name);
- if (s != NULL) {
- tofree = concat_str(s, p);
- p = (const char *)tofree;
- xfree(s);
- }
- }
- if (p != NULL) {
- vim_setenv_ext(name, p);
- arg_end = arg;
- }
- name[len] = c1;
- xfree(tofree);
- }
- }
+ // ":let $VAR = expr": Set environment variable.
+ return ex_let_env(arg, tv, is_const, endchars, op);
+ } else if (*arg == '&') {
// ":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 == '&') {
- if (is_const) {
- emsg(_("E996: Cannot lock an option"));
- return NULL;
- }
- // Find the end of the name.
- int scope;
- char *const p = (char *)find_option_end((const char **)&arg, &scope);
- if (p == NULL
- || (endchars != NULL
- && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) {
- emsg(_(e_letunexp));
- } else {
- varnumber_T n = 0;
- getoption_T opt_type;
- long numval;
- char *stringval = NULL;
- const char *s = NULL;
- bool failed = false;
- uint32_t opt_p_flags;
- char *tofree = NULL;
-
- const char c1 = *p;
- *p = NUL;
-
- opt_type = get_option_value(arg, &numval, &stringval, &opt_p_flags, scope);
- if (opt_type == gov_bool
- || opt_type == gov_number
- || opt_type == gov_hidden_bool
- || opt_type == gov_hidden_number) {
- // number, possibly hidden
- n = (long)tv_get_number(tv);
- }
-
- if ((opt_p_flags & P_FUNC) && tv_is_func(*tv)) {
- // If the option can be set to a function reference or a lambda
- // and the passed value is a function reference, then convert it to
- // the name (string) of the function reference.
- s = tofree = encode_tv2string(tv, NULL);
- } else if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) {
- // Avoid setting a string option to the text "v:false" or similar.
- s = tv_get_string_chk(tv);
- }
-
- if (op != NULL && *op != '=') {
- if (((opt_type == gov_bool || opt_type == gov_number) && *op == '.')
- || (opt_type == gov_string && *op != '.')) {
- semsg(_(e_letwrong), op);
- failed = true; // don't set the value
- } else {
- // number or bool
- if (opt_type == gov_number || opt_type == gov_bool) {
- switch (*op) {
- case '+':
- n = numval + n; break;
- case '-':
- n = numval - n; break;
- case '*':
- n = numval * n; break;
- case '/':
- n = num_divide(numval, n); break;
- case '%':
- n = num_modulus(numval, n); break;
- }
- s = NULL;
- } else if (opt_type == gov_string && stringval != NULL && s != NULL) {
- // string
- char *const oldstringval = stringval;
- stringval = concat_str(stringval, s);
- xfree(oldstringval);
- s = stringval;
- }
- }
- }
-
- if (!failed) {
- if (opt_type != gov_string || s != NULL) {
- char *err = set_option_value(arg, n, s, scope);
- arg_end = p;
- if (err != NULL) {
- emsg(_(err));
- }
- } else {
- emsg(_(e_stringreq));
- }
- }
- *p = c1;
- xfree(stringval);
- xfree(tofree);
- }
- // ":let @r = expr": Set register contents.
+ return ex_let_option(arg, tv, is_const, endchars, op);
} else if (*arg == '@') {
- if (is_const) {
- emsg(_("E996: Cannot lock a register"));
- return NULL;
- }
- arg++;
-
- int regname = utf_ptr2char(arg);
- int mblen = utf_ptr2len(arg);
-
- if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) {
- semsg(_(e_letwrong), op);
- } else if (endchars != NULL
- && vim_strchr(endchars, (uint8_t)(*skipwhite(arg + mblen))) == NULL) {
- emsg(_(e_letunexp));
- } else {
- char *s;
-
- char *ptofree = NULL;
- const char *p = tv_get_string_chk(tv);
- if (p != NULL && op != NULL && *op == '.') {
- s = get_reg_contents(*arg == '@' ? '"' : regname, kGRegExprSrc);
- if (s != NULL) {
- ptofree = concat_str(s, p);
- p = (const char *)ptofree;
- xfree(s);
- }
- }
- if (p != NULL) {
- write_reg_contents(*arg == '@' ? '"' : regname,
- p, (ssize_t)strlen(p), false);
- arg_end = arg + mblen;
- }
- xfree(ptofree);
- }
+ // ":let @r = expr": Set register contents.
+ return ex_let_register(arg, tv, is_const, endchars, op);
+ } else if (eval_isnamec1(*arg) || *arg == '{') {
// ":let var = expr": Set internal variable.
// ":let {expr} = expr": Idem, name made with curly braces
- } else if (eval_isnamec1(*arg) || *arg == '{') {
lval_T lv;
-
char *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, (uint8_t)(*skipwhite(p))) == NULL) {
@@ -808,7 +977,7 @@ static void ex_unletlock(exarg_T *eap, char *argstart, int deep, ex_unletlock_ca
do {
if (*arg == '$') {
- lv.ll_name = (const char *)arg;
+ lv.ll_name = arg;
lv.ll_tv = NULL;
arg++;
if (get_env_len((const char **)&arg) == 0) {
@@ -866,10 +1035,9 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_
{
int forceit = eap->forceit;
int ret = OK;
- int cc;
if (lp->ll_tv == NULL) {
- cc = (uint8_t)(*name_end);
+ int cc = (uint8_t)(*name_end);
*name_end = NUL;
// Environment variable, normal name or expanded name.
@@ -891,57 +1059,57 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_
lp->ll_name_len))) {
return FAIL;
} else if (lp->ll_range) {
- assert(lp->ll_list != NULL);
- // Delete a range of List items.
- listitem_T *const first_li = lp->ll_li;
- listitem_T *last_li = first_li;
- for (;;) {
- listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li);
- if (value_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock,
- lp->ll_name,
- lp->ll_name_len)) {
- return false;
- }
- lp->ll_li = li;
- lp->ll_n1++;
- if (lp->ll_li == NULL || (!lp->ll_empty2 && lp->ll_n2 < lp->ll_n1)) {
- break;
- }
- last_li = lp->ll_li;
- }
- tv_list_remove_items(lp->ll_list, first_li, last_li);
+ tv_list_unlet_range(lp->ll_list, lp->ll_li, lp->ll_n1, !lp->ll_empty2, lp->ll_n2);
+ } else if (lp->ll_list != NULL) {
+ // unlet a List item.
+ tv_list_item_remove(lp->ll_list, lp->ll_li);
} else {
- if (lp->ll_list != NULL) {
- // unlet a List item.
- tv_list_item_remove(lp->ll_list, lp->ll_li);
- } else {
- // unlet a Dictionary item.
- dict_T *d = lp->ll_dict;
- assert(d != NULL);
- dictitem_T *di = lp->ll_di;
- bool watched = tv_dict_is_watched(d);
- char *key = NULL;
- typval_T oldtv;
+ // unlet a Dictionary item.
+ dict_T *d = lp->ll_dict;
+ assert(d != NULL);
+ dictitem_T *di = lp->ll_di;
+ bool watched = tv_dict_is_watched(d);
+ char *key = NULL;
+ typval_T oldtv;
- if (watched) {
- tv_copy(&di->di_tv, &oldtv);
- // need to save key because dictitem_remove will free it
- key = xstrdup((char *)di->di_key);
- }
+ if (watched) {
+ tv_copy(&di->di_tv, &oldtv);
+ // need to save key because dictitem_remove will free it
+ key = xstrdup(di->di_key);
+ }
- tv_dict_item_remove(d, di);
+ tv_dict_item_remove(d, di);
- if (watched) {
- tv_dict_watcher_notify(d, key, NULL, &oldtv);
- tv_clear(&oldtv);
- xfree(key);
- }
+ if (watched) {
+ tv_dict_watcher_notify(d, key, NULL, &oldtv);
+ tv_clear(&oldtv);
+ xfree(key);
}
}
return ret;
}
+/// Unlet one item or a range of items from a list.
+/// Return OK or FAIL.
+static void tv_list_unlet_range(list_T *const l, listitem_T *const li_first, const int n1_arg,
+ const bool has_n2, const int n2)
+{
+ assert(l != NULL);
+ // Delete a range of List items.
+ listitem_T *li_last = li_first;
+ int n1 = n1_arg;
+ while (true) {
+ listitem_T *const li = TV_LIST_ITEM_NEXT(l, li_last);
+ n1++;
+ if (li == NULL || (has_n2 && n2 < n1)) {
+ break;
+ }
+ li_last = li;
+ }
+ tv_list_remove_items(l, li_first, li_last);
+}
+
/// unlet a variable
///
/// @param[in] name Variable name to unlet.
@@ -1086,8 +1254,8 @@ static int do_lock_var(lval_T *lp, char *name_end FUNC_ATTR_UNUSED, exarg_T *eap
/// @param dip non-NULL when typval's dict item is needed
/// @param verbose may give error message
/// @param no_autoload do not use script autoloading
-int get_var_tv(const char *name, int len, typval_T *rettv, dictitem_T **dip, bool verbose,
- bool no_autoload)
+int eval_variable(const char *name, int len, typval_T *rettv, dictitem_T **dip, bool verbose,
+ bool no_autoload)
{
int ret = OK;
typval_T *tv = NULL;
@@ -1162,7 +1330,7 @@ void vars_clear_ext(hashtab_T *ht, int free_val)
}
}
hash_clear(ht);
- ht->ht_used = 0;
+ hash_init(ht);
}
/// Delete a variable from hashtab "ht" at item "hi".
@@ -1180,7 +1348,7 @@ void delete_var(hashtab_T *ht, hashitem_T *hi)
static void list_one_var(dictitem_T *v, const char *prefix, int *first)
{
char *const s = encode_tv2echo(&v->di_tv, NULL);
- list_one_var_a(prefix, (const char *)v->di_key, (ptrdiff_t)strlen((char *)v->di_key),
+ list_one_var_a(prefix, v->di_key, (ptrdiff_t)strlen(v->di_key),
v->di_tv.v_type, (s == NULL ? "" : s), first);
xfree(s);
}
@@ -1191,11 +1359,11 @@ static void list_one_var(dictitem_T *v, const char *prefix, int *first)
static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t name_len,
const VarType type, const char *string, int *first)
{
- // don't use msg() or msg_attr() to avoid overwriting "v:statusmsg"
+ // don't use msg() to avoid overwriting "v:statusmsg"
msg_start();
msg_puts(prefix);
if (name != NULL) { // "a:" vars don't have a name stored
- msg_puts_attr_len(name, name_len, 0);
+ msg_puts_len(name, name_len, 0);
}
msg_putchar(' ');
msg_advance(22);
@@ -1217,7 +1385,7 @@ static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t
msg_putchar(' ');
}
- msg_outtrans((char *)string);
+ msg_outtrans(string, 0);
if (type == VAR_FUNC || type == VAR_PARTIAL) {
msg_puts("()");
@@ -1228,6 +1396,62 @@ static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t
}
}
+/// Additional handling for setting a v: variable.
+///
+/// @return true if the variable should be set normally,
+/// false if nothing else needs to be done.
+bool before_set_vvar(const char *const varname, dictitem_T *const di, typval_T *const tv,
+ const bool copy, const bool watched, bool *const type_error)
+{
+ if (di->di_tv.v_type == VAR_STRING) {
+ typval_T oldtv = TV_INITIAL_VALUE;
+ if (watched) {
+ tv_copy(&di->di_tv, &oldtv);
+ }
+ XFREE_CLEAR(di->di_tv.vval.v_string);
+ if (copy || tv->v_type != VAR_STRING) {
+ const char *const val = tv_get_string(tv);
+ // Careful: when assigning to v:errmsg and tv_get_string()
+ // causes an error message the variable will already be set.
+ if (di->di_tv.vval.v_string == NULL) {
+ di->di_tv.vval.v_string = xstrdup(val);
+ }
+ } else {
+ // Take over the string to avoid an extra alloc/free.
+ di->di_tv.vval.v_string = tv->vval.v_string;
+ tv->vval.v_string = NULL;
+ }
+ // Notify watchers
+ if (watched) {
+ tv_dict_watcher_notify(&vimvardict, varname, &di->di_tv, &oldtv);
+ tv_clear(&oldtv);
+ }
+ return false;
+ } else if (di->di_tv.v_type == VAR_NUMBER) {
+ typval_T oldtv = TV_INITIAL_VALUE;
+ if (watched) {
+ tv_copy(&di->di_tv, &oldtv);
+ }
+ di->di_tv.vval.v_number = tv_get_number(tv);
+ if (strcmp(varname, "searchforward") == 0) {
+ set_search_direction(di->di_tv.vval.v_number ? '/' : '?');
+ } else if (strcmp(varname, "hlsearch") == 0) {
+ no_hlsearch = !di->di_tv.vval.v_number;
+ redraw_all_later(UPD_SOME_VALID);
+ }
+ // Notify watchers
+ if (watched) {
+ tv_dict_watcher_notify(&vimvardict, varname, &di->di_tv, &oldtv);
+ tv_clear(&oldtv);
+ }
+ return false;
+ } else if (di->di_tv.v_type != tv->v_type) {
+ *type_error = true;
+ return false;
+ }
+ return true;
+}
+
/// Set variable to the given value
///
/// If the variable already exists, the value is updated. Otherwise the variable
@@ -1257,31 +1481,29 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv,
const bool is_const)
FUNC_ATTR_NONNULL_ALL
{
- dictitem_T *v;
- hashtab_T *ht;
- dict_T *dict;
-
const char *varname;
- ht = find_var_ht_dict(name, name_len, &varname, &dict);
+ dict_T *dict;
+ hashtab_T *ht = find_var_ht_dict(name, name_len, &varname, &dict);
const bool watched = tv_dict_is_watched(dict);
if (ht == NULL || *varname == NUL) {
semsg(_(e_illvar), name);
return;
}
- v = find_var_in_ht(ht, 0, varname, name_len - (size_t)(varname - name), true);
+ const size_t varname_len = name_len - (size_t)(varname - name);
+ dictitem_T *di = find_var_in_ht(ht, 0, varname, varname_len, true);
// Search in parent scope which is possible to reference from lambda
- if (v == NULL) {
- v = find_var_in_scoped_ht(name, name_len, true);
+ if (di == NULL) {
+ di = find_var_in_scoped_ht(name, name_len, true);
}
- if (tv_is_func(*tv) && var_wrong_func_name(name, v == NULL)) {
+ if (tv_is_func(*tv) && var_wrong_func_name(name, di == NULL)) {
return;
}
typval_T oldtv = TV_INITIAL_VALUE;
- if (v != NULL) {
+ if (di != NULL) {
if (is_const) {
emsg(_(e_cannot_mod));
return;
@@ -1291,9 +1513,9 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv,
// - Whether the variable is read-only
// - Whether the variable value is locked
// - Whether the variable is locked
- if (var_check_ro(v->di_flags, name, name_len)
- || value_check_lock(v->di_tv.v_lock, name, name_len)
- || var_check_lock(v->di_flags, name, name_len)) {
+ if (var_check_ro(di->di_flags, name, name_len)
+ || value_check_lock(di->di_tv.v_lock, name, name_len)
+ || var_check_lock(di->di_flags, name, name_len)) {
return;
}
@@ -1301,42 +1523,19 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv,
// Handle setting internal v: variables separately where needed to
// prevent changing the type.
- if (is_vimvarht(ht)) {
- if (v->di_tv.v_type == VAR_STRING) {
- XFREE_CLEAR(v->di_tv.vval.v_string);
- if (copy || tv->v_type != VAR_STRING) {
- const char *const val = tv_get_string(tv);
-
- // Careful: when assigning to v:errmsg and tv_get_string()
- // causes an error message the variable will already be set.
- if (v->di_tv.vval.v_string == NULL) {
- v->di_tv.vval.v_string = xstrdup(val);
- }
- } 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 = tv_get_number(tv);
- if (strcmp(varname, "searchforward") == 0) {
- set_search_direction(v->di_tv.vval.v_number ? '/' : '?');
- } else if (strcmp(varname, "hlsearch") == 0) {
- no_hlsearch = !v->di_tv.vval.v_number;
- redraw_all_later(UPD_SOME_VALID);
- }
- return;
- } else if (v->di_tv.v_type != tv->v_type) {
- semsg(_("E963: setting %s to value with wrong type"), name);
- return;
+ bool type_error = false;
+ if (is_vimvarht(ht)
+ && !before_set_vvar(varname, di, tv, copy, watched, &type_error)) {
+ if (type_error) {
+ semsg(_(e_setting_v_str_to_value_with_wrong_type), varname);
}
+ return;
}
if (watched) {
- tv_copy(&v->di_tv, &oldtv);
+ tv_copy(&di->di_tv, &oldtv);
}
- tv_clear(&v->di_tv);
+ tv_clear(&di->di_tv);
} else { // Add a new variable.
// Can't add "v:" or "a:" variable.
if (is_vimvarht(ht) || ht == get_funccal_args_ht()) {
@@ -1352,28 +1551,28 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv,
// Make sure dict is valid
assert(dict != NULL);
- v = xmalloc(sizeof(dictitem_T) + strlen(varname));
- STRCPY(v->di_key, varname);
- if (hash_add(ht, (char *)v->di_key) == FAIL) {
- xfree(v);
+ di = xmalloc(offsetof(dictitem_T, di_key) + varname_len + 1);
+ memcpy(di->di_key, varname, varname_len + 1);
+ if (hash_add(ht, di->di_key) == FAIL) {
+ xfree(di);
return;
}
- v->di_flags = DI_FLAGS_ALLOC;
+ di->di_flags = DI_FLAGS_ALLOC;
if (is_const) {
- v->di_flags |= DI_FLAGS_LOCK;
+ di->di_flags |= DI_FLAGS_LOCK;
}
}
if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) {
- tv_copy(tv, &v->di_tv);
+ tv_copy(tv, &di->di_tv);
} else {
- v->di_tv = *tv;
- v->di_tv.v_lock = VAR_UNLOCKED;
+ di->di_tv = *tv;
+ di->di_tv.v_lock = VAR_UNLOCKED;
tv_init(tv);
}
if (watched) {
- tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv);
+ tv_dict_watcher_notify(dict, di->di_key, &di->di_tv, &oldtv);
tv_clear(&oldtv);
}
@@ -1381,7 +1580,7 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv,
// Like :lockvar! name: lock the value and what it contains, but only
// if the reference count is up to one. That locks only literal
// values.
- tv_item_lock(&v->di_tv, DICT_MAXNEST, true, true);
+ tv_item_lock(&di->di_tv, DICT_MAXNEST, true, true);
}
}
@@ -1574,7 +1773,7 @@ static void get_var_from(const char *varname, typval_T *rettv, typval_T *deftv,
tv_dict_set_ret(rettv, opts);
done = true;
}
- } else if (get_option_tv(&varname, rettv, true) == OK) {
+ } else if (eval_option(&varname, rettv, true) == OK) {
// Local option
done = true;
}
@@ -1644,28 +1843,121 @@ static void getwinvar(typval_T *argvars, typval_T *rettv, int off)
get_var_from(varname, rettv, &argvars[off + 2], 'w', tp, win, NULL);
}
-/// Set option "varname" to the value of "varp" for the current buffer/window.
-static void set_option_from_tv(const char *varname, typval_T *varp)
+/// Convert typval to option value for a particular option.
+///
+/// @param[in] tv typval to convert.
+/// @param[in] option Option name.
+/// @param[in] flags Option flags.
+/// @param[out] error Whether an error occurred.
+///
+/// @return Typval converted to OptVal. Must be freed by caller.
+/// Returns NIL_OPTVAL for invalid option name.
+static OptVal tv_to_optval(typval_T *tv, const char *option, uint32_t flags, bool *error)
{
- long numval = 0;
- const char *strval;
- bool error = false;
+ OptVal value = NIL_OPTVAL;
char nbuf[NUMBUFLEN];
-
- if (varp->v_type == VAR_BOOL) {
- if (is_string_option(varname)) {
+ bool err = false;
+
+ if ((flags & P_FUNC) && tv_is_func(*tv)) {
+ // If the option can be set to a function reference or a lambda
+ // and the passed value is a function reference, then convert it to
+ // the name (string) of the function reference.
+ char *strval = encode_tv2string(tv, NULL);
+ err = strval == NULL;
+ value = CSTR_AS_OPTVAL(strval);
+ } else if (flags & (P_NUM | P_BOOL)) {
+ varnumber_T n = (flags & P_NUM) ? tv_get_number_chk(tv, &err)
+ : tv_get_bool_chk(tv, &err);
+ // This could be either "0" or a string that's not a number.
+ // So we need to check if it's actually a number.
+ if (!err && tv->v_type == VAR_STRING && n == 0) {
+ unsigned idx;
+ for (idx = 0; tv->vval.v_string[idx] == '0'; idx++) {}
+ if (tv->vval.v_string[idx] != NUL || idx == 0) {
+ // There's another character after zeros or the string is empty.
+ // In both cases, we are trying to set a num option using a string.
+ err = true;
+ semsg(_("E521: Number required: &%s = '%s'"), option, tv->vval.v_string);
+ }
+ }
+ value = (flags & P_NUM) ? NUMBER_OPTVAL((OptInt)n) : BOOLEAN_OPTVAL(TRISTATE_FROM_INT(n));
+ } else if ((flags & P_STRING) || is_tty_option(option)) {
+ // Avoid setting string option to a boolean or a special value.
+ if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) {
+ const char *strval = tv_get_string_buf_chk(tv, nbuf);
+ err = strval == NULL;
+ value = CSTR_TO_OPTVAL(strval);
+ } else if (flags & P_STRING) {
+ err = true;
emsg(_(e_stringreq));
- return;
}
- numval = (long)varp->vval.v_number;
- strval = "0"; // avoid using "false"
} else {
- numval = (long)tv_get_number_chk(varp, &error);
- strval = tv_get_string_buf_chk(varp, nbuf);
+ abort(); // This should never happen.
}
- if (!error && strval != NULL) {
- set_option_value_give_err(varname, numval, strval, OPT_LOCAL);
+
+ if (error != NULL) {
+ *error = err;
}
+ return value;
+}
+
+/// Convert an option value to typval.
+///
+/// @param[in] value Option value to convert.
+///
+/// @return OptVal converted to typval.
+typval_T optval_as_tv(OptVal value)
+{
+ typval_T rettv = { .v_type = VAR_SPECIAL, .vval = { .v_special = kSpecialVarNull } };
+
+ switch (value.type) {
+ case kOptValTypeNil:
+ break;
+ case kOptValTypeBoolean:
+ switch (value.data.boolean) {
+ case kTrue:
+ rettv.v_type = VAR_BOOL;
+ rettv.vval.v_bool = kBoolVarTrue;
+ break;
+ case kFalse:
+ rettv.v_type = VAR_BOOL;
+ rettv.vval.v_bool = kBoolVarFalse;
+ break;
+ case kNone:
+ break; // return v:null for None boolean value
+ }
+ break;
+ case kOptValTypeNumber:
+ rettv.v_type = VAR_NUMBER;
+ rettv.vval.v_number = value.data.number;
+ break;
+ case kOptValTypeString:
+ rettv.v_type = VAR_STRING;
+ rettv.vval.v_string = value.data.string.data;
+ break;
+ }
+
+ return rettv;
+}
+
+/// Set option "varname" to the value of "varp" for the current buffer/window.
+static void set_option_from_tv(const char *varname, typval_T *varp)
+{
+ int opt_idx = findoption(varname);
+ if (opt_idx < 0) {
+ semsg(_(e_unknown_option2), varname);
+ return;
+ }
+ uint32_t opt_p_flags = get_option(opt_idx)->flags;
+
+ bool error = false;
+ OptVal value = tv_to_optval(varp, varname, opt_p_flags, &error);
+
+ if (!error) {
+ set_option_value_give_err(varname, value, OPT_LOCAL);
+ }
+
+ optval_free(value);
}
/// "setwinvar()" and "settabwinvar()" functions
@@ -1723,10 +2015,10 @@ bool var_exists(const char *var)
if (tofree != NULL) {
name = tofree;
}
- n = get_var_tv(name, len, &tv, NULL, false, true) == OK;
+ n = eval_variable(name, len, &tv, NULL, false, true) == OK;
if (n) {
// Handle d.key, l[idx], f(expr).
- n = handle_subscript(&var, &tv, true, false, name, &name) == OK;
+ n = handle_subscript(&var, &tv, &EVALARG_EVALUATE, false) == OK;
if (n) {
tv_clear(&tv);
}
diff --git a/src/nvim/eval/vars.h b/src/nvim/eval/vars.h
index b87c9d62cb..6ddf449ff1 100644
--- a/src/nvim/eval/vars.h
+++ b/src/nvim/eval/vars.h
@@ -1,9 +1,13 @@
-#ifndef NVIM_EVAL_VARS_H
-#define NVIM_EVAL_VARS_H
+#pragma once
-#include "nvim/ex_cmds_defs.h"
+#include <stddef.h> // IWYU pragma: keep
+
+#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep
+#include "nvim/garray_defs.h" // IWYU pragma: keep
+#include "nvim/hashtab_defs.h" // IWYU pragma: keep
+#include "nvim/option_defs.h" // IWYU pragma: keep
+#include "nvim/types_defs.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/vars.h.generated.h"
#endif
-#endif // NVIM_EVAL_VARS_H
diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c
index f58a0c488a..e0abbad477 100644
--- a/src/nvim/eval/window.c
+++ b/src/nvim/eval/window.c
@@ -1,6 +1,3 @@
-// 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/window.c: Window related builtin functions
#include <stdbool.h>
@@ -9,7 +6,7 @@
#include <stdlib.h>
#include <string.h>
-#include "nvim/ascii.h"
+#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
@@ -21,23 +18,22 @@
#include "nvim/garray.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
-#include "nvim/macros.h"
-#include "nvim/memline_defs.h"
+#include "nvim/macros_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/move.h"
-#include "nvim/option_defs.h"
-#include "nvim/pos.h"
-#include "nvim/types.h"
-#include "nvim/vim.h"
+#include "nvim/pos_defs.h"
+#include "nvim/types_defs.h"
+#include "nvim/vim_defs.h"
#include "nvim/window.h"
+#include "nvim/winfloat.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/window.c.generated.h"
#endif
-static char *e_invalwindow = N_("E957: Invalid window number");
-static char e_cannot_resize_window_in_another_tab_page[]
+static const char *e_invalwindow = N_("E957: Invalid window number");
+static const char e_cannot_resize_window_in_another_tab_page[]
= N_("E1308: Cannot resize a window in another tab page");
static int win_getid(typval_T *argvars)
@@ -98,6 +94,7 @@ win_T *win_id2wp(int id)
}
/// Return the window and tab pointer of window "id".
+/// Returns NULL when not found.
win_T *win_id2wp_tp(int id, tabpage_T **tpp)
{
FOR_ALL_TAB_WINDOWS(tp, wp) {
@@ -192,9 +189,9 @@ win_T *find_tabwin(typval_T *wvp, typval_T *tvp)
if (wvp->v_type != VAR_UNKNOWN) {
if (tvp->v_type != VAR_UNKNOWN) {
- long n = tv_get_number(tvp);
+ int n = (int)tv_get_number(tvp);
if (n >= 0) {
- tp = find_tabpage((int)n);
+ tp = find_tabpage(n);
}
} else {
tp = curtab;
@@ -265,7 +262,7 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar)
} else {
// Extract the window count (if specified). e.g. winnr('3j')
char *endp;
- long count = strtol((char *)arg, &endp, 10);
+ int count = (int)strtol(arg, &endp, 10);
if (count <= 0) {
// if count is not specified, default to 1
count = 1;
@@ -639,7 +636,7 @@ void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (wp == NULL || targetwin == NULL || wp == targetwin
|| !win_valid(wp) || !win_valid(targetwin)
- || win_valid_floating(wp) || win_valid_floating(targetwin)) {
+ || win_float_valid(wp) || win_float_valid(targetwin)) {
emsg(_(e_invalwindow));
rettv->vval.v_number = -1;
return;
@@ -651,8 +648,7 @@ void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
dict_T *d;
dictitem_T *di;
- if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL) {
- emsg(_(e_invarg));
+ if (tv_check_for_nonnull_dict_arg(argvars, 2) == FAIL) {
return;
}
@@ -796,51 +792,50 @@ void f_winrestcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "winrestview()" function
void f_winrestview(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- dict_T *dict = argvars[0].vval.v_dict;
+ if (tv_check_for_nonnull_dict_arg(argvars, 0) == FAIL) {
+ return;
+ }
- if (argvars[0].v_type != VAR_DICT || dict == NULL) {
- emsg(_(e_invarg));
- } else {
- dictitem_T *di;
- if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) {
- curwin->w_cursor.lnum = (linenr_T)tv_get_number(&di->di_tv);
- }
- if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) {
- curwin->w_cursor.col = (colnr_T)tv_get_number(&di->di_tv);
- }
- if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) {
- curwin->w_cursor.coladd = (colnr_T)tv_get_number(&di->di_tv);
- }
- if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) {
- curwin->w_curswant = (colnr_T)tv_get_number(&di->di_tv);
- curwin->w_set_curswant = false;
- }
- if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) {
- set_topline(curwin, (linenr_T)tv_get_number(&di->di_tv));
- }
- if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) {
- curwin->w_topfill = (int)tv_get_number(&di->di_tv);
- }
- if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) {
- curwin->w_leftcol = (colnr_T)tv_get_number(&di->di_tv);
- }
- if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) {
- curwin->w_skipcol = (colnr_T)tv_get_number(&di->di_tv);
- }
+ dict_T *dict = argvars[0].vval.v_dict;
+ dictitem_T *di;
+ if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) {
+ curwin->w_cursor.lnum = (linenr_T)tv_get_number(&di->di_tv);
+ }
+ if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) {
+ curwin->w_cursor.col = (colnr_T)tv_get_number(&di->di_tv);
+ }
+ if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) {
+ curwin->w_cursor.coladd = (colnr_T)tv_get_number(&di->di_tv);
+ }
+ if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) {
+ curwin->w_curswant = (colnr_T)tv_get_number(&di->di_tv);
+ curwin->w_set_curswant = false;
+ }
+ if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) {
+ set_topline(curwin, (linenr_T)tv_get_number(&di->di_tv));
+ }
+ if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) {
+ curwin->w_topfill = (int)tv_get_number(&di->di_tv);
+ }
+ if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) {
+ curwin->w_leftcol = (colnr_T)tv_get_number(&di->di_tv);
+ }
+ if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) {
+ curwin->w_skipcol = (colnr_T)tv_get_number(&di->di_tv);
+ }
- check_cursor();
- win_new_height(curwin, curwin->w_height);
- win_new_width(curwin, curwin->w_width);
- changed_window_setting();
+ check_cursor();
+ win_new_height(curwin, curwin->w_height);
+ win_new_width(curwin, curwin->w_width);
+ changed_window_setting();
- if (curwin->w_topline <= 0) {
- curwin->w_topline = 1;
- }
- if (curwin->w_topline > curbuf->b_ml.ml_line_count) {
- curwin->w_topline = curbuf->b_ml.ml_line_count;
- }
- check_topfill(curwin, true);
+ if (curwin->w_topline <= 0) {
+ curwin->w_topline = 1;
+ }
+ if (curwin->w_topline > curbuf->b_ml.ml_line_count) {
+ curwin->w_topline = curbuf->b_ml.ml_line_count;
}
+ check_topfill(curwin, true);
}
/// "winsaveview()" function
diff --git a/src/nvim/eval/window.h b/src/nvim/eval/window.h
index 995f0a55a9..ed879c895a 100644
--- a/src/nvim/eval/window.h
+++ b/src/nvim/eval/window.h
@@ -1,5 +1,4 @@
-#ifndef NVIM_EVAL_WINDOW_H
-#define NVIM_EVAL_WINDOW_H
+#pragma once
#include <stdbool.h>
#include <string.h>
@@ -11,9 +10,10 @@
#include "nvim/globals.h"
#include "nvim/mark.h"
#include "nvim/option_defs.h"
-#include "nvim/os/os.h"
-#include "nvim/pos.h"
-#include "nvim/vim.h"
+#include "nvim/option_vars.h"
+#include "nvim/os/fs.h"
+#include "nvim/pos_defs.h"
+#include "nvim/vim_defs.h"
#include "nvim/window.h"
/// Structure used by switch_win() to pass values to restore_win()
@@ -75,4 +75,3 @@ typedef struct {
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/window.h.generated.h"
#endif
-#endif // NVIM_EVAL_WINDOW_H