diff options
| author | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 21:52:58 +0000 |
|---|---|---|
| committer | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 21:52:58 +0000 |
| commit | 931bffbda3668ddc609fc1da8f9eb576b170aa52 (patch) | |
| tree | d8c1843a95da5ea0bb4acc09f7e37843d9995c86 /src/nvim/eval/funcs.c | |
| parent | 142d9041391780ac15b89886a54015fdc5c73995 (diff) | |
| parent | 4a8bf24ac690004aedf5540fa440e788459e5e34 (diff) | |
| download | rneovim-userreg.tar.gz rneovim-userreg.tar.bz2 rneovim-userreg.zip | |
Merge remote-tracking branch 'upstream/master' into userreguserreg
Diffstat (limited to 'src/nvim/eval/funcs.c')
| -rw-r--r-- | src/nvim/eval/funcs.c | 2257 |
1 files changed, 1057 insertions, 1200 deletions
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) /// "foreg |