diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:09:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:09:20 +0000 |
commit | 029f72b1a93430b24b88eb3a72c6114d9f149737 (patch) | |
tree | 765d5c2041967f9c6fef195fe343d9234a030e90 /src/evalfunc.c | |
parent | Initial commit. (diff) | |
download | vim-029f72b1a93430b24b88eb3a72c6114d9f149737.tar.xz vim-029f72b1a93430b24b88eb3a72c6114d9f149737.zip |
Adding upstream version 2:9.1.0016.upstream/2%9.1.0016
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/evalfunc.c')
-rw-r--r-- | src/evalfunc.c | 11387 |
1 files changed, 11387 insertions, 0 deletions
diff --git a/src/evalfunc.c b/src/evalfunc.c new file mode 100644 index 0000000..e37b3a4 --- /dev/null +++ b/src/evalfunc.c @@ -0,0 +1,11387 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +/* + * evalfunc.c: Builtin functions + */ +#define USING_FLOAT_STUFF + +#include "vim.h" + +#if defined(FEAT_EVAL) || defined(PROTO) + +#ifdef VMS +# include <float.h> +#endif + +static void f_and(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_BEVAL +static void f_balloon_gettext(typval_T *argvars, typval_T *rettv); +static void f_balloon_show(typval_T *argvars, typval_T *rettv); +# if defined(FEAT_BEVAL_TERM) +static void f_balloon_split(typval_T *argvars, typval_T *rettv); +# endif +#endif +static void f_byte2line(typval_T *argvars, typval_T *rettv); +static void f_call(typval_T *argvars, typval_T *rettv); +static void f_changenr(typval_T *argvars, typval_T *rettv); +static void f_char2nr(typval_T *argvars, typval_T *rettv); +static void f_charcol(typval_T *argvars, typval_T *rettv); +static void f_col(typval_T *argvars, typval_T *rettv); +static void f_confirm(typval_T *argvars, typval_T *rettv); +static void f_copy(typval_T *argvars, typval_T *rettv); +static void f_cursor(typval_T *argsvars, typval_T *rettv); +#ifdef MSWIN +static void f_debugbreak(typval_T *argvars, typval_T *rettv); +#endif +static void f_deepcopy(typval_T *argvars, typval_T *rettv); +static void f_did_filetype(typval_T *argvars, typval_T *rettv); +static void f_echoraw(typval_T *argvars, typval_T *rettv); +static void f_empty(typval_T *argvars, typval_T *rettv); +static void f_environ(typval_T *argvars, typval_T *rettv); +static void f_err_teapot(typval_T *argvars, typval_T *rettv); +static void f_escape(typval_T *argvars, typval_T *rettv); +static void f_eval(typval_T *argvars, typval_T *rettv); +static void f_eventhandler(typval_T *argvars, typval_T *rettv); +static void f_execute(typval_T *argvars, typval_T *rettv); +static void f_exists_compiled(typval_T *argvars, typval_T *rettv); +static void f_expand(typval_T *argvars, typval_T *rettv); +static void f_expandcmd(typval_T *argvars, typval_T *rettv); +static void f_feedkeys(typval_T *argvars, typval_T *rettv); +static void f_fnameescape(typval_T *argvars, typval_T *rettv); +static void f_foreground(typval_T *argvars, typval_T *rettv); +static void f_funcref(typval_T *argvars, typval_T *rettv); +static void f_function(typval_T *argvars, typval_T *rettv); +static void f_garbagecollect(typval_T *argvars, typval_T *rettv); +static void f_get(typval_T *argvars, typval_T *rettv); +static void f_getchangelist(typval_T *argvars, typval_T *rettv); +static void f_getcharpos(typval_T *argvars, typval_T *rettv); +static void f_getcharsearch(typval_T *argvars, typval_T *rettv); +static void f_getenv(typval_T *argvars, typval_T *rettv); +static void f_getfontname(typval_T *argvars, typval_T *rettv); +static void f_getjumplist(typval_T *argvars, typval_T *rettv); +static void f_getpid(typval_T *argvars, typval_T *rettv); +static void f_getcurpos(typval_T *argvars, typval_T *rettv); +static void f_getcursorcharpos(typval_T *argvars, typval_T *rettv); +static void f_getpos(typval_T *argvars, typval_T *rettv); +static void f_getreg(typval_T *argvars, typval_T *rettv); +static void f_getreginfo(typval_T *argvars, typval_T *rettv); +static void f_getregtype(typval_T *argvars, typval_T *rettv); +static void f_gettagstack(typval_T *argvars, typval_T *rettv); +static void f_gettext(typval_T *argvars, typval_T *rettv); +static void f_haslocaldir(typval_T *argvars, typval_T *rettv); +static void f_hlID(typval_T *argvars, typval_T *rettv); +static void f_hlexists(typval_T *argvars, typval_T *rettv); +static void f_hostname(typval_T *argvars, typval_T *rettv); +static void f_index(typval_T *argvars, typval_T *rettv); +static void f_indexof(typval_T *argvars, typval_T *rettv); +static void f_input(typval_T *argvars, typval_T *rettv); +static void f_inputdialog(typval_T *argvars, typval_T *rettv); +static void f_inputlist(typval_T *argvars, typval_T *rettv); +static void f_inputrestore(typval_T *argvars, typval_T *rettv); +static void f_inputsave(typval_T *argvars, typval_T *rettv); +static void f_inputsecret(typval_T *argvars, typval_T *rettv); +static void f_interrupt(typval_T *argvars, typval_T *rettv); +static void f_invert(typval_T *argvars, typval_T *rettv); +static void f_islocked(typval_T *argvars, typval_T *rettv); +static void f_keytrans(typval_T *argvars, typval_T *rettv); +static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv); +static void f_libcall(typval_T *argvars, typval_T *rettv); +static void f_libcallnr(typval_T *argvars, typval_T *rettv); +static void f_line(typval_T *argvars, typval_T *rettv); +static void f_line2byte(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_LUA +static void f_luaeval(typval_T *argvars, typval_T *rettv); +#endif +static void f_match(typval_T *argvars, typval_T *rettv); +static void f_matchbufline(typval_T *argvars, typval_T *rettv); +static void f_matchend(typval_T *argvars, typval_T *rettv); +static void f_matchlist(typval_T *argvars, typval_T *rettv); +static void f_matchstr(typval_T *argvars, typval_T *rettv); +static void f_matchstrlist(typval_T *argvars, typval_T *rettv); +static void f_matchstrpos(typval_T *argvars, typval_T *rettv); +static void f_max(typval_T *argvars, typval_T *rettv); +static void f_min(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_MZSCHEME +static void f_mzeval(typval_T *argvars, typval_T *rettv); +#endif +static void f_nextnonblank(typval_T *argvars, typval_T *rettv); +static void f_nr2char(typval_T *argvars, typval_T *rettv); +static void f_or(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_PERL +static void f_perleval(typval_T *argvars, typval_T *rettv); +#endif +static void f_prevnonblank(typval_T *argvars, typval_T *rettv); +static void f_printf(typval_T *argvars, typval_T *rettv); +static void f_pum_getpos(typval_T *argvars, typval_T *rettv); +static void f_pumvisible(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_PYTHON3 +static void f_py3eval(typval_T *argvars, typval_T *rettv); +#endif +#ifdef FEAT_PYTHON +static void f_pyeval(typval_T *argvars, typval_T *rettv); +#endif +#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) +static void f_pyxeval(typval_T *argvars, typval_T *rettv); +#endif +static void f_test_srand_seed(typval_T *argvars, typval_T *rettv); +static void f_rand(typval_T *argvars, typval_T *rettv); +static void f_range(typval_T *argvars, typval_T *rettv); +static void f_reg_executing(typval_T *argvars, typval_T *rettv); +static void f_reg_recording(typval_T *argvars, typval_T *rettv); +static void f_rename(typval_T *argvars, typval_T *rettv); +static void f_repeat(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_RUBY +static void f_rubyeval(typval_T *argvars, typval_T *rettv); +#endif +static void f_screenattr(typval_T *argvars, typval_T *rettv); +static void f_screenchar(typval_T *argvars, typval_T *rettv); +static void f_screenchars(typval_T *argvars, typval_T *rettv); +static void f_screencol(typval_T *argvars, typval_T *rettv); +static void f_screenrow(typval_T *argvars, typval_T *rettv); +static void f_screenstring(typval_T *argvars, typval_T *rettv); +static void f_search(typval_T *argvars, typval_T *rettv); +static void f_searchdecl(typval_T *argvars, typval_T *rettv); +static void f_searchpair(typval_T *argvars, typval_T *rettv); +static void f_searchpairpos(typval_T *argvars, typval_T *rettv); +static void f_searchpos(typval_T *argvars, typval_T *rettv); +static void f_setcharpos(typval_T *argvars, typval_T *rettv); +static void f_setcharsearch(typval_T *argvars, typval_T *rettv); +static void f_setcursorcharpos(typval_T *argvars, typval_T *rettv); +static void f_setenv(typval_T *argvars, typval_T *rettv); +static void f_setfperm(typval_T *argvars, typval_T *rettv); +static void f_setpos(typval_T *argvars, typval_T *rettv); +static void f_setreg(typval_T *argvars, typval_T *rettv); +static void f_settagstack(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_CRYPT +static void f_sha256(typval_T *argvars, typval_T *rettv); +#endif +static void f_shellescape(typval_T *argvars, typval_T *rettv); +static void f_shiftwidth(typval_T *argvars, typval_T *rettv); +static void f_soundfold(typval_T *argvars, typval_T *rettv); +static void f_spellbadword(typval_T *argvars, typval_T *rettv); +static void f_spellsuggest(typval_T *argvars, typval_T *rettv); +static void f_split(typval_T *argvars, typval_T *rettv); +static void f_srand(typval_T *argvars, typval_T *rettv); +static void f_submatch(typval_T *argvars, typval_T *rettv); +static void f_substitute(typval_T *argvars, typval_T *rettv); +static void f_swapfilelist(typval_T *argvars, typval_T *rettv); +static void f_swapinfo(typval_T *argvars, typval_T *rettv); +static void f_swapname(typval_T *argvars, typval_T *rettv); +static void f_synID(typval_T *argvars, typval_T *rettv); +static void f_synIDattr(typval_T *argvars, typval_T *rettv); +static void f_synIDtrans(typval_T *argvars, typval_T *rettv); +static void f_synstack(typval_T *argvars, typval_T *rettv); +static void f_synconcealed(typval_T *argvars, typval_T *rettv); +static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv); +static void f_taglist(typval_T *argvars, typval_T *rettv); +static void f_tagfiles(typval_T *argvars, typval_T *rettv); +static void f_type(typval_T *argvars, typval_T *rettv); +static void f_virtcol(typval_T *argvars, typval_T *rettv); +static void f_visualmode(typval_T *argvars, typval_T *rettv); +static void f_wildmenumode(typval_T *argvars, typval_T *rettv); +static void f_windowsversion(typval_T *argvars, typval_T *rettv); +static void f_wordcount(typval_T *argvars, typval_T *rettv); +static void f_xor(typval_T *argvars, typval_T *rettv); + + +/* + * Functions that check the argument type of a builtin function. + * Each function returns FAIL and gives an error message if the type is wrong. + */ + +// Context passed to an arg_ function. +typedef struct { + int arg_count; // actual argument count + type2_T *arg_types; // list of argument types + int arg_idx; // current argument index (first arg is zero) + cctx_T *arg_cctx; +} argcontext_T; + +// A function to check one argument type. The first argument is the type to +// check. If needed, other argument types can be obtained with the context. +// E.g. if "arg_idx" is 1, then (type - 1) is the first argument type. +// NOTE: Use "arg_any", not NULL, in funcentry_T.f_argcheck array +// to accept an argument of any type. +typedef int (*argcheck_T)(type_T *, type_T *, argcontext_T *); + +/* + * Call need_type() to check an argument type. + */ + static int +check_arg_type( + type_T *expected, + type_T *actual, + argcontext_T *context) +{ + return need_type(actual, expected, FALSE, + context->arg_idx - context->arg_count, context->arg_idx + 1, + context->arg_cctx, FALSE, FALSE); +} + +/* + * Call need_type() to check an argument type and that it is modifiable + */ + static int +check_arg_type_mod( + type_T *expected, + type_T *actual, + argcontext_T *context) +{ + if (need_type(actual, expected, FALSE, + context->arg_idx - context->arg_count, context->arg_idx + 1, + context->arg_cctx, FALSE, FALSE) == FAIL) + return FAIL; + return arg_type_modifiable(actual, context->arg_idx + 1); +} + +/* + * Give an error if "type" is a constant. + */ + int +arg_type_modifiable(type_T *type, int arg_idx) +{ + char *tofree; + + if ((type->tt_flags & TTFLAG_CONST) == 0) + return OK; + semsg(_(e_argument_nr_trying_to_modify_const_str), + arg_idx, type_name(type, &tofree)); + vim_free(tofree); + return FAIL; +} + +/* + * Return OK for any type unconditionally. + */ + static int +arg_any(type_T *type UNUSED, + type_T *decl_type UNUSED, + argcontext_T *context UNUSED) +{ + return OK; +} + +/* + * Check "type" is a float or a number. + */ + static int +arg_float_or_nr(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_FLOAT + || type->tt_type == VAR_NUMBER + || type_any_or_unknown(type)) + return OK; + arg_type_mismatch(&t_number, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" is a number. + */ + static int +arg_number(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + return check_arg_type(&t_number, type, context); +} + +/* + * Check "type" is an object. + */ + static int +arg_object(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_OBJECT + || type_any_or_unknown(type)) + return OK; + arg_type_mismatch(&t_object, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" is a dict of 'any'. + */ + static int +arg_dict_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + return check_arg_type(&t_dict_any, type, context); +} + +/* + * Check "type" is a list of 'any'. + */ + static int +arg_list_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + return check_arg_type(&t_list_any, type, context); +} + +/* + * Check "type" is a list of 'any' and modifiable + */ + static int +arg_list_any_mod( + type_T *type, + type_T *decl_type UNUSED, + argcontext_T *context) +{ + return check_arg_type_mod(&t_list_any, type, context); +} + +/* + * Check "type" is a list of numbers. + */ + static int +arg_list_number(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + return check_arg_type(&t_list_number, type, context); +} + +/* + * Check "type" is a list of strings. + */ + static int +arg_list_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + return check_arg_type(&t_list_string, type, context); +} + +/* + * Check "type" is a string. + */ + static int +arg_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + return check_arg_type(&t_string, type, context); +} + +/* + * Check "type" is a blob + */ + static int +arg_blob(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + return check_arg_type(&t_blob, type, context); +} + +/* + * Check "type" is a bool or number 0 or 1. + */ + static int +arg_bool(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + return check_arg_type(&t_bool, type, context); +} + +/* + * Check "type" is a list of 'any' or a blob. + */ + static int +arg_list_or_blob(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_LIST + || type->tt_type == VAR_BLOB + || type_any_or_unknown(type)) + return OK; + arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" is a modifiable list of 'any' or a blob. + */ + static int +arg_list_or_blob_mod( + type_T *type, + type_T *decl_type, + argcontext_T *context) +{ + if (arg_list_or_blob(type, decl_type, context) == FAIL) + return FAIL; + return arg_type_modifiable(type, context->arg_idx + 1); +} + +/* + * Check "type" is a string or a number + */ + static int +arg_string_or_nr(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_STRING + || type->tt_type == VAR_NUMBER + || type_any_or_unknown(type)) + return OK; + arg_type_mismatch(&t_string, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" is a buffer (string or a number) + */ + static int +arg_buffer(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_STRING + || type->tt_type == VAR_NUMBER + || type_any_or_unknown(type)) + return OK; + arg_type_mismatch(&t_string, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" is a buffer or a dict of any + */ + static int +arg_buffer_or_dict_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_STRING + || type->tt_type == VAR_NUMBER + || type->tt_type == VAR_DICT + || type_any_or_unknown(type)) + return OK; + arg_type_mismatch(&t_string, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" is a line (string or a number) + */ + static int +arg_lnum(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_STRING + || type->tt_type == VAR_NUMBER + || type_any_or_unknown(type)) + return OK; + arg_type_mismatch(&t_string, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" is a string or a list of strings. + */ + static int +arg_string_or_list_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_STRING + || type_any_or_unknown(type)) + return OK; + if (type->tt_type != VAR_LIST) + { + arg_type_mismatch(&t_string, type, context->arg_idx + 1); + return FAIL; + } + if (type->tt_member->tt_type == VAR_ANY + || type->tt_member->tt_type == VAR_STRING) + return OK; + + arg_type_mismatch(&t_list_string, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" is a string or a list of 'any' + */ + static int +arg_string_or_list_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_STRING + || type->tt_type == VAR_LIST + || type_any_or_unknown(type)) + return OK; + arg_type_mismatch(&t_string, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" is a string or a dict of 'any' + */ + static int +arg_string_or_dict_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_STRING + || type->tt_type == VAR_DICT + || type_any_or_unknown(type)) + return OK; + arg_type_mismatch(&t_string, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" is a string or a blob + */ + static int +arg_string_or_blob(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_STRING + || type->tt_type == VAR_BLOB + || type_any_or_unknown(type)) + return OK; + arg_type_mismatch(&t_string, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" is a list of 'any' or a dict of 'any'. + */ + static int +arg_list_or_dict(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_LIST + || type->tt_type == VAR_DICT + || type_any_or_unknown(type)) + return OK; + arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" is a list of 'any' or a dict of 'any'. And modifiable. + */ + static int +arg_list_or_dict_mod( + type_T *type, + type_T *decl_type, + argcontext_T *context) +{ + if (arg_list_or_dict(type, decl_type, context) == FAIL) + return FAIL; + return arg_type_modifiable(type, context->arg_idx + 1); +} + +/* + * Check "type" is a list of 'any' or a dict of 'any' or a blob. + * Also check if "type" is modifiable. + */ + static int +arg_list_or_dict_or_blob_mod( + type_T *type, + type_T *decl_type UNUSED, + argcontext_T *context) +{ + if (type->tt_type == VAR_LIST + || type->tt_type == VAR_DICT + || type->tt_type == VAR_BLOB + || type_any_or_unknown(type)) + return arg_type_modifiable(type, context->arg_idx + 1); + arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" is a list of 'any' or a dict of 'any' or a blob or a string. + */ + static int +arg_list_or_dict_or_blob_or_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_LIST + || type->tt_type == VAR_DICT + || type->tt_type == VAR_BLOB + || type->tt_type == VAR_STRING + || type_any_or_unknown(type)) + return OK; + arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" is a list of 'any' or a dict of 'any' or a blob or a string. + * Also check the value is modifiable. + */ + static int +arg_list_or_dict_or_blob_or_string_mod( + type_T *type, + type_T *decl_type, + argcontext_T *context) +{ + if (arg_list_or_dict_or_blob_or_string(type, decl_type, context) == FAIL) + return FAIL; + return arg_type_modifiable(type, context->arg_idx + 1); +} + +/* + * Check second argument of map() or filter(). + */ + static int +check_map_filter_arg2(type_T *type, argcontext_T *context, int is_map) +{ + type_T *expected_member = NULL; + type_T *(args[2]); + type_T t_func_exp = {VAR_FUNC, 2, 0, 0, NULL, NULL, args}; + + if (context->arg_types[0].type_curr->tt_type == VAR_LIST + || context->arg_types[0].type_curr->tt_type == VAR_DICT) + { + // Use the declared type if possible, so that an error is given if + // a declared list changes type, but not if a constant list changes + // type. + if (context->arg_types[0].type_decl->tt_type == VAR_LIST + || context->arg_types[0].type_decl->tt_type == VAR_DICT) + expected_member = context->arg_types[0].type_decl->tt_member; + else + expected_member = context->arg_types[0].type_curr->tt_member; + } + else if (context->arg_types[0].type_curr->tt_type == VAR_STRING) + expected_member = &t_string; + else if (context->arg_types[0].type_curr->tt_type == VAR_BLOB) + expected_member = &t_number; + + args[0] = NULL; + args[1] = &t_unknown; + if (type->tt_argcount != -1) + { + if (!(type->tt_argcount == 2 || (type->tt_argcount == 1 + && (type->tt_flags & TTFLAG_VARARGS)))) + { + emsg(_(e_invalid_number_of_arguments)); + return FAIL; + } + if (type->tt_flags & TTFLAG_VARARGS) + // check the argument types at runtime + t_func_exp.tt_argcount = -1; + else + { + if (context->arg_types[0].type_curr->tt_type == VAR_STRING + || context->arg_types[0].type_curr->tt_type == VAR_BLOB + || context->arg_types[0].type_curr->tt_type == VAR_LIST) + args[0] = &t_number; + else if (context->arg_types[0].type_decl->tt_type == VAR_DICT) + args[0] = &t_string; + if (args[0] != NULL) + args[1] = expected_member; + } + } + + if (!type_any_or_unknown(type->tt_member) || args[0] != NULL) + { + where_T where = WHERE_INIT; + + if (is_map) + t_func_exp.tt_member = expected_member == NULL + || type_any_or_unknown(type->tt_member) + ? &t_any : expected_member; + else + t_func_exp.tt_member = &t_bool; + if (args[0] == NULL) + args[0] = &t_unknown; + if (type->tt_argcount == -1) + t_func_exp.tt_argcount = -1; + + where.wt_index = 2; + where.wt_kind = WT_ARGUMENT; + return check_type(&t_func_exp, type, TRUE, where); + } + return OK; +} + +/* + * Check second argument of filter(): func must return a bool. + */ + static int +arg_filter_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_STRING + || type->tt_type == VAR_PARTIAL + || type_any_or_unknown(type)) + return OK; + + if (type->tt_type == VAR_FUNC) + return check_map_filter_arg2(type, context, FALSE); + semsg(_(e_string_or_function_required_for_argument_nr), 2); + return FAIL; +} + +/* + * Check second argument of map(), the function. + */ + static int +arg_map_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_STRING + || type->tt_type == VAR_PARTIAL + || type_any_or_unknown(type)) + return OK; + + if (type->tt_type == VAR_FUNC) + return check_map_filter_arg2(type, context, TRUE); + semsg(_(e_string_or_function_required_for_argument_nr), 2); + return FAIL; +} + +/* + * Check second argument of sort() and uniq(), the "how" argument. + */ + static int +arg_sort_how(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_STRING + || type->tt_type == VAR_PARTIAL + || type_any_or_unknown(type)) + return OK; + + if (type->tt_type == VAR_FUNC) + { + type_T *(args[2]); + type_T t_func_exp = {VAR_FUNC, 2, 0, 0, &t_number, NULL, args}; + + if (context->arg_types[0].type_curr->tt_type == VAR_LIST) + args[0] = context->arg_types[0].type_curr->tt_member; + else + args[0] = &t_unknown; + if (!type_any_or_unknown(type->tt_member) || args[0] != &t_unknown) + { + where_T where = WHERE_INIT; + + args[1] = args[0]; + if (type->tt_argcount == -1) + t_func_exp.tt_argcount = -1; + where.wt_index = 2; + where.wt_kind = WT_ARGUMENT; + return check_type(&t_func_exp, type, TRUE, where); + } + + return OK; + } + semsg(_(e_string_or_function_required_for_argument_nr), 2); + return FAIL; +} + +/* + * Check an expression argument, can be a string, funcref or partial. + * Also accept a bool, a constant resulting from compiling a string argument. + * Also accept a number, one and zero are accepted. + */ + static int +arg_string_or_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_STRING + || type->tt_type == VAR_PARTIAL + || type->tt_type == VAR_FUNC + || type->tt_type == VAR_BOOL + || type->tt_type == VAR_NUMBER + || type_any_or_unknown(type)) + return OK; + arg_type_mismatch(&t_func_any, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check varargs' "type" are class. + */ + static int +varargs_class(type_T *type UNUSED, + type_T *decl_type UNUSED, + argcontext_T *context) +{ + for (int i = context->arg_idx; i < context->arg_count; ++i) + { + type2_T *types = &context->arg_types[i]; + if (types->type_curr->tt_type != VAR_CLASS) + { + semsg(_(e_class_or_typealias_required_for_argument_nr), i + 1); + return FAIL; + } + } + return OK; +} + +/* + * Check "type" is a list of 'any' or a blob or a string. + */ + static int +arg_string_list_or_blob(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_LIST + || type->tt_type == VAR_BLOB + || type->tt_type == VAR_STRING + || type_any_or_unknown(type)) + return OK; + arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" is a modifiable list of 'any' or a blob or a string. + */ + static int +arg_string_list_or_blob_mod(type_T *type, type_T *decl_type, argcontext_T *context) +{ + if (arg_string_list_or_blob(type, decl_type, context) == FAIL) + return FAIL; + return arg_type_modifiable(type, context->arg_idx + 1); +} + +/* + * Check "type" is a job. + */ + static int +arg_job(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + return check_arg_type(&t_job, type, context); +} + +/* + * Check "type" is a channel or a job. + */ + static int +arg_chan_or_job(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_CHANNEL + || type->tt_type == VAR_JOB + || type_any_or_unknown(type)) + return OK; + arg_type_mismatch(&t_channel, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" can be used as the type_decl of the previous argument. + * Must not be used for the first argcheck_T entry. + */ + static int +arg_same_as_prev(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + type_T *prev_type = context->arg_types[context->arg_idx - 1].type_decl; + + return check_arg_type(prev_type, type, context); +} + +/* + * Check "type" is the same basic type as the previous argument, checks list or + * dict vs other type, but not member type. + * Must not be used for the first argcheck_T entry. + */ + static int +arg_same_struct_as_prev(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + type_T *prev_type = context->arg_types[context->arg_idx - 1].type_curr; + + if (prev_type->tt_type != context->arg_types[context->arg_idx].type_curr->tt_type) + return check_arg_type(prev_type, type, context); + return OK; +} + +/* + * Check "type" is an item of the list or blob of the previous arg. + * Must not be used for the first argcheck_T entry. + */ + static int +arg_item_of_prev(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + type_T *prev_type = context->arg_types[context->arg_idx - 1].type_curr; + type_T *expected; + + if (prev_type->tt_type == VAR_LIST) + expected = prev_type->tt_member; + else if (prev_type->tt_type == VAR_BLOB) + expected = &t_number; + else + // probably VAR_ANY, can't check + return OK; + + return check_arg_type(expected, type, context); +} + +/* + * Check "type" is a string or a number or a list + */ + static int +arg_str_or_nr_or_list(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_STRING + || type->tt_type == VAR_NUMBER + || type->tt_type == VAR_LIST + || type_any_or_unknown(type)) + return OK; + arg_type_mismatch(&t_string, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" is a dict of 'any' or a string + */ + static int +arg_dict_any_or_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_DICT + || type->tt_type == VAR_STRING + || type_any_or_unknown(type)) + return OK; + arg_type_mismatch(&t_string, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" which is the third argument of extend() (number or string or + * any) + */ + static int +arg_extend3(type_T *type, type_T *decl_type, argcontext_T *context) +{ + type_T *first_type = context->arg_types[context->arg_idx - 2].type_curr; + + if (first_type->tt_type == VAR_LIST) + return arg_number(type, decl_type, context); + if (first_type->tt_type == VAR_DICT) + return arg_string(type, decl_type, context); + return OK; +} + +/* + * Check "type" which is the first argument of get() (blob or list or dict or + * funcref) + */ + static int +arg_get1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_BLOB + || type->tt_type == VAR_LIST + || type->tt_type == VAR_DICT + || type->tt_type == VAR_FUNC + || type->tt_type == VAR_PARTIAL + || type_any_or_unknown(type)) + return OK; + + arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" which is the first argument of len() (number or string or + * blob or list or dict) + */ + static int +arg_len1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_STRING + || type->tt_type == VAR_NUMBER + || type->tt_type == VAR_BLOB + || type->tt_type == VAR_LIST + || type->tt_type == VAR_DICT + || type_any_or_unknown(type)) + return OK; + + arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" which is the second argument of remove() (number or string or + * any) + */ + static int +arg_remove2(type_T *type, type_T *decl_type, argcontext_T *context) +{ + type_T *first_type = context->arg_types[context->arg_idx - 1].type_curr; + + if (first_type->tt_type == VAR_LIST || first_type->tt_type == VAR_BLOB) + return arg_number(type, decl_type, context); + if (first_type->tt_type == VAR_DICT) + return arg_string_or_nr(type, decl_type, context); + return OK; +} + +/* + * Check "type" which is the first argument of repeat() (string or number or + * list or any) + */ + static int +arg_repeat1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_STRING + || type->tt_type == VAR_NUMBER + || type->tt_type == VAR_BLOB + || type->tt_type == VAR_LIST + || type_any_or_unknown(type)) + return OK; + + arg_type_mismatch(&t_string, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" which is the first argument of slice() (list or blob or string + * or any) + */ + static int +arg_slice1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_LIST + || type->tt_type == VAR_BLOB + || type->tt_type == VAR_STRING + || type_any_or_unknown(type)) + return OK; + + arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" which is the first argument of count() (string or list or dict + * or any) + */ + static int +arg_string_or_list_or_dict(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_STRING + || type->tt_type == VAR_LIST + || type->tt_type == VAR_DICT + || type_any_or_unknown(type)) + return OK; + + semsg(_(e_string_list_or_dict_required_for_argument_nr), + context->arg_idx + 1); + return FAIL; +} + +/* + * Check "type" which is the first argument of cursor() (number or string or + * list or any) + */ + static int +arg_cursor1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +{ + if (type->tt_type == VAR_NUMBER + || type->tt_type == VAR_STRING + || type->tt_type == VAR_LIST + || type_any_or_unknown(type)) + return OK; + + arg_type_mismatch(&t_number, type, context->arg_idx + 1); + return FAIL; +} + +/* + * Lists of functions that check the argument types of a builtin function. + */ +static argcheck_T arg1_blob[] = {arg_blob}; +static argcheck_T arg1_bool[] = {arg_bool}; +static argcheck_T arg1_buffer[] = {arg_buffer}; +static argcheck_T arg1_buffer_or_dict_any[] = {arg_buffer_or_dict_any}; +static argcheck_T arg1_chan_or_job[] = {arg_chan_or_job}; +static argcheck_T arg1_dict_any[] = {arg_dict_any}; +static argcheck_T arg1_dict_or_string[] = {arg_dict_any_or_string}; +static argcheck_T arg1_float_or_nr[] = {arg_float_or_nr}; +static argcheck_T arg1_job[] = {arg_job}; +static argcheck_T arg1_list_any[] = {arg_list_any}; +static argcheck_T arg1_list_number[] = {arg_list_number}; +static argcheck_T arg1_string_or_list_or_blob_mod[] = {arg_string_list_or_blob_mod}; +static argcheck_T arg1_list_or_dict[] = {arg_list_or_dict}; +static argcheck_T arg1_list_string[] = {arg_list_string}; +static argcheck_T arg1_string_or_list_or_dict[] = {arg_string_or_list_or_dict}; +static argcheck_T arg1_lnum[] = {arg_lnum}; +static argcheck_T arg1_number[] = {arg_number}; +static argcheck_T arg1_string[] = {arg_string}; +static argcheck_T arg1_string_or_list_any[] = {arg_string_or_list_any}; +static argcheck_T arg1_string_or_list_string[] = {arg_string_or_list_string}; +static argcheck_T arg1_string_or_nr[] = {arg_string_or_nr}; +static argcheck_T arg2_any_buffer[] = {arg_any, arg_buffer}; +static argcheck_T arg2_buffer_any[] = {arg_buffer, arg_any}; +static argcheck_T arg2_buffer_bool[] = {arg_buffer, arg_bool}; +static argcheck_T arg2_buffer_list_any[] = {arg_buffer, arg_list_any}; +static argcheck_T arg2_buffer_lnum[] = {arg_buffer, arg_lnum}; +static argcheck_T arg2_buffer_number[] = {arg_buffer, arg_number}; +static argcheck_T arg2_buffer_string[] = {arg_buffer, arg_string}; +static argcheck_T arg2_chan_or_job_dict[] = {arg_chan_or_job, arg_dict_any}; +static argcheck_T arg2_chan_or_job_string[] = {arg_chan_or_job, arg_string}; +static argcheck_T arg2_dict_any_list_any[] = {arg_dict_any, arg_list_any}; +static argcheck_T arg2_dict_any_string_or_nr[] = {arg_dict_any, arg_string_or_nr}; +static argcheck_T arg2_dict_string[] = {arg_dict_any, arg_string}; +static argcheck_T arg2_float_or_nr[] = {arg_float_or_nr, arg_float_or_nr}; +static argcheck_T arg2_job_dict[] = {arg_job, arg_dict_any}; +static argcheck_T arg2_job_string_or_number[] = {arg_job, arg_string_or_nr}; +static argcheck_T arg2_list_any_number[] = {arg_list_any, arg_number}; +static argcheck_T arg2_list_any_string[] = {arg_list_any, arg_string}; +static argcheck_T arg2_list_number[] = {arg_list_number, arg_list_number}; +static argcheck_T arg2_list_number_bool[] = {arg_list_number, arg_bool}; +static argcheck_T arg2_listblobmod_item[] = {arg_list_or_blob_mod, arg_item_of_prev}; +static argcheck_T arg2_lnum[] = {arg_lnum, arg_lnum}; +static argcheck_T arg2_lnum_number[] = {arg_lnum, arg_number}; +static argcheck_T arg2_number[] = {arg_number, arg_number}; +static argcheck_T arg2_number_any[] = {arg_number, arg_any}; +static argcheck_T arg2_number_bool[] = {arg_number, arg_bool}; +static argcheck_T arg2_number_dict_any[] = {arg_number, arg_dict_any}; +static argcheck_T arg2_number_list[] = {arg_number, arg_list_any}; +static argcheck_T arg2_number_string[] = {arg_number, arg_string}; +static argcheck_T arg2_number_string_or_list[] = {arg_number, arg_string_or_list_any}; +static argcheck_T arg2_str_or_nr_or_list_dict[] = {arg_str_or_nr_or_list, arg_dict_any}; +static argcheck_T arg2_string[] = {arg_string, arg_string}; +static argcheck_T arg2_string_any[] = {arg_string, arg_any}; +static argcheck_T arg2_string_bool[] = {arg_string, arg_bool}; +static argcheck_T arg2_string_chan_or_job[] = {arg_string, arg_chan_or_job}; +static argcheck_T arg2_string_dict[] = {arg_string, arg_dict_any}; +static argcheck_T arg2_string_list_number[] = {arg_string, arg_list_number}; +static argcheck_T arg2_string_number[] = {arg_string, arg_number}; +static argcheck_T arg2_string_or_list_dict[] = {arg_string_or_list_any, arg_dict_any}; +static argcheck_T arg2_string_or_list_number[] = {arg_string_or_list_any, arg_number}; +static argcheck_T arg2_string_string_or_number[] = {arg_string, arg_string_or_nr}; +static argcheck_T arg3_any_list_dict[] = {arg_any, arg_list_any, arg_dict_any}; +static argcheck_T arg3_buffer_lnum_lnum[] = {arg_buffer, arg_lnum, arg_lnum}; +static argcheck_T arg3_buffer_number_number[] = {arg_buffer, arg_number, arg_number}; +static argcheck_T arg3_buffer_string_any[] = {arg_buffer, arg_string, arg_any}; +static argcheck_T arg3_buffer_string_dict[] = {arg_buffer, arg_string, arg_dict_any}; +static argcheck_T arg3_dict_number_number[] = {arg_dict_any, arg_number, arg_number}; +static argcheck_T arg3_list_string_dict[] = {arg_list_any, arg_string, arg_dict_any}; +static argcheck_T arg3_lnum_number_bool[] = {arg_lnum, arg_number, arg_bool}; +static argcheck_T arg3_number[] = {arg_number, arg_number, arg_number}; +static argcheck_T arg3_number_any_dict[] = {arg_number, arg_any, arg_dict_any}; +static argcheck_T arg3_number_number_dict[] = {arg_number, arg_number, arg_dict_any}; +static argcheck_T arg3_number_string_any[] = {arg_number, arg_string, arg_any}; +static argcheck_T arg3_number_string_buffer[] = {arg_number, arg_string, arg_buffer}; +static argcheck_T arg3_number_string_string[] = {arg_number, arg_string, arg_string}; +static argcheck_T arg3_string[] = {arg_string, arg_string, arg_string}; +static argcheck_T arg3_string_any_dict[] = {arg_string, arg_any, arg_dict_any}; +static argcheck_T arg3_string_any_string[] = {arg_string, arg_any, arg_string}; +static argcheck_T arg3_string_bool_bool[] = {arg_string, arg_bool, arg_bool}; +static argcheck_T arg3_string_number_bool[] = {arg_string, arg_number, arg_bool}; +static argcheck_T arg3_string_number_number[] = {arg_string, arg_number, arg_number}; +static argcheck_T arg3_string_or_dict_bool_dict[] = {arg_string_or_dict_any, arg_bool, arg_dict_any}; +static argcheck_T arg3_string_or_list_bool_number[] = {arg_string_or_list_any, arg_bool, arg_number}; +static argcheck_T arg3_string_string_bool[] = {arg_string, arg_string, arg_bool}; +static argcheck_T arg3_string_string_dict[] = {arg_string, arg_string, arg_dict_any}; +static argcheck_T arg3_string_string_number[] = {arg_string, arg_string, arg_number}; +static argcheck_T arg4_number_number_string_any[] = {arg_number, arg_number, arg_string, arg_any}; +static argcheck_T arg4_string_string_any_string[] = {arg_string, arg_string, arg_any, arg_string}; +static argcheck_T arg4_string_string_number_string[] = {arg_string, arg_string, arg_number, arg_string}; +static argcheck_T arg4_string_number_bool_bool[] = {arg_string, arg_number, arg_bool, arg_bool}; +/* Function specific argument types (not covered by the above) */ +static argcheck_T arg15_assert_fails[] = {arg_string_or_nr, arg_string_or_list_any, arg_any, arg_number, arg_string}; +static argcheck_T arg34_assert_inrange[] = {arg_float_or_nr, arg_float_or_nr, arg_float_or_nr, arg_string}; +static argcheck_T arg4_browse[] = {arg_bool, arg_string, arg_string, arg_string}; +static argcheck_T arg23_chanexpr[] = {arg_chan_or_job, arg_any, arg_dict_any}; +static argcheck_T arg23_chanraw[] = {arg_chan_or_job, arg_string_or_blob, arg_dict_any}; +static argcheck_T arg24_count[] = {arg_string_or_list_or_dict, arg_any, arg_bool, arg_number}; +static argcheck_T arg13_cursor[] = {arg_cursor1, arg_number, arg_number}; +static argcheck_T arg12_deepcopy[] = {arg_any, arg_bool}; +static argcheck_T arg12_execute[] = {arg_string_or_list_string, arg_string}; +static argcheck_T arg23_extend[] = {arg_list_or_dict_mod, arg_same_as_prev, arg_extend3}; +static argcheck_T arg23_extendnew[] = {arg_list_or_dict, arg_same_struct_as_prev, arg_extend3}; +static argcheck_T arg23_get[] = {arg_get1, arg_string_or_nr, arg_any}; +static argcheck_T arg14_glob[] = {arg_string, arg_bool, arg_bool, arg_bool}; +static argcheck_T arg25_globpath[] = {arg_string, arg_string, arg_bool, arg_bool, arg_bool}; +static argcheck_T arg24_index[] = {arg_list_or_blob, arg_item_of_prev, arg_number, arg_bool}; +static argcheck_T arg23_index[] = {arg_list_or_blob, arg_filter_func, arg_dict_any}; +static argcheck_T arg23_insert[] = {arg_list_or_blob, arg_item_of_prev, arg_number}; +static argcheck_T arg1_len[] = {arg_len1}; +static argcheck_T arg3_libcall[] = {arg_string, arg_string, arg_string_or_nr}; +static argcheck_T arg14_maparg[] = {arg_string, arg_string, arg_bool, arg_bool}; +static argcheck_T arg2_filter[] = {arg_list_or_dict_or_blob_or_string_mod, arg_filter_func}; +static argcheck_T arg2_instanceof[] = {arg_object, varargs_class, NULL }; +static argcheck_T arg2_map[] = {arg_list_or_dict_or_blob_or_string_mod, arg_map_func}; +static argcheck_T arg2_mapnew[] = {arg_list_or_dict_or_blob_or_string, arg_any}; +static argcheck_T arg25_matchadd[] = {arg_string, arg_string, arg_number, arg_number, arg_dict_any}; +static argcheck_T arg25_matchaddpos[] = {arg_string, arg_list_any, arg_number, arg_number, arg_dict_any}; +static argcheck_T arg23_matchstrlist[] = {arg_list_string, arg_string, arg_dict_any}; +static argcheck_T arg45_matchbufline[] = {arg_buffer, arg_string, arg_lnum, arg_lnum, arg_dict_any}; +static argcheck_T arg119_printf[] = {arg_string_or_nr, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any}; +static argcheck_T arg23_reduce[] = {arg_string_list_or_blob, arg_any, arg_any}; +static argcheck_T arg24_remote_expr[] = {arg_string, arg_string, arg_string, arg_number}; +static argcheck_T arg23_remove[] = {arg_list_or_dict_or_blob_mod, arg_remove2, arg_number}; +static argcheck_T arg2_repeat[] = {arg_repeat1, arg_number}; +static argcheck_T arg15_search[] = {arg_string, arg_string, arg_number, arg_number, arg_string_or_func}; +static argcheck_T arg37_searchpair[] = {arg_string, arg_string, arg_string, arg_string, arg_string_or_func, arg_number, arg_number}; +static argcheck_T arg3_setbufline[] = {arg_buffer, arg_lnum, arg_str_or_nr_or_list}; +static argcheck_T arg2_setline[] = {arg_lnum, arg_any}; +static argcheck_T arg24_setloclist[] = {arg_number, arg_list_any, arg_string, arg_dict_any}; +static argcheck_T arg13_setqflist[] = {arg_list_any, arg_string, arg_dict_any}; +static argcheck_T arg23_settagstack[] = {arg_number, arg_dict_any, arg_string}; +static argcheck_T arg02_sign_getplaced[] = {arg_buffer, arg_dict_any}; +static argcheck_T arg45_sign_place[] = {arg_number, arg_string, arg_string, arg_buffer, arg_dict_any}; +static argcheck_T arg23_slice[] = {arg_slice1, arg_number, arg_number}; +static argcheck_T arg13_sortuniq[] = {arg_list_any_mod, arg_sort_how, arg_dict_any}; +static argcheck_T arg24_strpart[] = {arg_string, arg_number, arg_number, arg_bool}; +static argcheck_T arg12_system[] = {arg_string, arg_str_or_nr_or_list}; +static argcheck_T arg23_win_execute[] = {arg_number, arg_string_or_list_string, arg_string}; +static argcheck_T arg23_writefile[] = {arg_list_or_blob, arg_string, arg_string}; +static argcheck_T arg24_match_func[] = {arg_string_or_list_any, arg_string, arg_number, arg_number}; + +// Can be used by functions called through "f_retfunc" to create new types. +static garray_T *current_type_gap = NULL; + +/* + * Functions that return the return type of a builtin function. + * Note that "argtypes" is NULL if "argcount" is zero. + */ + static type_T * +ret_void(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + return &t_void; +} + static type_T * +ret_any(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + return &t_any; +} + static type_T * +ret_bool(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + return &t_bool; +} + static type_T * +ret_number_bool(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + return &t_number_bool; +} + static type_T * +ret_number(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + return &t_number; +} + static type_T * +ret_float(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + return &t_float; +} + static type_T * +ret_string(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + return &t_string; +} + static type_T * +ret_list_any(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + return &t_list_any; +} + static type_T * +ret_list_number(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type) +{ + *decl_type = &t_list_any; + return &t_list_number; +} + static type_T * +ret_list_string(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type) +{ + *decl_type = &t_list_any; + return &t_list_string; +} + static type_T * +ret_list_dict_any(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type) +{ + *decl_type = &t_list_any; + return &t_list_dict_any; +} + static type_T * +ret_list_items(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type) +{ + *decl_type = &t_list_any; + return &t_list_list_any; +} + + static type_T * +ret_list_string_items(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type) +{ + *decl_type = &t_list_any; + return &t_list_list_string; +} + static type_T * +ret_dict_any(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + return &t_dict_any; +} + static type_T * +ret_job_info(int argcount, + type2_T *argtypes UNUSED, + type_T **decl_type) +{ + if (argcount == 0) + { + *decl_type = &t_list_any; + return &t_list_job; + } + return &t_dict_any; +} + static type_T * +ret_dict_number(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + return &t_dict_number; +} + static type_T * +ret_dict_string(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + return &t_dict_string; +} + static type_T * +ret_blob(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + return &t_blob; +} + static type_T * +ret_func_any(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + return &t_func_any; +} + static type_T * +ret_func_unknown(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + return &t_func_unknown; +} + static type_T * +ret_channel(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + return &t_channel; +} + static type_T * +ret_job(int argcount UNUSED, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + return &t_job; +} + static type_T * +ret_first_arg(int argcount, + type2_T *argtypes, + type_T **decl_type) +{ + if (argcount > 0) + { + *decl_type = argtypes[0].type_decl; + return argtypes[0].type_curr; + } + return &t_void; +} + static type_T * +ret_slice(int argcount, + type2_T *argtypes, + type_T **decl_type) +{ + if (argcount > 0) + { + if (argtypes[0].type_decl != NULL) + { + switch (argtypes[0].type_decl->tt_type) + { + case VAR_STRING: *decl_type = &t_string; break; + case VAR_BLOB: *decl_type = &t_blob; break; + case VAR_LIST: *decl_type = &t_list_any; break; + default: break; + } + } + return argtypes[0].type_curr; + } + return &t_void; +} + static type_T * +ret_copy(int argcount, + type2_T *argtypes, + type_T **decl_type) +{ + if (argcount > 0) + { + if (argtypes[0].type_decl != NULL) + { + if (argtypes[0].type_decl->tt_type == VAR_LIST) + *decl_type = &t_list_any; + else if (argtypes[0].type_decl->tt_type == VAR_DICT) + *decl_type = &t_dict_any; + else + *decl_type = argtypes[0].type_decl; + } + if (argtypes[0].type_curr != NULL) + { + if (argtypes[0].type_curr->tt_type == VAR_LIST) + return &t_list_any; + else if (argtypes[0].type_curr->tt_type == VAR_DICT) + return &t_dict_any; + } + return argtypes[0].type_curr; + } + return &t_void; +} + static type_T * +ret_extend(int argcount, + type2_T *argtypes, + type_T **decl_type) +{ + if (argcount > 0) + { + *decl_type = argtypes[0].type_decl; + // if the second argument has a different current type then the current + // type is "any" + if (argcount > 1 && !equal_type(argtypes[0].type_curr, + argtypes[1].type_curr, 0)) + { + if (argtypes[0].type_curr->tt_type == VAR_LIST) + return &t_list_any; + if (argtypes[0].type_curr->tt_type == VAR_DICT) + return &t_dict_any; + } + return argtypes[0].type_curr; + } + return &t_void; +} + static type_T * +ret_repeat(int argcount, + type2_T *argtypes, + type_T **decl_type UNUSED) +{ + if (argcount == 0) + return &t_any; + if (argtypes[0].type_curr == &t_number) + return &t_string; + return argtypes[0].type_curr; +} +// for map(): returns first argument but item type may differ + static type_T * +ret_first_cont(int argcount, + type2_T *argtypes, + type_T **decl_type UNUSED) +{ + if (argcount > 0) + { + if (argtypes[0].type_curr->tt_type == VAR_LIST) + return &t_list_any; + if (argtypes[0].type_curr->tt_type == VAR_DICT) + return &t_dict_any; + if (argtypes[0].type_curr->tt_type == VAR_BLOB) + return argtypes[0].type_curr; + } + return &t_any; +} +// for getline() + static type_T * +ret_getline(int argcount, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + if (argcount == 1) + return &t_string; + *decl_type = &t_list_any; + return &t_list_string; +} +// for finddir() + static type_T * +ret_finddir(int argcount, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + if (argcount < 3) + return &t_string; + // Depending on the count would be a string or a list of strings. + return &t_any; +} +// for values(): list of member of first argument + static type_T * +ret_list_member(int argcount, + type2_T *argtypes, + type_T **decl_type) +{ + if (argcount > 0) + { + type_T *t = argtypes[0].type_decl; + if (current_type_gap != NULL + && (t->tt_type == VAR_DICT || t->tt_type == VAR_LIST)) + t = get_list_type(t->tt_member, current_type_gap); + else + t = &t_list_any; + *decl_type = t; + + t = argtypes[0].type_curr; + if (current_type_gap != NULL + && (t->tt_type == VAR_DICT || t->tt_type == VAR_LIST)) + return get_list_type(t->tt_member, current_type_gap); + } + return &t_list_any; +} + +/* + * Used for getqflist(): returns list if there is no argument, dict if there is + * one. + */ + static type_T * +ret_list_or_dict_0(int argcount, + type2_T *argtypes UNUSED, + type_T **decl_type) +{ + if (argcount > 0) + return &t_dict_any; + *decl_type = &t_list_any; + return &t_list_dict_any; +} + +/* + * Used for getloclist(): returns list if there is one argument, dict if there + * are two. + */ + static type_T * +ret_list_or_dict_1(int argcount, + type2_T *argtypes UNUSED, + type_T **decl_type) +{ + if (argcount > 1) + return &t_dict_any; + *decl_type = &t_list_any; + return &t_list_dict_any; +} + + static type_T * +ret_argv(int argcount, + type2_T *argtypes UNUSED, + type_T **decl_type) +{ + // argv() returns list of strings + if (argcount == 0) + { + *decl_type = &t_list_any; + return &t_list_string; + } + + // argv(0) returns a string, but argv(-1] returns a list + return &t_any; +} + + static type_T * +ret_remove(int argcount, + type2_T *argtypes, + type_T **decl_type) +{ + if (argcount > 0) + { + if (argtypes[0].type_curr->tt_type == VAR_LIST + || argtypes[0].type_curr->tt_type == VAR_DICT) + { + if (argcount == 3) + { + *decl_type = argtypes[0].type_decl; + return argtypes[0].type_curr; + } + if (argtypes[0].type_curr->tt_type + == argtypes[0].type_decl->tt_type) + *decl_type = argtypes[0].type_decl->tt_member; + return argtypes[0].type_curr->tt_member; + } + if (argtypes[0].type_curr->tt_type == VAR_BLOB) + return &t_number; + } + return &t_any; +} + + static type_T * +ret_getreg(int argcount, + type2_T *argtypes UNUSED, + type_T **decl_type) +{ + // Assume that if the third argument is passed it's non-zero + if (argcount == 3) + { + *decl_type = &t_list_any; + return &t_list_string; + } + return &t_string; +} + + static type_T * +ret_virtcol(int argcount, + type2_T *argtypes UNUSED, + type_T **decl_type) +{ + // Assume that if the second argument is passed it's non-zero + if (argcount > 1) + { + *decl_type = &t_list_any; + return &t_list_number; + } + return &t_number; +} + + static type_T * +ret_maparg(int argcount, + type2_T *argtypes UNUSED, + type_T **decl_type UNUSED) +{ + // Assume that if the fourth argument is passed it's non-zero + if (argcount == 4) + return &t_dict_any; + return &t_string; +} + +/* + * Array with names and number of arguments of all internal functions + * MUST BE KEPT SORTED IN strcmp() ORDER FOR BINARY SEARCH! + * + * The builtin function may be varargs. In that case + * - f_max_argc == VARGS + * - For varargs, f_argcheck must be NULL terminated. The last non-null + * entry in f_argcheck should validate all the remaining args. + */ +typedef struct +{ + char *f_name; // function name + char f_min_argc; // minimal number of arguments + char f_max_argc; // maximal number of arguments + char f_argtype; // for method: FEARG_ values; bits FE_ + argcheck_T *f_argcheck; // list of functions to check argument types; + // use "arg_any" (not NULL) to accept an + // argument of any type + type_T *(*f_retfunc)(int argcount, type2_T *argtypes, + type_T **decl_type); + // return type function + void (*f_func)(typval_T *args, typval_T *rvar); + // implementation of function +} funcentry_T; + +// Set f_max_argc to VARGS for varargs. +#define VARGS CHAR_MAX + +// values for f_argtype; zero means it cannot be used as a method +#define FEARG_1 0x01 // base is the first argument +#define FEARG_2 0x02 // base is the second argument +#define FEARG_3 0x03 // base is the third argument +#define FEARG_4 0x04 // base is the fourth argument +#define FEARG_MASK 0x0F // bits in f_argtype used as argument index +#define FE_X 0x10 // builtin accepts a non-value (class, typealias) + +#if defined(HAVE_MATH_H) +# define MATH_FUNC(name) name +#else +# define MATH_FUNC(name) NULL +#endif +#ifdef FEAT_TIMERS +# define TIMER_FUNC(name) name +#else +# define TIMER_FUNC(name) NULL +#endif +#ifdef FEAT_JOB_CHANNEL +# define JOB_FUNC(name) name +#else +# define JOB_FUNC(name) NULL +#endif +#ifdef FEAT_PROP_POPUP +# define PROP_FUNC(name) name +#else +# define PROP_FUNC(name) NULL +#endif +#ifdef FEAT_SIGNS +# define SIGN_FUNC(name) name +#else +# define SIGN_FUNC(name) NULL +#endif +#ifdef FEAT_SOUND +# define SOUND_FUNC(name) name +#else +# define SOUND_FUNC(name) NULL +#endif +#ifdef FEAT_TERMINAL +# define TERM_FUNC(name) name +#else +# define TERM_FUNC(name) NULL +#endif + +static funcentry_T global_functions[] = +{ + {"abs", 1, 1, FEARG_1, arg1_float_or_nr, + ret_any, f_abs}, + {"acos", 1, 1, FEARG_1, arg1_float_or_nr, + ret_float, f_acos}, + {"add", 2, 2, FEARG_1, arg2_listblobmod_item, + ret_first_arg, f_add}, + {"and", 2, 2, FEARG_1, arg2_number, + ret_number, f_and}, + {"append", 2, 2, FEARG_2, arg2_setline, + ret_number_bool, f_append}, + {"appendbufline", 3, 3, FEARG_3, arg3_setbufline, + ret_number_bool, f_appendbufline}, + {"argc", 0, 1, 0, arg1_number, + ret_number, f_argc}, + {"argidx", 0, 0, 0, NULL, + ret_number, f_argidx}, + {"arglistid", 0, 2, 0, arg2_number, + ret_number, f_arglistid}, + {"argv", 0, 2, 0, arg2_number, + ret_argv, f_argv}, + {"asin", 1, 1, FEARG_1, arg1_float_or_nr, + ret_float, f_asin}, + {"assert_beeps", 1, 1, FEARG_1, arg1_string, + ret_number_bool, f_assert_beeps}, + {"assert_equal", 2, 3, FEARG_2, NULL, + ret_number_bool, f_assert_equal}, + {"assert_equalfile", 2, 3, FEARG_1, arg3_string, + ret_number_bool, f_assert_equalfile}, + {"assert_exception", 1, 2, 0, arg2_string, + ret_number_bool, f_assert_exception}, + {"assert_fails", 1, 5, FEARG_1, arg15_assert_fails, + ret_number_bool, f_assert_fails}, + {"assert_false", 1, 2, FEARG_1, NULL, + ret_number_bool, f_assert_false}, + {"assert_inrange", 3, 4, FEARG_3, arg34_assert_inrange, + ret_number_bool, f_assert_inrange}, + {"assert_match", 2, 3, FEARG_2, arg3_string, + ret_number_bool, f_assert_match}, + {"assert_nobeep", 1, 1, FEARG_1, arg1_string, + ret_number_bool, f_assert_nobeep}, + {"assert_notequal", 2, 3, FEARG_2, NULL, + ret_number_bool, f_assert_notequal}, + {"assert_notmatch", 2, 3, FEARG_2, arg3_string, + ret_number_bool, f_assert_notmatch}, + {"assert_report", 1, 1, FEARG_1, arg1_string, + ret_number_bool, f_assert_report}, + {"assert_true", 1, 2, FEARG_1, NULL, + ret_number_bool, f_assert_true}, + {"atan", 1, 1, FEARG_1, arg1_float_or_nr, + ret_float, f_atan}, + {"atan2", 2, 2, FEARG_1, arg2_float_or_nr, + ret_float, f_atan2}, + {"autocmd_add", 1, 1, FEARG_1, arg1_list_any, + ret_number_bool, f_autocmd_add}, + {"autocmd_delete", 1, 1, FEARG_1, arg1_list_any, + ret_number_bool, f_autocmd_delete}, + {"autocmd_get", 0, 1, FEARG_1, arg1_dict_any, + ret_list_dict_any, f_autocmd_get}, + {"balloon_gettext", 0, 0, 0, NULL, + ret_string, +#ifdef FEAT_BEVAL + f_balloon_gettext +#else + NULL +#endif + }, + {"balloon_show", 1, 1, FEARG_1, arg1_string_or_list_any, + ret_void, +#ifdef FEAT_BEVAL + f_balloon_show +#else + NULL +#endif + }, + {"balloon_split", 1, 1, FEARG_1, arg1_string, + ret_list_string, +#if defined(FEAT_BEVAL_TERM) + f_balloon_split +#else + NULL +#endif + }, + {"blob2list", 1, 1, FEARG_1, arg1_blob, + ret_list_number, f_blob2list}, + {"browse", 4, 4, 0, arg4_browse, + ret_string, f_browse}, + {"browsedir", 2, 2, 0, arg2_string, + ret_string, f_browsedir}, + {"bufadd", 1, 1, FEARG_1, arg1_string, + ret_number, f_bufadd}, + {"bufexists", 1, 1, FEARG_1, arg1_buffer, + ret_number_bool, f_bufexists}, + {"buffer_exists", 1, 1, FEARG_1, arg1_buffer, // obsolete + ret_number_bool, f_bufexists}, + {"buffer_name", 0, 1, FEARG_1, arg1_buffer, // obsolete + ret_string, f_bufname}, + {"buffer_number", 0, 1, FEARG_1, arg1_buffer, // obsolete + ret_number, f_bufnr}, + {"buflisted", 1, 1, FEARG_1, arg1_buffer, + ret_number_bool, f_buflisted}, + {"bufload", 1, 1, FEARG_1, arg1_buffer, + ret_void, f_bufload}, + {"bufloaded", 1, 1, FEARG_1, arg1_buffer, + ret_number_bool, f_bufloaded}, + {"bufname", 0, 1, FEARG_1, arg1_buffer, + ret_string, f_bufname}, + {"bufnr", 0, 2, FEARG_1, arg2_buffer_bool, + ret_number, f_bufnr}, + {"bufwinid", 1, 1, FEARG_1, arg1_buffer, + ret_number, f_bufwinid}, + {"bufwinnr", 1, 1, FEARG_1, arg1_buffer, + ret_number, f_bufwinnr}, + {"byte2line", 1, 1, FEARG_1, arg1_number, + ret_number, f_byte2line}, + {"byteidx", 2, 3, FEARG_1, arg3_string_number_bool, + ret_number, f_byteidx}, + {"byteidxcomp", 2, 3, FEARG_1, arg3_string_number_bool, + ret_number, f_byteidxcomp}, + {"call", 2, 3, FEARG_1, arg3_any_list_dict, + ret_any, f_call}, + {"ceil", 1, 1, FEARG_1, arg1_float_or_nr, + ret_float, f_ceil}, + {"ch_canread", 1, 1, FEARG_1, arg1_chan_or_job, + ret_number_bool, JOB_FUNC(f_ch_canread)}, + {"ch_close", 1, 1, FEARG_1, arg1_chan_or_job, + ret_void, JOB_FUNC(f_ch_close)}, + {"ch_close_in", 1, 1, FEARG_1, arg1_chan_or_job, + ret_void, JOB_FUNC(f_ch_close_in)}, + {"ch_evalexpr", 2, 3, FEARG_1, arg23_chanexpr, + ret_any, JOB_FUNC(f_ch_evalexpr)}, + {"ch_evalraw", 2, 3, FEARG_1, arg23_chanraw, + ret_any, JOB_FUNC(f_ch_evalraw)}, + {"ch_getbufnr", 2, 2, FEARG_1, arg2_chan_or_job_string, + ret_number, JOB_FUNC(f_ch_getbufnr)}, + {"ch_getjob", 1, 1, FEARG_1, arg1_chan_or_job, + ret_job, JOB_FUNC(f_ch_getjob)}, + {"ch_info", 1, 1, FEARG_1, arg1_chan_or_job, + ret_dict_any, JOB_FUNC(f_ch_info)}, + {"ch_log", 1, 2, FEARG_1, arg2_string_chan_or_job, + ret_void, f_ch_log}, + {"ch_logfile", 1, 2, FEARG_1, arg2_string, + ret_void, f_ch_logfile}, + {"ch_open", 1, 2, FEARG_1, arg2_string_dict, + ret_channel, JOB_FUNC(f_ch_open)}, + {"ch_read", 1, 2, FEARG_1, arg2_chan_or_job_dict, + ret_string, JOB_FUNC(f_ch_read)}, + {"ch_readblob", 1, 2, FEARG_1, arg2_chan_or_job_dict, + ret_blob, JOB_FUNC(f_ch_readblob)}, + {"ch_readraw", 1, 2, FEARG_1, arg2_chan_or_job_dict, + ret_string, JOB_FUNC(f_ch_readraw)}, + {"ch_sendexpr", 2, 3, FEARG_1, arg23_chanexpr, + ret_any, JOB_FUNC(f_ch_sendexpr)}, + {"ch_sendraw", 2, 3, FEARG_1, arg23_chanraw, + ret_void, JOB_FUNC(f_ch_sendraw)}, + {"ch_setoptions", 2, 2, FEARG_1, arg2_chan_or_job_dict, + ret_void, JOB_FUNC(f_ch_setoptions)}, + {"ch_status", 1, 2, FEARG_1, arg2_chan_or_job_dict, + ret_string, JOB_FUNC(f_ch_status)}, + {"changenr", 0, 0, 0, NULL, + ret_number, f_changenr}, + {"char2nr", 1, 2, FEARG_1, arg2_string_bool, + ret_number, f_char2nr}, + {"charclass", 1, 1, FEARG_1, arg1_string, + ret_number, f_charclass}, + {"charcol", 1, 2, FEARG_1, arg2_string_or_list_number, + ret_number, f_charcol}, + {"charidx", 2, 4, FEARG_1, arg4_string_number_bool_bool, + ret_number, f_charidx}, + {"chdir", 1, 1, FEARG_1, arg1_string, + ret_string, f_chdir}, + {"cindent", 1, 1, FEARG_1, arg1_lnum, + ret_number, f_cindent}, + {"clearmatches", 0, 1, FEARG_1, arg1_number, + ret_void, f_clearmatches}, + {"col", 1, 2, FEARG_1, arg2_string_or_list_number, + ret_number, f_col}, + {"complete", 2, 2, FEARG_2, arg2_number_list, + ret_void, f_complete}, + {"complete_add", 1, 1, FEARG_1, arg1_dict_or_string, + ret_number, f_complete_add}, + {"complete_check", 0, 0, 0, NULL, + ret_number_bool, f_complete_check}, + {"complete_info", 0, 1, FEARG_1, arg1_list_string, + ret_dict_any, f_complete_info}, + {"confirm", 1, 4, FEARG_1, arg4_string_string_number_string, + ret_number, f_confirm}, + {"copy", 1, 1, FEARG_1, NULL, + ret_copy, f_copy}, + {"cos", 1, 1, FEARG_1, arg1_float_or_nr, + ret_float, f_cos}, + {"cosh", 1, 1, FEARG_1, arg1_float_or_nr, + ret_float, f_cosh}, + {"count", 2, 4, FEARG_1, arg24_count, + ret_number, f_count}, + {"cscope_connection",0,3, 0, arg3_number_string_string, + ret_number, f_cscope_connection}, + {"cursor", 1, 3, FEARG_1, arg13_cursor, + ret_number, f_cursor}, + {"debugbreak", 1, 1, FEARG_1, arg1_number, + ret_number, +#ifdef MSWIN + f_debugbreak +#else + NULL +#endif + }, + {"deepcopy", 1, 2, FEARG_1, arg12_deepcopy, + ret_copy, f_deepcopy}, + {"delete", 1, 2, FEARG_1, arg2_string, + ret_number_bool, f_delete}, + {"deletebufline", 2, 3, FEARG_1, arg3_buffer_lnum_lnum, + ret_number_bool, f_deletebufline}, + {"did_filetype", 0, 0, 0, NULL, + ret_number_bool, f_did_filetype}, + {"diff_filler", 1, 1, FEARG_1, arg1_lnum, + ret_number, f_diff_filler}, + {"diff_hlID", 2, 2, FEARG_1, arg2_lnum_number, + ret_number, f_diff_hlID}, + {"digraph_get", 1, 1, FEARG_1, arg1_string, + ret_string, f_digraph_get}, + {"digraph_getlist",0, 1, FEARG_1, arg1_bool, + ret_list_string_items, f_digraph_getlist}, + {"digraph_set", 2, 2, FEARG_1, arg2_string, + ret_bool, f_digraph_set}, + {"digraph_setlist",1, 1, FEARG_1, arg1_list_string, + ret_bool, f_digraph_setlist}, + {"echoraw", 1, 1, FEARG_1, arg1_string, + ret_void, f_echoraw}, + {"empty", 1, 1, FEARG_1, NULL, + ret_number_bool, f_empty}, + {"environ", 0, 0, 0, NULL, + ret_dict_string, f_environ}, + {"err_teapot", 0, 1, 0, NULL, + ret_number_bool, f_err_teapot}, + {"escape", 2, 2, FEARG_1, arg2_string, + ret_string, f_escape}, + {"eval", 1, 1, FEARG_1, arg1_string, + ret_any, f_eval}, + {"eventhandler", 0, 0, 0, NULL, + ret_number_bool, f_eventhandler}, + {"executable", 1, 1, FEARG_1, arg1_string, + ret_number, f_executable}, + {"execute", 1, 2, FEARG_1, arg12_execute, + ret_string, f_execute}, + {"exepath", 1, 1, FEARG_1, arg1_string, + ret_string, f_exepath}, + {"exists", 1, 1, FEARG_1, arg1_string, + ret_number_bool, f_exists}, + {"exists_compiled", 1, 1, FEARG_1, arg1_string, + ret_number_bool, f_exists_compiled}, + {"exp", 1, 1, FEARG_1, arg1_float_or_nr, + ret_float, f_exp}, + {"expand", 1, 3, FEARG_1, arg3_string_bool_bool, + ret_any, f_expand}, + {"expandcmd", 1, 2, FEARG_1, arg2_string_dict, + ret_string, f_expandcmd}, + {"extend", 2, 3, FEARG_1, arg23_extend, + ret_extend, f_extend}, + {"extendnew", 2, 3, FEARG_1, arg23_extendnew, + ret_first_cont, f_extendnew}, + {"feedkeys", 1, 2, FEARG_1, arg2_string, + ret_void, f_feedkeys}, + {"file_readable", 1, 1, FEARG_1, arg1_string, // obsolete + ret_number_bool, f_filereadable}, + {"filereadable", 1, 1, FEARG_1, arg1_string, + ret_number_bool, f_filereadable}, + {"filewritable", 1, 1, FEARG_1, arg1_string, + ret_number, f_filewritable}, + {"filter", 2, 2, FEARG_1, arg2_filter, + ret_first_arg, f_filter}, + {"finddir", 1, 3, FEARG_1, arg3_string_string_number, + ret_finddir, f_finddir}, + {"findfile", 1, 3, FEARG_1, arg3_string_string_number, + ret_any, f_findfile}, + {"flatten", 1, 2, FEARG_1, arg2_list_any_number, + ret_list_any, f_flatten}, + {"flattennew", 1, 2, FEARG_1, arg2_list_any_number, + ret_list_any, f_flattennew}, + {"float2nr", 1, 1, FEARG_1, arg1_float_or_nr, + ret_number, f_float2nr}, + {"floor", 1, 1, FEARG_1, arg1_float_or_nr, + ret_float, f_floor}, + {"fmod", 2, 2, FEARG_1, arg2_float_or_nr, + ret_float, f_fmod}, + {"fnameescape", 1, 1, FEARG_1, arg1_string, + ret_string, f_fnameescape}, + {"fnamemodify", 2, 2, FEARG_1, arg2_string, + ret_string, f_fnamemodify}, + {"foldclosed", 1, 1, FEARG_1, arg1_lnum, + ret_number, f_foldclosed}, + {"foldclosedend", 1, 1, FEARG_1, arg1_lnum, + ret_number, f_foldclosedend}, + {"foldlevel", 1, 1, FEARG_1, arg1_lnum, + ret_number, f_foldlevel}, + {"foldtext", 0, 0, 0, NULL, + ret_string, f_foldtext}, + {"foldtextresult", 1, 1, FEARG_1, arg1_lnum, + ret_string, f_foldtextresult}, + {"foreground", 0, 0, 0, NULL, + ret_void, f_foreground}, + {"fullcommand", 1, 2, FEARG_1, arg2_string_bool, + ret_string, f_fullcommand}, + {"funcref", 1, 3, FEARG_1, arg3_any_list_dict, + ret_func_unknown, f_funcref}, + {"function", 1, 3, FEARG_1, arg3_any_list_dict, + ret_func_unknown, f_function}, + {"garbagecollect", 0, 1, 0, arg1_bool, + ret_void, f_garbagecollect}, + {"get", 2, 3, FEARG_1, arg23_get, + ret_any, f_get}, + {"getbufinfo", 0, 1, FEARG_1, arg1_buffer_or_dict_any, + ret_list_dict_any, f_getbufinfo}, + {"getbufline", 2, 3, FEARG_1, arg3_buffer_lnum_lnum, + ret_list_string, f_getbufline}, + {"getbufoneline", 2, 2, FEARG_1, arg2_buffer_lnum, + ret_string, f_getbufoneline}, + {"getbufvar", 2, 3, FEARG_1, arg3_buffer_string_any, + ret_any, f_getbufvar}, + {"getcellwidths", 0, 0, 0, NULL, + ret_list_any, f_getcellwidths}, + {"getchangelist", 0, 1, FEARG_1, arg1_buffer, + ret_list_any, f_getchangelist}, + {"getchar", 0, 1, 0, arg1_bool, + ret_any, f_getchar}, + {"getcharmod", 0, 0, 0, NULL, + ret_number, f_getcharmod}, + {"getcharpos", 1, 1, FEARG_1, arg1_string, + ret_list_number, f_getcharpos}, + {"getcharsearch", 0, 0, 0, NULL, + ret_dict_any, f_getcharsearch}, + {"getcharstr", 0, 1, 0, arg1_bool, + ret_string, f_getcharstr}, + {"getcmdcompltype", 0, 0, 0, NULL, + ret_string, f_getcmdcompltype}, + {"getcmdline", 0, 0, 0, NULL, + ret_string, f_getcmdline}, + {"getcmdpos", 0, 0, 0, NULL, + ret_number, f_getcmdpos}, + {"getcmdscreenpos", 0, 0, 0, NULL, + ret_number, f_getcmdscreenpos}, + {"getcmdtype", 0, 0, 0, NULL, + ret_string, f_getcmdtype}, + {"getcmdwintype", 0, 0, 0, NULL, + ret_string, f_getcmdwintype}, + {"getcompletion", 2, 3, FEARG_1, arg3_string_string_bool, + ret_list_string, f_getcompletion}, + {"getcurpos", 0, 1, FEARG_1, arg1_number, + ret_list_number, f_getcurpos}, + {"getcursorcharpos", 0, 1, FEARG_1, arg1_number, + ret_list_number, f_getcursorcharpos}, + {"getcwd", 0, 2, FEARG_1, arg2_number, + ret_string, f_getcwd}, + {"getenv", 1, 1, FEARG_1, arg1_string, + ret_any, f_getenv}, + {"getfontname", 0, 1, 0, arg1_string, + ret_string, f_getfontname}, + {"getfperm", 1, 1, FEARG_1, arg1_string, + ret_string, f_getfperm}, + {"getfsize", 1, 1, FEARG_1, arg1_string, + ret_number, f_getfsize}, + {"getftime", 1, 1, FEARG_1, arg1_string, + ret_number, f_getftime}, + {"getftype", 1, 1, FEARG_1, arg1_string, + ret_string, f_getftype}, + {"getimstatus", 0, 0, 0, NULL, + ret_number_bool, f_getimstatus}, + {"getjumplist", 0, 2, FEARG_1, arg2_number, + ret_list_any, f_getjumplist}, + {"getline", 1, 2, FEARG_1, arg2_lnum, + ret_getline, f_getline}, + {"getloclist", 1, 2, 0, arg2_number_dict_any, + ret_list_or_dict_1, f_getloclist}, + {"getmarklist", 0, 1, FEARG_1, arg1_buffer, + ret_list_dict_any, f_getmarklist}, + {"getmatches", 0, 1, 0, arg1_number, + ret_list_dict_any, f_getmatches}, + {"getmousepos", 0, 0, 0, NULL, + ret_dict_number, f_getmousepos}, + {"getmouseshape", 0, 0, 0, NULL, + ret_string, f_getmouseshape}, + {"getpid", 0, 0, 0, NULL, + ret_number, f_getpid}, + {"getpos", 1, 1, FEARG_1, arg1_string, + ret_list_number, f_getpos}, + {"getqflist", 0, 1, 0, arg1_dict_any, + ret_list_or_dict_0, f_getqflist}, + {"getreg", 0, 3, FEARG_1, arg3_string_bool_bool, + ret_getreg, f_getreg}, + {"getreginfo", 0, 1, FEARG_1, arg1_string, + ret_dict_any, f_getreginfo}, + {"getregtype", 0, 1, FEARG_1, arg1_string, + ret_string, f_getregtype}, + {"getscriptinfo", 0, 1, 0, arg1_dict_any, + ret_list_dict_any, f_getscriptinfo}, + {"gettabinfo", 0, 1, FEARG_1, arg1_number, + ret_list_dict_any, f_gettabinfo}, + {"gettabvar", 2, 3, FEARG_1, arg3_number_string_any, + ret_any, f_gettabvar}, + {"gettabwinvar", 3, 4, FEARG_1, arg4_number_number_string_any, + ret_any, f_gettabwinvar}, + {"gettagstack", 0, 1, FEARG_1, arg1_number, + ret_dict_any, f_gettagstack}, + {"gettext", 1, 1, FEARG_1, arg1_string, + ret_string, f_gettext}, + {"getwininfo", 0, 1, FEARG_1, arg1_number, + ret_list_dict_any, f_getwininfo}, + {"getwinpos", 0, 1, FEARG_1, arg1_number, + ret_list_number, f_getwinpos}, + {"getwinposx", 0, 0, 0, NULL, + ret_number, f_getwinposx}, + {"getwinposy", 0, 0, 0, NULL, + ret_number, f_getwinposy}, + {"getwinvar", 2, 3, FEARG_1, arg3_number_string_any, + ret_any, f_getwinvar}, + {"glob", 1, 4, FEARG_1, arg14_glob, + ret_any, f_glob}, + {"glob2regpat", 1, 1, FEARG_1, arg1_string, + ret_string, f_glob2regpat}, + {"globpath", 2, 5, FEARG_2, arg25_globpath, + ret_any, f_globpath}, + {"has", 1, 2, 0, arg2_string_bool, + ret_number_bool, f_has}, + {"has_key", 2, 2, FEARG_1, arg2_dict_any_string_or_nr, + ret_number_bool, f_has_key}, + {"haslocaldir", 0, 2, FEARG_1, arg2_number, + ret_number, f_haslocaldir}, + {"hasmapto", 1, 3, FEARG_1, arg3_string_string_bool, + ret_number_bool, f_hasmapto}, + {"highlightID", 1, 1, FEARG_1, arg1_string, // obsolete + ret_number, f_hlID}, + {"highlight_exists",1, 1, FEARG_1, arg1_string, // obsolete + ret_number_bool, f_hlexists}, + {"histadd", 2, 2, FEARG_2, arg2_string, + ret_number_bool, f_histadd}, + {"histdel", 1, 2, FEARG_1, arg2_string_string_or_number, + ret_number_bool, f_histdel}, + {"histget", 1, 2, FEARG_1, arg2_string_number, + ret_string, f_histget}, + {"histnr", 1, 1, FEARG_1, arg1_string, + ret_number, f_histnr}, + {"hlID", 1, 1, FEARG_1, arg1_string, + ret_number, f_hlID}, + {"hlexists", 1, 1, FEARG_1, arg1_string, + ret_number_bool, f_hlexists}, + {"hlget", 0, 2, FEARG_1, arg2_string_bool, + ret_list_dict_any, f_hlget}, + {"hlset", 1, 1, FEARG_1, arg1_list_any, + ret_number_bool, f_hlset}, + {"hostname", 0, 0, 0, NULL, + ret_string, f_hostname}, + {"iconv", 3, 3, FEARG_1, arg3_string, + ret_string, f_iconv}, + {"indent", 1, 1, FEARG_1, arg1_lnum, + ret_number, f_indent}, + {"index", 2, 4, FEARG_1, arg24_index, + ret_number, f_index}, + {"indexof", 2, 3, FEARG_1, arg23_index, + ret_number, f_indexof}, + {"input", 1, 3, FEARG_1, arg3_string, + ret_string, f_input}, + {"inputdialog", 1, 3, FEARG_1, arg3_string, + ret_string, f_inputdialog}, + {"inputlist", 1, 1, FEARG_1, arg1_list_string, + ret_number, f_inputlist}, + {"inputrestore", 0, 0, 0, NULL, + ret_number_bool, f_inputrestore}, + {"inputsave", 0, 0, 0, NULL, + ret_number_bool, f_inputsave}, + {"inputsecret", 1, 2, FEARG_1, arg2_string, + ret_string, f_inputsecret}, + {"insert", 2, 3, FEARG_1, arg23_insert, + ret_first_arg, f_insert}, + {"instanceof", 2, VARGS, FEARG_1|FE_X, arg2_instanceof, + ret_bool, f_instanceof}, + {"interrupt", 0, 0, 0, NULL, + ret_void, f_interrupt}, + {"invert", 1, 1, FEARG_1, arg1_number, + ret_number, f_invert}, + {"isabsolutepath", 1, 1, FEARG_1, arg1_string, + ret_number_bool, f_isabsolutepath}, + {"isdirectory", 1, 1, FEARG_1, arg1_string, + ret_number_bool, f_isdirectory}, + {"isinf", 1, 1, FEARG_1, arg1_float_or_nr, + ret_number, MATH_FUNC(f_isinf)}, + {"islocked", 1, 1, FEARG_1, arg1_string, + ret_number_bool, f_islocked}, + {"isnan", 1, 1, FEARG_1, arg1_float_or_nr, + ret_number_bool, MATH_FUNC(f_isnan)}, + {"items", 1, 1, FEARG_1, arg1_string_or_list_or_dict, + ret_list_items, f_items}, + {"job_getchannel", 1, 1, FEARG_1, arg1_job, + ret_channel, JOB_FUNC(f_job_getchannel)}, + {"job_info", 0, 1, FEARG_1, arg1_job, + ret_job_info, JOB_FUNC(f_job_info)}, + {"job_setoptions", 2, 2, FEARG_1, arg2_job_dict, + ret_void, JOB_FUNC(f_job_setoptions)}, + {"job_start", 1, 2, FEARG_1, arg2_string_or_list_dict, + ret_job, JOB_FUNC(f_job_start)}, + {"job_status", 1, 1, FEARG_1, arg1_job, + ret_string, JOB_FUNC(f_job_status)}, + {"job_stop", 1, 2, FEARG_1, arg2_job_string_or_number, + ret_number_bool, JOB_FUNC(f_job_stop)}, + {"join", 1, 2, FEARG_1, arg2_list_any_string, + ret_string, f_join}, + {"js_decode", 1, 1, FEARG_1, arg1_string, + ret_any, f_js_decode}, + {"js_encode", 1, 1, FEARG_1, NULL, + ret_string, f_js_encode}, + {"json_decode", 1, 1, FEARG_1, arg1_string, + ret_any, f_json_decode}, + {"json_encode", 1, 1, FEARG_1, NULL, + ret_string, f_json_encode}, + {"keys", 1, 1, FEARG_1, arg1_dict_any, + ret_list_string, f_keys}, + {"keytrans", 1, 1, FEARG_1, arg1_string, + ret_string, f_keytrans}, + {"last_buffer_nr", 0, 0, 0, NULL, // obsolete + ret_number, f_last_buffer_nr}, + {"len", 1, 1, FEARG_1, arg1_len, + ret_number, f_len}, + {"libcall", 3, 3, FEARG_3, arg3_libcall, + ret_string, f_libcall}, + {"libcallnr", 3, 3, FEARG_3, arg3_libcall, + ret_number, f_libcallnr}, + {"line", 1, 2, FEARG_1, arg2_string_number, + ret_number, f_line}, + {"line2byte", 1, 1, FEARG_1, arg1_lnum, + ret_number, f_line2byte}, + {"lispindent", 1, 1, FEARG_1, arg1_lnum, + ret_number, f_lispindent}, + {"list2blob", 1, 1, FEARG_1, arg1_list_number, + ret_blob, f_list2blob}, + {"list2str", 1, 2, FEARG_1, arg2_list_number_bool, + ret_string, f_list2str}, + {"listener_add", 1, 2, FEARG_2, arg2_any_buffer, + ret_number, f_listener_add}, + {"listener_flush", 0, 1, FEARG_1, arg1_buffer, + ret_void, f_listener_flush}, + {"listener_remove", 1, 1, FEARG_1, arg1_number, + ret_number_bool, f_listener_remove}, + {"localtime", 0, 0, 0, NULL, + ret_number, f_localtime}, + {"log", 1, 1, FEARG_1, arg1_float_or_nr, + ret_float, f_log}, + {"log10", 1, 1, FEARG_1, arg1_float_or_nr, + ret_float, f_log10}, + {"luaeval", 1, 2, FEARG_1, arg2_string_any, + ret_any, +#ifdef FEAT_LUA + f_luaeval +#else + NULL +#endif + }, + {"map", 2, 2, FEARG_1, arg2_map, + ret_first_cont, f_map}, + {"maparg", 1, 4, FEARG_1, arg14_maparg, + ret_maparg, f_maparg}, + {"mapcheck", 1, 3, FEARG_1, arg3_string_string_bool, + ret_string, f_mapcheck}, + {"maplist", 0, 1, 0, arg1_bool, + ret_list_dict_any, f_maplist}, + {"mapnew", 2, 2, FEARG_1, arg2_mapnew, + ret_first_cont, f_mapnew}, + {"mapset", 1, 3, FEARG_1, arg3_string_or_dict_bool_dict, + ret_void, f_mapset}, + {"match", 2, 4, FEARG_1, arg24_match_func, + ret_any, f_match}, + {"matchadd", 2, 5, FEARG_1, arg25_matchadd, + ret_number, f_matchadd}, + {"matchaddpos", 2, 5, FEARG_1, arg25_matchaddpos, + ret_number, f_matchaddpos}, + {"matcharg", 1, 1, FEARG_1, arg1_number, + ret_list_string, f_matcharg}, + {"matchbufline", 4, 5, FEARG_1, arg45_matchbufline, + ret_list_any, f_matchbufline}, + {"matchdelete", 1, 2, FEARG_1, arg2_number, + ret_number_bool, f_matchdelete}, + {"matchend", 2, 4, FEARG_1, arg24_match_func, + ret_number, f_matchend}, + {"matchfuzzy", 2, 3, FEARG_1, arg3_list_string_dict, + ret_list_any, f_matchfuzzy}, + {"matchfuzzypos", 2, 3, FEARG_1, arg3_list_string_dict, + ret_list_any, f_matchfuzzypos}, + {"matchlist", 2, 4, FEARG_1, arg24_match_func, + ret_list_string, f_matchlist}, + {"matchstr", 2, 4, FEARG_1, arg24_match_func, + ret_string, f_matchstr}, + {"matchstrlist", 2, 3, FEARG_1, arg23_matchstrlist, + ret_list_any, f_matchstrlist}, + {"matchstrpos", 2, 4, FEARG_1, arg24_match_func, + ret_list_any, f_matchstrpos}, + {"max", 1, 1, FEARG_1, arg1_list_or_dict, + ret_number, f_max}, + {"menu_info", 1, 2, FEARG_1, arg2_string, + ret_dict_any, +#ifdef FEAT_MENU + f_menu_info +#else + NULL +#endif + }, + {"min", 1, 1, FEARG_1, arg1_list_or_dict, + ret_number, f_min}, + {"mkdir", 1, 3, FEARG_1, arg3_string_string_number, + ret_number_bool, f_mkdir}, + {"mode", 0, 1, FEARG_1, arg1_bool, + ret_string, f_mode}, + {"mzeval", 1, 1, FEARG_1, arg1_string, + ret_any, +#ifdef FEAT_MZSCHEME + f_mzeval +#else + NULL +#endif + }, + {"nextnonblank", 1, 1, FEARG_1, arg1_lnum, + ret_number, f_nextnonblank}, + {"nr2char", 1, 2, FEARG_1, arg2_number_bool, + ret_string, f_nr2char}, + {"or", 2, 2, FEARG_1, arg2_number, + ret_number, f_or}, + {"pathshorten", 1, 2, FEARG_1, arg2_string_number, + ret_string, f_pathshorten}, + {"perleval", 1, 1, FEARG_1, arg1_string, + ret_any, +#ifdef FEAT_PERL + f_perleval +#else + NULL +#endif + }, + {"popup_atcursor", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict, + ret_number, PROP_FUNC(f_popup_atcursor)}, + {"popup_beval", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict, + ret_number, PROP_FUNC(f_popup_beval)}, + {"popup_clear", 0, 1, 0, arg1_bool, + ret_void, PROP_FUNC(f_popup_clear)}, + {"popup_close", 1, 2, FEARG_1, arg2_number_any, + ret_void, PROP_FUNC(f_popup_close)}, + {"popup_create", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict, + ret_number, PROP_FUNC(f_popup_create)}, + {"popup_dialog", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict, + ret_number, PROP_FUNC(f_popup_dialog)}, + {"popup_filter_menu", 2, 2, 0, arg2_number_string, + ret_bool, PROP_FUNC(f_popup_filter_menu)}, + {"popup_filter_yesno", 2, 2, 0, arg2_number_string, + ret_bool, PROP_FUNC(f_popup_filter_yesno)}, + {"popup_findecho", 0, 0, 0, NULL, + ret_number, PROP_FUNC(f_popup_findecho)}, + {"popup_findinfo", 0, 0, 0, NULL, + ret_number, PROP_FUNC(f_popup_findinfo)}, + {"popup_findpreview", 0, 0, 0, NULL, + ret_number, PROP_FUNC(f_popup_findpreview)}, + {"popup_getoptions", 1, 1, FEARG_1, arg1_number, + ret_dict_any, PROP_FUNC(f_popup_getoptions)}, + {"popup_getpos", 1, 1, FEARG_1, arg1_number, + ret_dict_any, PROP_FUNC(f_popup_getpos)}, + {"popup_hide", 1, 1, FEARG_1, arg1_number, + ret_void, PROP_FUNC(f_popup_hide)}, + {"popup_list", 0, 0, 0, NULL, + ret_list_number, PROP_FUNC(f_popup_list)}, + {"popup_locate", 2, 2, 0, arg2_number, + ret_number, PROP_FUNC(f_popup_locate)}, + {"popup_menu", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict, + ret_number, PROP_FUNC(f_popup_menu)}, + {"popup_move", 2, 2, FEARG_1, arg2_number_dict_any, + ret_void, PROP_FUNC(f_popup_move)}, + {"popup_notification", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict, + ret_number, PROP_FUNC(f_popup_notification)}, + {"popup_setoptions", 2, 2, FEARG_1, arg2_number_dict_any, + ret_void, PROP_FUNC(f_popup_setoptions)}, + {"popup_settext", 2, 2, FEARG_1, arg2_number_string_or_list, + ret_void, PROP_FUNC(f_popup_settext)}, + {"popup_show", 1, 1, FEARG_1, arg1_number, + ret_void, PROP_FUNC(f_popup_show)}, + {"pow", 2, 2, FEARG_1, arg2_float_or_nr, + ret_float, f_pow}, + {"prevnonblank", 1, 1, FEARG_1, arg1_lnum, + ret_number, f_prevnonblank}, + {"printf", 1, 19, FEARG_2, arg119_printf, + ret_string, f_printf}, + {"prompt_getprompt", 1, 1, FEARG_1, arg1_buffer, + ret_string, JOB_FUNC(f_prompt_getprompt)}, + {"prompt_setcallback", 2, 2, FEARG_1, arg2_buffer_any, + ret_void, JOB_FUNC(f_prompt_setcallback)}, + {"prompt_setinterrupt", 2, 2, FEARG_1, arg2_buffer_any, + ret_void, JOB_FUNC(f_prompt_setinterrupt)}, + {"prompt_setprompt", 2, 2, FEARG_1, arg2_buffer_string, + ret_void, JOB_FUNC(f_prompt_setprompt)}, + {"prop_add", 3, 3, FEARG_1, arg3_number_number_dict, + ret_number, PROP_FUNC(f_prop_add)}, + {"prop_add_list", 2, 2, FEARG_1, arg2_dict_any_list_any, + ret_void, PROP_FUNC(f_prop_add_list)}, + {"prop_clear", 1, 3, FEARG_1, arg3_number_number_dict, + ret_void, PROP_FUNC(f_prop_clear)}, + {"prop_find", 1, 2, FEARG_1, arg2_dict_string, + ret_dict_any, PROP_FUNC(f_prop_find)}, + {"prop_list", 1, 2, FEARG_1, arg2_number_dict_any, + ret_list_dict_any, PROP_FUNC(f_prop_list)}, + {"prop_remove", 1, 3, FEARG_1, arg3_dict_number_number, + ret_number, PROP_FUNC(f_prop_remove)}, + {"prop_type_add", 2, 2, FEARG_1, arg2_string_dict, + ret_void, PROP_FUNC(f_prop_type_add)}, + {"prop_type_change", 2, 2, FEARG_1, arg2_string_dict, + ret_void, PROP_FUNC(f_prop_type_change)}, + {"prop_type_delete", 1, 2, FEARG_1, arg2_string_dict, + ret_void, PROP_FUNC(f_prop_type_delete)}, + {"prop_type_get", 1, 2, FEARG_1, arg2_string_dict, + ret_dict_any, PROP_FUNC(f_prop_type_get)}, + {"prop_type_list", 0, 1, FEARG_1, arg1_dict_any, + ret_list_string, PROP_FUNC(f_prop_type_list)}, + {"pum_getpos", 0, 0, 0, NULL, + ret_dict_number, f_pum_getpos}, + {"pumvisible", 0, 0, 0, NULL, + ret_number_bool, f_pumvisible}, + {"py3eval", 1, 1, FEARG_1, arg1_string, + ret_any, +#ifdef FEAT_PYTHON3 + f_py3eval +#else + NULL +#endif + }, + {"pyeval", 1, 1, FEARG_1, arg1_string, + ret_any, +#ifdef FEAT_PYTHON + f_pyeval +#else + NULL +#endif + }, + {"pyxeval", 1, 1, FEARG_1, arg1_string, + ret_any, +#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) + f_pyxeval +#else + NULL +#endif + }, + {"rand", 0, 1, FEARG_1, arg1_list_number, + ret_number, f_rand}, + {"range", 1, 3, FEARG_1, arg3_number, + ret_list_number, f_range}, + {"readblob", 1, 3, FEARG_1, arg3_string_number_number, + ret_blob, f_readblob}, + {"readdir", 1, 3, FEARG_1, arg3_string_any_dict, + ret_list_string, f_readdir}, + {"readdirex", 1, 3, FEARG_1, arg3_string_any_dict, + ret_list_dict_any, f_readdirex}, + {"readfile", 1, 3, FEARG_1, arg3_string_string_number, + ret_list_string, f_readfile}, + {"reduce", 2, 3, FEARG_1, arg23_reduce, + ret_any, f_reduce}, + {"reg_executing", 0, 0, 0, NULL, + ret_string, f_reg_executing}, + {"reg_recording", 0, 0, 0, NULL, + ret_string, f_reg_recording}, + {"reltime", 0, 2, FEARG_1, arg2_list_number, + ret_list_any, f_reltime}, + {"reltimefloat", 1, 1, FEARG_1, arg1_list_number, + ret_float, f_reltimefloat}, + {"reltimestr", 1, 1, FEARG_1, arg1_list_number, + ret_string, f_reltimestr}, + {"remote_expr", 2, 4, FEARG_1, arg24_remote_expr, + ret_string, f_remote_expr}, + {"remote_foreground", 1, 1, FEARG_1, arg1_string, + ret_string, f_remote_foreground}, + {"remote_peek", 1, 2, FEARG_1, arg2_string, + ret_number, f_remote_peek}, + {"remote_read", 1, 2, FEARG_1, arg2_string_number, + ret_string, f_remote_read}, + {"remote_send", 2, 3, FEARG_1, arg3_string, + ret_string, f_remote_send}, + {"remote_startserver", 1, 1, FEARG_1, arg1_string, + ret_void, f_remote_startserver}, + {"remove", 2, 3, FEARG_1, arg23_remove, + ret_remove, f_remove}, + {"rename", 2, 2, FEARG_1, arg2_string, + ret_number_bool, f_rename}, + {"repeat", 2, 2, FEARG_1, arg2_repeat, + ret_repeat, f_repeat}, + {"resolve", 1, 1, FEARG_1, arg1_string, + ret_string, f_resolve}, + {"reverse", 1, 1, FEARG_1, arg1_string_or_list_or_blob_mod, + ret_first_arg, f_reverse}, + {"round", 1, 1, FEARG_1, arg1_float_or_nr, + ret_float, f_round}, + {"rubyeval", 1, 1, FEARG_1, arg1_string, + ret_any, +#ifdef FEAT_RUBY + f_rubyeval +#else + NULL +#endif + }, + {"screenattr", 2, 2, FEARG_1, arg2_number, + ret_number, f_screenattr}, + {"screenchar", 2, 2, FEARG_1, arg2_number, + ret_number, f_screenchar}, + {"screenchars", 2, 2, FEARG_1, arg2_number, + ret_list_number, f_screenchars}, + {"screencol", 0, 0, 0, NULL, + ret_number, f_screencol}, + {"screenpos", 3, 3, FEARG_1, arg3_number, + ret_dict_number, f_screenpos}, + {"screenrow", 0, 0, 0, NULL, + ret_number, f_screenrow}, + {"screenstring", 2, 2, FEARG_1, arg2_number, + ret_string, f_screenstring}, + {"search", 1, 5, FEARG_1, arg15_search, + ret_number, f_search}, + {"searchcount", 0, 1, FEARG_1, arg1_dict_any, + ret_dict_any, f_searchcount}, + {"searchdecl", 1, 3, FEARG_1, arg3_string_bool_bool, + ret_number_bool, f_searchdecl}, + {"searchpair", 3, 7, 0, arg37_searchpair, + ret_number, f_searchpair}, + {"searchpairpos", 3, 7, 0, arg37_searchpair, + ret_list_number, f_searchpairpos}, + {"searchpos", 1, 5, FEARG_1, arg15_search, + ret_list_number, f_searchpos}, + {"server2client", 2, 2, FEARG_1, arg2_string, + ret_number_bool, f_server2client}, + {"serverlist", 0, 0, 0, NULL, + ret_string, f_serverlist}, + {"setbufline", 3, 3, FEARG_3, arg3_setbufline, + ret_number_bool, f_setbufline}, + {"setbufvar", 3, 3, FEARG_3, arg3_buffer_string_any, + ret_void, f_setbufvar}, + {"setcellwidths", 1, 1, FEARG_1, arg1_list_any, + ret_void, f_setcellwidths}, + {"setcharpos", 2, 2, FEARG_2, arg2_string_list_number, + ret_number_bool, f_setcharpos}, + {"setcharsearch", 1, 1, FEARG_1, arg1_dict_any, + ret_void, f_setcharsearch}, + {"setcmdline", 1, 2, FEARG_1, arg2_string_number, + ret_number_bool, f_setcmdline}, + {"setcmdpos", 1, 1, FEARG_1, arg1_number, + ret_number_bool, f_setcmdpos}, + {"setcursorcharpos", 1, 3, FEARG_1, arg13_cursor, + ret_number_bool, f_setcursorcharpos}, + {"setenv", 2, 2, FEARG_2, arg2_string_any, + ret_void, f_setenv}, + {"setfperm", 2, 2, FEARG_1, arg2_string, + ret_number_bool, f_setfperm}, + {"setline", 2, 2, FEARG_2, arg2_setline, + ret_number_bool, f_setline}, + {"setloclist", 2, 4, FEARG_2, arg24_setloclist, + ret_number_bool, f_setloclist}, + {"setmatches", 1, 2, FEARG_1, arg2_list_any_number, + ret_number_bool, f_setmatches}, + {"setpos", 2, 2, FEARG_2, arg2_string_list_number, + ret_number_bool, f_setpos}, + {"setqflist", 1, 3, FEARG_1, arg13_setqflist, + ret_number_bool, f_setqflist}, + {"setreg", 2, 3, FEARG_2, arg3_string_any_string, + ret_number_bool, f_setreg}, + {"settabvar", 3, 3, FEARG_3, arg3_number_string_any, + ret_void, f_settabvar}, + {"settabwinvar", 4, 4, FEARG_4, arg4_number_number_string_any, + ret_void, f_settabwinvar}, + {"settagstack", 2, 3, FEARG_2, arg23_settagstack, + ret_number_bool, f_settagstack}, + {"setwinvar", 3, 3, FEARG_3, arg3_number_string_any, + ret_void, f_setwinvar}, + {"sha256", 1, 1, FEARG_1, arg1_string, + ret_string, +#ifdef FEAT_CRYPT + f_sha256 +#else + NULL +#endif + }, + {"shellescape", 1, 2, FEARG_1, arg2_string_bool, + ret_string, f_shellescape}, + {"shiftwidth", 0, 1, FEARG_1, arg1_number, + ret_number, f_shiftwidth}, + {"sign_define", 1, 2, FEARG_1, arg2_string_or_list_dict, + ret_any, SIGN_FUNC(f_sign_define)}, + {"sign_getdefined", 0, 1, FEARG_1, arg1_string, + ret_list_dict_any, SIGN_FUNC(f_sign_getdefined)}, + {"sign_getplaced", 0, 2, FEARG_1, arg02_sign_getplaced, + ret_list_dict_any, SIGN_FUNC(f_sign_getplaced)}, + {"sign_jump", 3, 3, FEARG_1, arg3_number_string_buffer, + ret_number, SIGN_FUNC(f_sign_jump)}, + {"sign_place", 4, 5, FEARG_1, arg45_sign_place, + ret_number, SIGN_FUNC(f_sign_place)}, + {"sign_placelist", 1, 1, FEARG_1, arg1_list_any, + ret_list_number, SIGN_FUNC(f_sign_placelist)}, + {"sign_undefine", 0, 1, FEARG_1, arg1_string_or_list_string, + ret_number_bool, SIGN_FUNC(f_sign_undefine)}, + {"sign_unplace", 1, 2, FEARG_1, arg2_string_dict, + ret_number_bool, SIGN_FUNC(f_sign_unplace)}, + {"sign_unplacelist", 1, 1, FEARG_1, arg1_list_any, + ret_list_number, SIGN_FUNC(f_sign_unplacelist)}, + {"simplify", 1, 1, FEARG_1, arg1_string, + ret_string, f_simplify}, + {"sin", 1, 1, FEARG_1, arg1_float_or_nr, + ret_float, f_sin}, + {"sinh", 1, 1, FEARG_1, arg1_float_or_nr, + ret_float, f_sinh}, + {"slice", 2, 3, FEARG_1, arg23_slice, + ret_slice, f_slice}, + {"sort", 1, 3, FEARG_1, arg13_sortuniq, + ret_first_arg, f_sort}, + {"sound_clear", 0, 0, 0, NULL, + ret_void, SOUND_FUNC(f_sound_clear)}, + {"sound_playevent", 1, 2, FEARG_1, arg2_string_any, + ret_number, SOUND_FUNC(f_sound_playevent)}, + {"sound_playfile", 1, 2, FEARG_1, arg2_string_any, + ret_number, SOUND_FUNC(f_sound_playfile)}, + {"sound_stop", 1, 1, FEARG_1, arg1_number, + ret_void, SOUND_FUNC(f_sound_stop)}, + {"soundfold", 1, 1, FEARG_1, arg1_string, + ret_string, f_soundfold}, + {"spellbadword", 0, 1, FEARG_1, arg1_string, + ret_list_string, f_spellbadword}, + {"spellsuggest", 1, 3, FEARG_1, arg3_string_number_bool, + ret_list_string, f_spellsuggest}, + {"split", 1, 3, FEARG_1, arg3_string_string_bool, + ret_list_string, f_split}, + {"sqrt", 1, 1, FEARG_1, arg1_float_or_nr, + ret_float, f_sqrt}, + {"srand", 0, 1, FEARG_1, arg1_number, + ret_list_number, f_srand}, + {"state", 0, 1, FEARG_1, arg1_string, + ret_string, f_state}, + {"str2float", 1, 2, FEARG_1, arg2_string_bool, + ret_float, f_str2float}, + {"str2list", 1, 2, FEARG_1, arg2_string_bool, + ret_list_number, f_str2list}, + {"str2nr", 1, 3, FEARG_1, arg3_string_number_bool, + ret_number, f_str2nr}, + {"strcharlen", 1, 1, FEARG_1, arg1_string_or_nr, + ret_number, f_strcharlen}, + {"strcharpart", 2, 4, FEARG_1, arg24_strpart, + ret_string, f_strcharpart}, + {"strchars", 1, 2, FEARG_1, arg2_string_bool, + ret_number, f_strchars}, + {"strdisplaywidth", 1, 2, FEARG_1, arg2_string_number, + ret_number, f_strdisplaywidth}, + {"strftime", 1, 2, FEARG_1, arg2_string_number, + ret_string, +#ifdef HAVE_STRFTIME + f_strftime +#else + NULL +#endif + }, + {"strgetchar", 2, 2, FEARG_1, arg2_string_number, + ret_number, f_strgetchar}, + {"stridx", 2, 3, FEARG_1, arg3_string_string_number, + ret_number, f_stridx}, + {"string", 1, 1, FEARG_1|FE_X, NULL, + ret_string, f_string}, + {"strlen", 1, 1, FEARG_1, arg1_string_or_nr, + ret_number, f_strlen}, + {"strpart", 2, 4, FEARG_1, arg24_strpart, + ret_string, f_strpart}, + {"strptime", 2, 2, FEARG_1, arg2_string, + ret_number, +#ifdef HAVE_STRPTIME + f_strptime +#else + NULL +#endif + }, + {"strridx", 2, 3, FEARG_1, arg3_string_string_number, + ret_number, f_strridx}, + {"strtrans", 1, 1, FEARG_1, arg1_string, + ret_string, f_strtrans}, + {"strutf16len", 1, 2, FEARG_1, arg2_string_bool, + ret_number, f_strutf16len}, + {"strwidth", 1, 1, FEARG_1, arg1_string, + ret_number, f_strwidth}, + {"submatch", 1, 2, FEARG_1, arg2_number_bool, + ret_string, f_submatch}, + {"substitute", 4, 4, FEARG_1, arg4_string_string_any_string, + ret_string, f_substitute}, + {"swapfilelist", 0, 0, 0, NULL, + ret_list_string, f_swapfilelist}, + {"swapinfo", 1, 1, FEARG_1, arg1_string, + ret_dict_any, f_swapinfo}, + {"swapname", 1, 1, FEARG_1, arg1_buffer, + ret_string, f_swapname}, + {"synID", 3, 3, 0, arg3_lnum_number_bool, + ret_number, f_synID}, + {"synIDattr", 2, 3, FEARG_1, arg3_number_string_string, + ret_string, f_synIDattr}, + {"synIDtrans", 1, 1, FEARG_1, arg1_number, + ret_number, f_synIDtrans}, + {"synconcealed", 2, 2, 0, arg2_lnum_number, + ret_list_any, f_synconcealed}, + {"synstack", 2, 2, 0, arg2_lnum_number, + ret_list_number, f_synstack}, + {"system", 1, 2, FEARG_1, arg12_system, + ret_string, f_system}, + {"systemlist", 1, 2, FEARG_1, arg12_system, + ret_list_string, f_systemlist}, + {"tabpagebuflist", 0, 1, FEARG_1, arg1_number, + ret_list_number, f_tabpagebuflist}, + {"tabpagenr", 0, 1, 0, arg1_string, + ret_number, f_tabpagenr}, + {"tabpagewinnr", 1, 2, FEARG_1, arg2_number_string, + ret_number, f_tabpagewinnr}, + {"tagfiles", 0, 0, 0, NULL, + ret_list_string, f_tagfiles}, + {"taglist", 1, 2, FEARG_1, arg2_string, + ret_list_dict_any, f_taglist}, + {"tan", 1, 1, FEARG_1, arg1_float_or_nr, + ret_float, f_tan}, + {"tanh", 1, 1, FEARG_1, arg1_float_or_nr, + ret_float, f_tanh}, + {"tempname", 0, 0, 0, NULL, + ret_string, f_tempname}, + {"term_dumpdiff", 2, 3, FEARG_1, arg3_string_string_dict, + ret_number, TERM_FUNC(f_term_dumpdiff)}, + {"term_dumpload", 1, 2, FEARG_1, arg2_string_dict, + ret_number, TERM_FUNC(f_term_dumpload)}, + {"term_dumpwrite", 2, 3, FEARG_2, arg3_buffer_string_dict, + ret_void, TERM_FUNC(f_term_dumpwrite)}, + {"term_getaltscreen", 1, 1, FEARG_1, arg1_buffer, + ret_number, TERM_FUNC(f_term_getaltscreen)}, + {"term_getansicolors", 1, 1, FEARG_1, arg1_buffer, + ret_list_string, +#if defined(FEAT_TERMINAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) + f_term_getansicolors +#else + NULL +#endif + }, + {"term_getattr", 2, 2, FEARG_1, arg2_number_string, + ret_number, TERM_FUNC(f_term_getattr)}, + {"term_getcursor", 1, 1, FEARG_1, arg1_buffer, + ret_list_any, TERM_FUNC(f_term_getcursor)}, + {"term_getjob", 1, 1, FEARG_1, arg1_buffer, + ret_job, TERM_FUNC(f_term_getjob)}, + {"term_getline", 2, 2, FEARG_1, arg2_buffer_lnum, + ret_string, TERM_FUNC(f_term_getline)}, + {"term_getscrolled", 1, 1, FEARG_1, arg1_buffer, + ret_number, TERM_FUNC(f_term_getscrolled)}, + {"term_getsize", 1, 1, FEARG_1, arg1_buffer, + ret_list_number, TERM_FUNC(f_term_getsize)}, + {"term_getstatus", 1, 1, FEARG_1, arg1_buffer, + ret_string, TERM_FUNC(f_term_getstatus)}, + {"term_gettitle", 1, 1, FEARG_1, arg1_buffer, + ret_string, TERM_FUNC(f_term_gettitle)}, + {"term_gettty", 1, 2, FEARG_1, arg2_buffer_bool, + ret_string, TERM_FUNC(f_term_gettty)}, + {"term_list", 0, 0, 0, NULL, + ret_list_number, TERM_FUNC(f_term_list)}, + {"term_scrape", 2, 2, FEARG_1, arg2_buffer_lnum, + ret_list_dict_any, TERM_FUNC(f_term_scrape)}, + {"term_sendkeys", 2, 2, FEARG_1, arg2_buffer_string, + ret_void, TERM_FUNC(f_term_sendkeys)}, + {"term_setansicolors", 2, 2, FEARG_1, arg2_buffer_list_any, + ret_void, +#if defined(FEAT_TERMINAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) + f_term_setansicolors +#else + NULL +#endif + }, + {"term_setapi", 2, 2, FEARG_1, arg2_buffer_string, + ret_void, TERM_FUNC(f_term_setapi)}, + {"term_setkill", 2, 2, FEARG_1, arg2_buffer_string, + ret_void, TERM_FUNC(f_term_setkill)}, + {"term_setrestore", 2, 2, FEARG_1, arg2_buffer_string, + ret_void, TERM_FUNC(f_term_setrestore)}, + {"term_setsize", 3, 3, FEARG_1, arg3_buffer_number_number, + ret_void, TERM_FUNC(f_term_setsize)}, + {"term_start", 1, 2, FEARG_1, arg2_string_or_list_dict, + ret_number, TERM_FUNC(f_term_start)}, + {"term_wait", 1, 2, FEARG_1, arg2_buffer_number, + ret_void, TERM_FUNC(f_term_wait)}, + {"terminalprops", 0, 0, 0, NULL, + ret_dict_string, f_terminalprops}, + {"test_alloc_fail", 3, 3, FEARG_1, arg3_number, + ret_void, f_test_alloc_fail}, + {"test_autochdir", 0, 0, 0, NULL, + ret_void, f_test_autochdir}, + {"test_feedinput", 1, 1, FEARG_1, arg1_string, + ret_void, f_test_feedinput}, + {"test_garbagecollect_now", 0, 0, 0, NULL, + ret_void, f_test_garbagecollect_now}, + {"test_garbagecollect_soon", 0, 0, 0, NULL, + ret_void, f_test_garbagecollect_soon}, + {"test_getvalue", 1, 1, FEARG_1, arg1_string, + ret_number, f_test_getvalue}, + {"test_gui_event", 2, 2, FEARG_1, arg2_string_dict, + ret_bool, f_test_gui_event}, + {"test_ignore_error", 1, 1, FEARG_1, arg1_string, + ret_void, f_test_ignore_error}, + {"test_mswin_event", 2, 2, FEARG_1, arg2_string_dict, + ret_number, f_test_mswin_event}, + {"test_null_blob", 0, 0, 0, NULL, + ret_blob, f_test_null_blob}, + {"test_null_channel", 0, 0, 0, NULL, + ret_channel, JOB_FUNC(f_test_null_channel)}, + {"test_null_dict", 0, 0, 0, NULL, + ret_dict_any, f_test_null_dict}, + {"test_null_function", 0, 0, 0, NULL, + ret_func_any, f_test_null_function}, + {"test_null_job", 0, 0, 0, NULL, + ret_job, JOB_FUNC(f_test_null_job)}, + {"test_null_list", 0, 0, 0, NULL, + ret_list_any, f_test_null_list}, + {"test_null_partial", 0, 0, 0, NULL, + ret_func_any, f_test_null_partial}, + {"test_null_string", 0, 0, 0, NULL, + ret_string, f_test_null_string}, + {"test_option_not_set", 1, 1, FEARG_1, arg1_string, + ret_void, f_test_option_not_set}, + {"test_override", 2, 2, FEARG_2, arg2_string_number, + ret_void, f_test_override}, + {"test_refcount", 1, 1, FEARG_1|FE_X, NULL, + ret_number, f_test_refcount}, + {"test_setmouse", 2, 2, 0, arg2_number, + ret_void, f_test_setmouse}, + {"test_settime", 1, 1, FEARG_1, arg1_number, + ret_void, f_test_settime}, + {"test_srand_seed", 0, 1, FEARG_1, arg1_number, + ret_void, f_test_srand_seed}, + {"test_unknown", 0, 0, 0, NULL, + ret_any, f_test_unknown}, + {"test_void", 0, 0, 0, NULL, + ret_void, f_test_void}, + {"timer_info", 0, 1, FEARG_1, arg1_number, + ret_list_dict_any, TIMER_FUNC(f_timer_info)}, + {"timer_pause", 2, 2, FEARG_1, arg2_number_bool, + ret_void, TIMER_FUNC(f_timer_pause)}, + {"timer_start", 2, 3, FEARG_1, arg3_number_any_dict, + ret_number, TIMER_FUNC(f_timer_start)}, + {"timer_stop", 1, 1, FEARG_1, arg1_number, + ret_void, TIMER_FUNC(f_timer_stop)}, + {"timer_stopall", 0, 0, 0, NULL, + ret_void, TIMER_FUNC(f_timer_stopall)}, + {"tolower", 1, 1, FEARG_1, arg1_string, + ret_string, f_tolower}, + {"toupper", 1, 1, FEARG_1, arg1_string, + ret_string, f_toupper}, + {"tr", 3, 3, FEARG_1, arg3_string, + ret_string, f_tr}, + {"trim", 1, 3, FEARG_1, arg3_string_string_number, + ret_string, f_trim}, + {"trunc", 1, 1, FEARG_1, arg1_float_or_nr, + ret_float, f_trunc}, + {"type", 1, 1, FEARG_1|FE_X, NULL, + ret_number, f_type}, + {"typename", 1, 1, FEARG_1|FE_X, NULL, + ret_string, f_typename}, + {"undofile", 1, 1, FEARG_1, arg1_string, + ret_string, f_undofile}, + {"undotree", 0, 1, FEARG_1, arg1_buffer, + ret_dict_any, f_undotree}, + {"uniq", 1, 3, FEARG_1, arg13_sortuniq, + ret_first_arg, f_uniq}, + {"utf16idx", 2, 4, FEARG_1, arg4_string_number_bool_bool, + ret_number, f_utf16idx}, + {"values", 1, 1, FEARG_1, arg1_dict_any, + ret_list_member, f_values}, + {"virtcol", 1, 3, FEARG_1, arg3_string_or_list_bool_number, + ret_virtcol, f_virtcol}, + {"virtcol2col", 3, 3, FEARG_1, arg3_number, + ret_number, f_virtcol2col}, + {"visualmode", 0, 1, 0, arg1_bool, + ret_string, f_visualmode}, + {"wildmenumode", 0, 0, 0, NULL, + ret_number, f_wildmenumode}, + {"win_execute", 2, 3, FEARG_2, arg23_win_execute, + ret_string, f_win_execute}, + {"win_findbuf", 1, 1, FEARG_1, arg1_number, + ret_list_number, f_win_findbuf}, + {"win_getid", 0, 2, FEARG_1, arg2_number, + ret_number, f_win_getid}, + {"win_gettype", 0, 1, FEARG_1, arg1_number, + ret_string, f_win_gettype}, + {"win_gotoid", 1, 1, FEARG_1, arg1_number, + ret_number_bool, f_win_gotoid}, + {"win_id2tabwin", 1, 1, FEARG_1, arg1_number, + ret_list_number, f_win_id2tabwin}, + {"win_id2win", 1, 1, FEARG_1, arg1_number, + ret_number, f_win_id2win}, + {"win_move_separator", 2, 2, FEARG_1, arg2_number, + ret_number_bool, f_win_move_separator}, + {"win_move_statusline", 2, 2, FEARG_1, arg2_number, + ret_number_bool, f_win_move_statusline}, + {"win_screenpos", 1, 1, FEARG_1, arg1_number, + ret_list_number, f_win_screenpos}, + {"win_splitmove", 2, 3, FEARG_1, arg3_number_number_dict, + ret_number_bool, f_win_splitmove}, + {"winbufnr", 1, 1, FEARG_1, arg1_number, + ret_number, f_winbufnr}, + {"wincol", 0, 0, 0, NULL, + ret_number, f_wincol}, + {"windowsversion", 0, 0, 0, NULL, + ret_string, f_windowsversion}, + {"winheight", 1, 1, FEARG_1, arg1_number, + ret_number, f_winheight}, + {"winlayout", 0, 1, FEARG_1, arg1_number, + ret_list_any, f_winlayout}, + {"winline", 0, 0, 0, NULL, + ret_number, f_winline}, + {"winnr", 0, 1, FEARG_1, arg1_string, + ret_number, f_winnr}, + {"winrestcmd", 0, 0, 0, NULL, + ret_string, f_winrestcmd}, + {"winrestview", 1, 1, FEARG_1, arg1_dict_any, + ret_void, f_winrestview}, + {"winsaveview", 0, 0, 0, NULL, + ret_dict_number, f_winsaveview}, + {"winwidth", 1, 1, FEARG_1, arg1_number, + ret_number, f_winwidth}, + {"wordcount", 0, 0, 0, NULL, + ret_dict_number, f_wordcount}, + {"writefile", 2, 3, FEARG_1, arg23_writefile, + ret_number_bool, f_writefile}, + {"xor", 2, 2, FEARG_1, arg2_number, + ret_number, f_xor}, +}; + +/* + * Return true if specified function allows a type as an argument. + */ + static int +func_allows_type(int idx) +{ + return (global_functions[idx].f_argtype & FE_X) != 0; +} + +/* + * Function given to ExpandGeneric() to obtain the list of internal + * or user defined function names. + */ + char_u * +get_function_name(expand_T *xp, int idx) +{ + static int intidx = -1; + char_u *name; + + if (idx == 0) + intidx = -1; + if (intidx < 0) + { + name = get_user_func_name(xp, idx); + if (name != NULL) + { + if (*name != NUL && *name != '<' + && STRNCMP("g:", xp->xp_pattern, 2) == 0) + return cat_prefix_varname('g', name); + return name; + } + } + if (++intidx < (int)ARRAY_LENGTH(global_functions)) + { + // Skip if the function doesn't have an implementation (feature not + // implemented). + if (global_functions[intidx].f_func == NULL) + return (char_u *)""; + STRCPY(IObuff, global_functions[intidx].f_name); + STRCAT(IObuff, "("); + if (global_functions[intidx].f_max_argc == 0) + STRCAT(IObuff, ")"); + return IObuff; + } + + return NULL; +} + +/* + * Function given to ExpandGeneric() to obtain the list of internal or + * user defined variable or function names. + */ + char_u * +get_expr_name(expand_T *xp, int idx) +{ + static int intidx = -1; + char_u *name; + + if (idx == 0) + intidx = -1; + if (intidx < 0) + { + name = get_function_name(xp, idx); + if (name != NULL) + return name; + } + return get_user_var_name(xp, ++intidx); +} + +/* + * Find internal function "name" in table "global_functions". + * Return index, or -1 if not found or "implemented" is TRUE and the function + * is not implemented. + */ + static int +find_internal_func_opt(char_u *name, int implemented) +{ + int first = 0; + int last; + int cmp; + int x; + + last = (int)ARRAY_LENGTH(global_functions) - 1; + + // Find the function name in the table. Binary search. + while (first <= last) + { + x = first + ((unsigned)(last - first) >> 1); + cmp = STRCMP(name, global_functions[x].f_name); + if (cmp < 0) + last = x - 1; + else if (cmp > 0) + first = x + 1; + else if (implemented && global_functions[x].f_func == NULL) + break; + else + return x; + } + return -1; +} + +/* + * Find internal function "name" in table "global_functions". + * Return index, or -1 if not found or the function is not implemented. + */ + int +find_internal_func(char_u *name) +{ + return find_internal_func_opt(name, TRUE); +} + + int +has_internal_func(char_u *name) +{ + return find_internal_func_opt(name, TRUE) >= 0; +} + + static int +has_internal_func_name(char_u *name) +{ + return find_internal_func_opt(name, FALSE) >= 0; +} + + char * +internal_func_name(int idx) +{ + return global_functions[idx].f_name; +} + +/* + * Check the argument types for builtin function "idx". + * Uses the list of types on the type stack: "types". + * Return FAIL and gives an error message when a type is wrong. + */ + int +internal_func_check_arg_types( + type2_T *types, + int idx, + int argcount, + cctx_T *cctx) +{ + // Some internal functions accept types like Class as arguments. For other + // functions, check the arguments are not types. + if (!(func_allows_type(idx))) + { + for (int i = 0; i < argcount; ++i) + if (check_type_is_value(types[i].type_curr) == FAIL) + return FAIL; + } + + argcheck_T *argchecks = global_functions[idx].f_argcheck; + + if (argchecks == NULL) + return OK; + + argcontext_T context; + + context.arg_count = argcount; + context.arg_types = types; + context.arg_cctx = cctx; + for (int i = 0; i < argcount && argchecks[i] != NULL; ++i) + { + context.arg_idx = i; + if (argchecks[i](types[i].type_curr, types[i].type_decl, + &context) == FAIL) + return FAIL; + } + return OK; +} + +/* + * Get the argument count for function "idx". + * "argcount" is the total argument count, "min_argcount" the non-optional + * argument count. + */ + void +internal_func_get_argcount(int idx, int *argcount, int *min_argcount) +{ + *argcount = global_functions[idx].f_max_argc; + *min_argcount = global_functions[idx].f_min_argc; +} + +/* + * Call the "f_retfunc" function to obtain the return type of function "idx". + * "decl_type" is set to the declared type. + * "argtypes" is the list of argument types or NULL when there are no + * arguments. + * "argcount" may be less than the actual count when only getting the type. + */ + type_T * +internal_func_ret_type( + int idx, + int argcount, + type2_T *argtypes, + type_T **decl_type, + garray_T *type_gap) +{ + type_T *ret; + + current_type_gap = type_gap; + *decl_type = NULL; + ret = global_functions[idx].f_retfunc(argcount, argtypes, decl_type); + if (*decl_type == NULL) + *decl_type = ret; + current_type_gap = NULL; + return ret; +} + +/* + * Return TRUE if "idx" is for the map() function. + */ + int +internal_func_is_map(int idx) +{ + return global_functions[idx].f_func == f_map; +} + +/* + * Check the argument count to use for internal function "idx". + * Returns -1 for failure, 0 if no method base accepted, 1 if method base is + * first argument, 2 if method base is second argument, etc. 9 if method base + * is last argument. + */ + int +check_internal_func(int idx, int argcount) +{ + funcerror_T res; + char *name; + + if (argcount < global_functions[idx].f_min_argc) + res = FCERR_TOOFEW; + else if (argcount > global_functions[idx].f_max_argc) + res = FCERR_TOOMANY; + else + return global_functions[idx].f_argtype & FEARG_MASK; + + name = internal_func_name(idx); + if (res == FCERR_TOOMANY) + semsg(_(e_too_many_arguments_for_function_str), name); + else + semsg(_(e_not_enough_arguments_for_function_str), name); + return -1; +} + +/* + * Some internal functions accept types like Class as arguments. For other + * functions, check the arguments are not types. + * + * Return OK/FAIL. + */ + static int +check_args_for_type(int idx, int argcount, typval_T *argvars) +{ + if (!func_allows_type(idx)) + { + for (int i = 0; i < argcount; ++i) + if (check_typval_is_value(&argvars[i]) == FAIL) + return FAIL; + } + return OK; +} + + funcerror_T +call_internal_func( + char_u *name, + int argcount, + typval_T *argvars, + typval_T *rettv) +{ + int i; + + i = find_internal_func(name); + if (i < 0) + return FCERR_UNKNOWN; + if (argcount < global_functions[i].f_min_argc) + return FCERR_TOOFEW; + if (argcount > global_functions[i].f_max_argc) + return FCERR_TOOMANY; + if (check_args_for_type(i, argcount, argvars) == FAIL) + return FCERR_OTHER; + argvars[argcount].v_type = VAR_UNKNOWN; + global_functions[i].f_func(argvars, rettv); + return FCERR_NONE; +} + + void +call_internal_func_by_idx( + int idx, + typval_T *argvars, + typval_T *rettv) +{ + global_functions[idx].f_func(argvars, rettv); +} + +/* + * Invoke a method for base->method(). + */ + funcerror_T +call_internal_method( + char_u *name, + int argcount, + typval_T *argvars, + typval_T *rettv, + typval_T *basetv) +{ + int fi; + typval_T argv[MAX_FUNC_ARGS + 1]; + + fi = find_internal_func(name); + if (fi < 0) + return FCERR_UNKNOWN; + if ((global_functions[fi].f_argtype & FEARG_MASK) == 0) + return FCERR_NOTMETHOD; + if (argcount + 1 < global_functions[fi].f_min_argc) + return FCERR_TOOFEW; + if (argcount + 1 > global_functions[fi].f_max_argc) + return FCERR_TOOMANY; + if (check_args_for_type(fi, argcount, argvars) == FAIL) + return FCERR_OTHER; + + if ((global_functions[fi].f_argtype & FEARG_MASK) == FEARG_2) + { + if (argcount < 1) + return FCERR_TOOFEW; + + // base value goes second + argv[0] = argvars[0]; + argv[1] = *basetv; + for (int i = 1; i < argcount; ++i) + argv[i + 1] = argvars[i]; + } + else if ((global_functions[fi].f_argtype & FEARG_MASK) == FEARG_3) + { + if (argcount < 2) + return FCERR_TOOFEW; + + // base value goes third + argv[0] = argvars[0]; + argv[1] = argvars[1]; + argv[2] = *basetv; + for (int i = 2; i < argcount; ++i) + argv[i + 1] = argvars[i]; + } + else if ((global_functions[fi].f_argtype & FEARG_MASK) == FEARG_4) + { + if (argcount < 3) + return FCERR_TOOFEW; + + // base value goes fourth + argv[0] = argvars[0]; + argv[1] = argvars[1]; + argv[2] = argvars[2]; + argv[3] = *basetv; + for (int i = 3; i < argcount; ++i) + argv[i + 1] = argvars[i]; + } + else + { + // FEARG_1: base value goes first + argv[0] = *basetv; + for (int i = 0; i < argcount; ++i) + argv[i + 1] = argvars[i]; + } + argv[argcount + 1].v_type = VAR_UNKNOWN; + + if (check_args_for_type(fi, argcount + 1, argv) == FAIL) + return FCERR_OTHER; + + global_functions[fi].f_func(argv, rettv); + return FCERR_NONE; +} + +/* + * Return TRUE for a non-zero Number and a non-empty String. + */ + int +non_zero_arg(typval_T *argvars) +{ + return ((argvars[0].v_type == VAR_NUMBER + && argvars[0].vval.v_number != 0) + || (argvars[0].v_type == VAR_BOOL + && argvars[0].vval.v_number == VVAL_TRUE) + || (argvars[0].v_type == VAR_STRING + && argvars[0].vval.v_string != NULL + && *argvars[0].vval.v_string != NUL)); +} + +/* + * "and(expr, expr)" function + */ + static void +f_and(typval_T *argvars, typval_T *rettv) +{ + if (in_vim9script() + && (check_for_number_arg(argvars, 0) == FAIL + || check_for_number_arg(argvars, 1) == FAIL)) + return; + + rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) + & tv_get_number_chk(&argvars[1], NULL); +} + +/* + * "balloon_show()" function + */ +#ifdef FEAT_BEVAL + static void +f_balloon_gettext(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + if (balloonEval == NULL) + return; + + if (balloonEval->msg == NULL) + rettv->vval.v_string = NULL; + else + rettv->vval.v_string = vim_strsave(balloonEval->msg); +} + + static void +f_balloon_show(typval_T *argvars, typval_T *rettv UNUSED) +{ + if (balloonEval == NULL) + return; + + if (in_vim9script() + && check_for_string_or_list_arg(argvars, 0) == FAIL) + return; + + if (argvars[0].v_type == VAR_LIST +# ifdef FEAT_GUI + && !gui.in_use +# endif + ) + { + list_T *l = argvars[0].vval.v_list; + + // empty list removes the balloon + post_balloon(balloonEval, NULL, + l == NULL || l->lv_len == 0 ? NULL : l); + } + else + { + char_u *mesg; + + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + mesg = tv_get_string_chk(&argvars[0]); + if (mesg != NULL) + // empty string removes the balloon + post_balloon(balloonEval, *mesg == NUL ? NULL : mesg, NULL); + } +} + +# if defined(FEAT_BEVAL_TERM) + static void +f_balloon_split(typval_T *argvars, typval_T *rettv UNUSED) +{ + if (rettv_list_alloc(rettv) != OK) + return; + + char_u *msg; + + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + msg = tv_get_string_chk(&argvars[0]); + if (msg != NULL) + { + pumitem_T *array; + int size = split_message(msg, &array); + + // Skip the first and last item, they are always empty. + for (int i = 1; i < size - 1; ++i) + list_append_string(rettv->vval.v_list, array[i].pum_text, -1); + while (size > 0) + vim_free(array[--size].pum_text); + vim_free(array); + } +} +# endif +#endif + +/* + * Get the buffer from "arg" and give an error and return NULL if it is not + * valid. + */ + buf_T * +get_buf_arg(typval_T *arg) +{ + buf_T *buf; + + ++emsg_off; + buf = tv_get_buf(arg, FALSE); + --emsg_off; + if (buf == NULL) + semsg(_(e_invalid_buffer_name_str), tv_get_string(arg)); + return buf; +} + +/* + * "byte2line(byte)" function + */ + static void +f_byte2line(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifndef FEAT_BYTEOFF + rettv->vval.v_number = -1; +#else + long boff = 0; + + if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL) + return; + + boff = tv_get_number(&argvars[0]) - 1; // boff gets -1 on type error + if (boff < 0) + rettv->vval.v_number = -1; + else + rettv->vval.v_number = ml_find_line_or_offset(curbuf, + (linenr_T)0, &boff); +#endif +} + +/* + * "call(func, arglist [, dict])" function + */ + static void +f_call(typval_T *argvars, typval_T *rettv) +{ + char_u *func; + partial_T *partial = NULL; + dict_T *selfdict = NULL; + char_u *dot; + char_u *tofree = NULL; + + if (in_vim9script() + && (check_for_string_or_func_arg(argvars, 0) == FAIL + || check_for_list_arg(argvars, 1) == FAIL + || check_for_opt_dict_arg(argvars, 2) == FAIL)) + return; + + if (check_for_list_arg(argvars, 1) == FAIL) + return; + if (argvars[1].vval.v_list == NULL) + return; + + if (argvars[0].v_type == VAR_FUNC) + func = argvars[0].vval.v_string; + else if (argvars[0].v_type == VAR_PARTIAL) + { + partial = argvars[0].vval.v_partial; + func = partial_name(partial); + } + else + func = tv_get_string(&argvars[0]); + if (func == NULL || *func == NUL) + return; // type error, empty name or null function + + dot = vim_strchr(func, '.'); + if (dot != NULL) + { + imported_T *import = find_imported(func, dot - func, TRUE); + + if (import != NULL && SCRIPT_ID_VALID(import->imp_sid)) + { + scriptitem_T *si = SCRIPT_ITEM(import->imp_sid); + + if (si->sn_autoload_prefix != NULL) + { + // Turn "import.Func" into "scriptname#Func". + tofree = concat_str(si->sn_autoload_prefix, dot + 1); + if (tofree == NULL) + return; + func = tofree; + } + } + } + + if (argvars[2].v_type != VAR_UNKNOWN) + { + if (check_for_dict_arg(argvars, 2) == FAIL) + return; + + selfdict = argvars[2].vval.v_dict; + } + + (void)func_call(func, &argvars[1], partial, selfdict, rettv); + + vim_free(tofree); +} + +/* + * "changenr()" function + */ + static void +f_changenr(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = curbuf->b_u_seq_cur; +} + +/* + * "char2nr(string)" function + */ + static void +f_char2nr(typval_T *argvars, typval_T *rettv) +{ + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_opt_bool_arg(argvars, 1) == FAIL)) + return; + + if (has_mbyte) + { + int utf8 = 0; + + if (argvars[1].v_type != VAR_UNKNOWN) + utf8 = (int)tv_get_bool_chk(&argvars[1], NULL); + + if (utf8) + rettv->vval.v_number = utf_ptr2char(tv_get_string(&argvars[0])); + else + rettv->vval.v_number = (*mb_ptr2char)(tv_get_string(&argvars[0])); + } + else + rettv->vval.v_number = tv_get_string(&argvars[0])[0]; +} + +/* + * Get the current cursor column and store it in 'rettv'. If 'charcol' is TRUE, + * returns the character index of the column. Otherwise, returns the byte index + * of the column. + */ + static void +get_col(typval_T *argvars, typval_T *rettv, int charcol) +{ + colnr_T col = 0; + pos_T *fp; + switchwin_T switchwin; + int winchanged = FALSE; + + if (check_for_string_or_list_arg(argvars, 0) == FAIL + || check_for_opt_number_arg(argvars, 1) == FAIL) + return; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + tabpage_T *tp; + win_T *wp; + + // use the window specified in the second argument + wp = win_id2wp_tp((int)tv_get_number(&argvars[1]), &tp); + if (wp == NULL || tp == NULL) + return; + + if (switch_win_noblock(&switchwin, wp, tp, TRUE) != OK) + return; + + check_cursor(); + winchanged = TRUE; + } + + int fnum = curbuf->b_fnum; + fp = var2fpos(&argvars[0], FALSE, &fnum, charcol); + if (fp != NULL && fnum == curbuf->b_fnum) + { + if (fp->col == MAXCOL) + { + // '> can be MAXCOL, get the length of the line then + if (fp->lnum <= curbuf->b_ml.ml_line_count) + col = (colnr_T)STRLEN(ml_get(fp->lnum)) + 1; + else + col = MAXCOL; + } + else + { + col = fp->col + 1; + // col(".") when the cursor is on the NUL at the end of the line + // because of "coladd" can be seen as an extra column. + if (virtual_active() && fp == &curwin->w_cursor) + { + char_u *p = ml_get_cursor(); + + if (curwin->w_cursor.coladd >= (colnr_T)chartabsize(p, + curwin->w_virtcol - curwin->w_cursor.coladd)) + { + int l; + + if (*p != NUL && p[(l = (*mb_ptr2len)(p))] == NUL) + col += l; + } + } + } + } + rettv->vval.v_number = col; + + if (winchanged) + restore_win_noblock(&switchwin, TRUE); +} + +/* + * "charcol()" function + */ + static void +f_charcol(typval_T *argvars, typval_T *rettv) +{ + get_col(argvars, rettv, TRUE); +} + + win_T * +get_optional_window(typval_T *argvars, int idx) +{ + win_T *win = curwin; + + if (argvars[idx].v_type == VAR_UNKNOWN) + return curwin; + + win = find_win_by_nr_or_id(&argvars[idx]); + if (win == NULL) + { + emsg(_(e_invalid_window_number)); + return NULL; + } + return win; +} + +/* + * "col(string)" function + */ + static void +f_col(typval_T *argvars, typval_T *rettv) +{ + get_col(argvars, rettv, FALSE); +} + +/* + * "confirm(message, buttons[, default [, type]])" function + */ + static void +f_confirm(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) + char_u *message; + char_u *buttons = NULL; + char_u buf[NUMBUFLEN]; + char_u buf2[NUMBUFLEN]; + int def = 1; + int type = VIM_GENERIC; + char_u *typestr; + int error = FALSE; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || (check_for_opt_string_arg(argvars, 1) == FAIL + || (argvars[1].v_type != VAR_UNKNOWN + && (check_for_opt_number_arg(argvars, 2) == FAIL + || (argvars[2].v_type != VAR_UNKNOWN + && check_for_opt_string_arg(argvars, 3) == FAIL)))))) + return; + + message = tv_get_string_chk(&argvars[0]); + if (message == NULL) + error = TRUE; + if (argvars[1].v_type != VAR_UNKNOWN) + { + buttons = tv_get_string_buf_chk(&argvars[1], buf); + if (buttons == NULL) + error = TRUE; + if (argvars[2].v_type != VAR_UNKNOWN) + { + def = (int)tv_get_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) + { + typestr = tv_get_string_buf_chk(&argvars[3], buf2); + if (typestr == NULL) + error = TRUE; + else + { + switch (TOUPPER_ASC(*typestr)) + { + case 'E': type = VIM_ERROR; break; + case 'Q': type = VIM_QUESTION; break; + case 'I': type = VIM_INFO; break; + case 'W': type = VIM_WARNING; break; + case 'G': type = VIM_GENERIC; break; + } + } + } + } + } + + if (buttons == NULL || *buttons == NUL) + buttons = (char_u *)_("&Ok"); + + if (!error) + rettv->vval.v_number = do_dialog(type, NULL, message, buttons, + def, NULL, FALSE); +#endif +} + +/* + * "copy()" function + */ + static void +f_copy(typval_T *argvars, typval_T *rettv) +{ + item_copy(&argvars[0], rettv, FALSE, TRUE, 0); +} + +/* + * Set the cursor position. + * 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, int charcol) +{ + long lnum, col; + long coladd = 0; + int set_curswant = TRUE; + + if (in_vim9script() + && (check_for_string_or_number_or_list_arg(argvars, 0) == FAIL + || check_for_opt_number_arg(argvars, 1) == FAIL + || (argvars[1].v_type != VAR_UNKNOWN + && check_for_opt_number_arg(argvars, 2) == FAIL))) + return; + + rettv->vval.v_number = -1; + if (argvars[0].v_type == VAR_LIST) + { + pos_T pos; + colnr_T curswant = -1; + + if (list2fpos(argvars, &pos, NULL, &curswant, charcol) == FAIL) + { + emsg(_(e_invalid_argument)); + return; + } + lnum = pos.lnum; + col = pos.col; + coladd = pos.coladd; + if (curswant >= 0) + { + curwin->w_curswant = curswant - 1; + set_curswant = FALSE; + } + } + else if ((argvars[0].v_type == VAR_NUMBER + || argvars[0].v_type == VAR_STRING) + && (argvars[1].v_type == VAR_NUMBER + || argvars[1].v_type == VAR_STRING)) + { + lnum = tv_get_lnum(argvars); + if (lnum < 0) + semsg(_(e_invalid_argument_str), tv_get_string(&argvars[0])); + else if (lnum == 0) + lnum = curwin->w_cursor.lnum; + col = (long)tv_get_number_chk(&argvars[1], NULL); + if (charcol) + col = buf_charidx_to_byteidx(curbuf, lnum, col) + 1; + if (argvars[2].v_type != VAR_UNKNOWN) + coladd = (long)tv_get_number_chk(&argvars[2], NULL); + } + else + { + emsg(_(e_invalid_argument)); + return; + } + if (lnum < 0 || col < 0 || coladd < 0) + return; // type error; errmsg already given + if (lnum > 0) + curwin->w_cursor.lnum = lnum; + if (col > 0) + curwin->w_cursor.col = col - 1; + curwin->w_cursor.coladd = coladd; + + // Make sure the cursor is in a valid position. + check_cursor(); + // Correct cursor for multi-byte character. + if (has_mbyte) + mb_adjust_cursor(); + + curwin->w_set_curswant = set_curswant; + rettv->vval.v_number = 0; +} + +/* + * "cursor(lnum, col)" function, or + * "cursor(list)" + * + * Moves the cursor to the specified line and column. + * Returns 0 when the position could be set, -1 otherwise. + */ + static void +f_cursor(typval_T *argvars, typval_T *rettv) +{ + set_cursorpos(argvars, rettv, FALSE); +} + +#ifdef MSWIN +/* + * "debugbreak()" function + */ + static void +f_debugbreak(typval_T *argvars, typval_T *rettv) +{ + int pid; + + rettv->vval.v_number = FAIL; + if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL) + return; + + pid = (int)tv_get_number(&argvars[0]); + if (pid == 0) + { + emsg(_(e_invalid_argument)); + return; + } + + HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid); + if (hProcess == NULL) + return; + + DebugBreakProcess(hProcess); + CloseHandle(hProcess); + rettv->vval.v_number = OK; +} +#endif + +/* + * "deepcopy()" function + */ + static void +f_deepcopy(typval_T *argvars, typval_T *rettv) +{ + varnumber_T noref = 0; + + if (check_for_opt_bool_arg(argvars, 1) == FAIL) + return; + + if (argvars[1].v_type != VAR_UNKNOWN) + noref = tv_get_bool_chk(&argvars[1], NULL); + + item_copy(&argvars[0], rettv, TRUE, TRUE, noref == 0 ? get_copyID() : 0); +} + +/* + * "did_filetype()" function + */ + static void +f_did_filetype(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ + rettv->vval.v_number = did_filetype; +} + +/* + * "echoraw({expr})" function + */ + static void +f_echoraw(typval_T *argvars, typval_T *rettv UNUSED) +{ + char_u *str; + + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + str = tv_get_string_chk(&argvars[0]); + if (str != NULL && *str != NUL) + { + out_str(str); + out_flush(); + } +} + +/* + * "empty({expr})" function + */ + static void +f_empty(typval_T *argvars, typval_T *rettv) +{ + int n = FALSE; + + switch (argvars[0].v_type) + { + case VAR_STRING: + case VAR_FUNC: + n = argvars[0].vval.v_string == NULL + || *argvars[0].vval.v_string == NUL; + break; + case VAR_PARTIAL: + n = FALSE; + break; + case VAR_NUMBER: + n = argvars[0].vval.v_number == 0; + break; + case VAR_FLOAT: + n = argvars[0].vval.v_float == 0.0; + break; + case VAR_LIST: + n = argvars[0].vval.v_list == NULL + || argvars[0].vval.v_list->lv_len == 0; + break; + case VAR_DICT: + n = argvars[0].vval.v_dict == NULL + || argvars[0].vval.v_dict->dv_hashtab.ht_used == 0; + break; + case VAR_BOOL: + case VAR_SPECIAL: + n = argvars[0].vval.v_number != VVAL_TRUE; + break; + case VAR_CLASS: + n = argvars[0].vval.v_class != NULL; + break; + case VAR_OBJECT: + n = argvars[0].vval.v_object != NULL; + break; + + case VAR_BLOB: + n = argvars[0].vval.v_blob == NULL + || argvars[0].vval.v_blob->bv_ga.ga_len == 0; + break; + + case VAR_JOB: +#ifdef FEAT_JOB_CHANNEL + n = argvars[0].vval.v_job == NULL + || argvars[0].vval.v_job->jv_status != JOB_STARTED; + break; +#endif + case VAR_CHANNEL: +#ifdef FEAT_JOB_CHANNEL + n = argvars[0].vval.v_channel == NULL + || !channel_is_open(argvars[0].vval.v_channel); + break; +#endif + case VAR_TYPEALIAS: + n = argvars[0].vval.v_typealias == NULL + || argvars[0].vval.v_typealias->ta_name == NULL + || *argvars[0].vval.v_typealias->ta_name == NUL; + break; + + case VAR_UNKNOWN: + case VAR_ANY: + case VAR_VOID: + case VAR_INSTR: + internal_error_no_abort("f_empty(UNKNOWN)"); + n = TRUE; + break; + } + + rettv->vval.v_number = n; +} + +/* + * "environ()" function + */ + static void +f_environ(typval_T *argvars UNUSED, typval_T *rettv) +{ +#if !defined(AMIGA) + int i = 0; + char_u *entry, *value; +# if defined (MSWIN) +# if !defined(_UCRT) + extern wchar_t **_wenviron; +# endif +# else + extern char **environ; +# endif + + if (rettv_dict_alloc(rettv) == FAIL) + return; + +# ifdef MSWIN + if (*_wenviron == NULL) + return; +# else + if (*environ == NULL) + return; +# endif + + for (i = 0; ; ++i) + { +# ifdef MSWIN + short_u *p; + + if ((p = (short_u *)_wenviron[i]) == NULL) + return; + entry = utf16_to_enc(p, NULL); +# else + if ((entry = (char_u *)environ[i]) == NULL) + return; + entry = vim_strsave(entry); +# endif + if (entry == NULL) // out of memory + return; + if ((value = vim_strchr(entry, '=')) == NULL) + { + vim_free(entry); + continue; + } + *value++ = NUL; + dict_add_string(rettv->vval.v_dict, (char *)entry, value); + vim_free(entry); + } +#endif +} + +/* + * "err_teapot()" function + */ + static void +f_err_teapot(typval_T *argvars, typval_T *rettv UNUSED) +{ + if (argvars[0].v_type != VAR_UNKNOWN) + { + if (argvars[0].v_type == VAR_STRING) + { + char_u *s = tv_get_string_strict(&argvars[0]); + if (*skipwhite(s) == NUL) + return; + } + + int err = FALSE; + int do_503 = eval_expr_to_bool(&argvars[0], &err); + if (!err && do_503) + { + emsg(_(e_coffee_currently_not_available)); + return; + } + } + + emsg(_(e_im_a_teapot)); +} + +/* + * "escape({string}, {chars})" function + */ + static void +f_escape(typval_T *argvars, typval_T *rettv) +{ + char_u buf[NUMBUFLEN]; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_string_arg(argvars, 1) == FAIL)) + return; + + rettv->vval.v_string = vim_strsave_escaped(tv_get_string(&argvars[0]), + tv_get_string_buf(&argvars[1], buf)); + rettv->v_type = VAR_STRING; +} + +/* + * "eval()" function + */ + static void +f_eval(typval_T *argvars, typval_T *rettv) +{ + char_u *s, *p; + + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + s = tv_get_string_chk(&argvars[0]); + if (s != NULL) + s = skipwhite(s); + + p = s; + if (s == NULL || eval1(&s, rettv, &EVALARG_EVALUATE) == FAIL) + { + if (p != NULL && !aborting()) + semsg(_(e_invalid_expression_str), p); + need_clr_eos = FALSE; + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + } + else if (*s != NUL) + semsg(_(e_trailing_characters_str), s); +} + +/* + * "eventhandler()" function + */ + static void +f_eventhandler(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = vgetc_busy || input_busy; +} + +static garray_T redir_execute_ga; + +/* + * Append "value[value_len]" to the execute() output. + */ + void +execute_redir_str(char_u *value, int value_len) +{ + int len; + + if (value_len == -1) + len = (int)STRLEN(value); // Append the entire string + else + len = value_len; // Append only "value_len" characters + if (ga_grow(&redir_execute_ga, len) == FAIL) + return; + + mch_memmove((char *)redir_execute_ga.ga_data + + redir_execute_ga.ga_len, value, len); + redir_execute_ga.ga_len += len; +} + +#if defined(FEAT_LUA) || defined(PROTO) +/* + * Get next line from a string containing NL separated lines. + * Called by do_cmdline() to get the next line. + * Returns an allocated string, or NULL when at the end of the string. + */ + static char_u * +get_str_line( + int c UNUSED, + void *cookie, + int indent UNUSED, + getline_opt_T options UNUSED) +{ + char_u *start = *(char_u **)cookie; + char_u *line; + char_u *p; + + p = start; + if (p == NULL || *p == NUL) + return NULL; + p = vim_strchr(p, '\n'); + if (p == NULL) + line = vim_strsave(start); + else + { + line = vim_strnsave(start, p - start); + p++; + } + + *(char_u **)cookie = p; + return line; +} + +/* + * Execute a series of Ex commands in 'str' + */ + void +execute_cmds_from_string(char_u *str) +{ + do_cmdline(NULL, get_str_line, (void *)&str, + DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); +} +#endif + +/* + * Get next line from a list. + * Called by do_cmdline() to get the next line. + * Returns allocated string, or NULL for end of function. + */ + char_u * +get_list_line( + int c UNUSED, + void *cookie, + int indent UNUSED, + getline_opt_T options UNUSED) +{ + listitem_T **p = (listitem_T **)cookie; + listitem_T *item = *p; + char_u buf[NUMBUFLEN]; + char_u *s; + + if (item == NULL) + return NULL; + s = tv_get_string_buf_chk(&item->li_tv, buf); + *p = item->li_next; + return s == NULL ? NULL : vim_strsave(s); +} + +/* + * "execute()" function + */ + void +execute_common(typval_T *argvars, typval_T *rettv, int arg_off) +{ + char_u *cmd = NULL; + list_T *list = NULL; + int save_msg_silent = msg_silent; + int save_emsg_silent = emsg_silent; + int save_emsg_noredir = emsg_noredir; + int save_redir_execute = redir_execute; + int save_redir_off = redir_off; + garray_T save_ga; + int save_msg_col = msg_col; + int save_sticky_cmdmod_flags = sticky_cmdmod_flags; + int echo_output = FALSE; + + rettv->vval.v_string = NULL; + rettv->v_type = VAR_STRING; + + if (argvars[arg_off].v_type == VAR_LIST) + { + list = argvars[arg_off].vval.v_list; + if (list == NULL || list->lv_len == 0) + // empty list, no commands, empty output + return; + ++list->lv_refcount; + } + else if (argvars[arg_off].v_type == VAR_JOB + || argvars[arg_off].v_type == VAR_CHANNEL) + { + semsg(_(e_using_invalid_value_as_string_str), + vartype_name(argvars[arg_off].v_type)); + return; + } + else + { + cmd = tv_get_string_chk(&argvars[arg_off]); + if (cmd == NULL) + return; + } + + if (argvars[arg_off + 1].v_type != VAR_UNKNOWN) + { + char_u buf[NUMBUFLEN]; + char_u *s = tv_get_string_buf_chk_strict(&argvars[arg_off + 1], buf, + in_vim9script()); + + if (s == NULL) + return; + if (*s == NUL) + echo_output = TRUE; + if (STRNCMP(s, "silent", 6) == 0) + ++msg_silent; + if (STRCMP(s, "silent!") == 0) + { + emsg_silent = TRUE; + emsg_noredir = TRUE; + } + } + else + ++msg_silent; + + if (redir_execute) + save_ga = redir_execute_ga; + ga_init2(&redir_execute_ga, sizeof(char), 500); + redir_execute = TRUE; + redir_off = FALSE; + if (!echo_output) + msg_col = 0; // prevent leading spaces + + // For "legacy call execute('cmd')" and "vim9cmd execute('cmd')" apply the + // command modifiers to "cmd". + sticky_cmdmod_flags = cmdmod.cmod_flags & (CMOD_LEGACY | CMOD_VIM9CMD); + if (cmd != NULL) + do_cmdline_cmd(cmd); + else + { + listitem_T *item; + + CHECK_LIST_MATERIALIZE(list); + item = list->lv_first; + do_cmdline(NULL, get_list_line, (void *)&item, + DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); + --list->lv_refcount; + } + sticky_cmdmod_flags = save_sticky_cmdmod_flags; + + // Need to append a NUL to the result. + if (ga_grow(&redir_execute_ga, 1) == OK) + { + ((char *)redir_execute_ga.ga_data)[redir_execute_ga.ga_len] = NUL; + rettv->vval.v_string = redir_execute_ga.ga_data; + } + else + { + ga_clear(&redir_execute_ga); + rettv->vval.v_string = NULL; + } + msg_silent = save_msg_silent; + emsg_silent = save_emsg_silent; + emsg_noredir = save_emsg_noredir; + + redir_execute = save_redir_execute; + if (redir_execute) + redir_execute_ga = save_ga; + redir_off = save_redir_off; + + // "silent reg" or "silent echo x" leaves msg_col somewhere in the line. + if (echo_output) + // When not working silently: put it in column zero. A following + // "echon" will overwrite the message, unavoidably. + msg_col = 0; + else + // When working silently: Put it back where it was, since nothing + // should have been written. + msg_col = save_msg_col; +} + +/* + * "execute()" function + */ + static void +f_execute(typval_T *argvars, typval_T *rettv) +{ + if (in_vim9script() + && (check_for_string_or_list_arg(argvars, 0) == FAIL + || check_for_opt_string_arg(argvars, 1) == FAIL)) + return; + + execute_common(argvars, rettv, 0); +} + +/* + * "exists()" function + */ + void +f_exists(typval_T *argvars, typval_T *rettv) +{ + char_u *p; + int n = FALSE; + + if (in_vim9script() && check_for_nonempty_string_arg(argvars, 0) == FAIL) + return; + + p = tv_get_string(&argvars[0]); + if (*p == '$') // environment variable + { + // first try "normal" environment variables (fast) + if (mch_getenv(p + 1) != NULL) + n = TRUE; + else + { + // try expanding things like $VIM and ${HOME} + p = expand_env_save(p); + if (p != NULL && *p != '$') + n = TRUE; + vim_free(p); + } + } + else if (*p == '&' || *p == '+') // option + { + n = (eval_option(&p, NULL, TRUE) == OK); + if (*skipwhite(p) != NUL) + n = FALSE; // trailing garbage + } + else if (*p == '*') // internal or user defined function + { + n = function_exists(p + 1, FALSE); + } + else if (*p == '?') // internal function only + { + n = has_internal_func_name(p + 1); + } + else if (*p == ':') + { + n = cmd_exists(p + 1); + } + else if (*p == '#') + { + if (p[1] == '#') + n = autocmd_supported(p + 2); + else + n = au_exists(p + 1); + } + else // internal variable + { + n = var_exists(p); + } + + rettv->vval.v_number = n; +} + + static void +f_exists_compiled(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ + emsg(_(e_exists_compiled_can_only_be_used_in_def_function)); +} + +/* + * "expand()" function + */ + static void +f_expand(typval_T *argvars, typval_T *rettv) +{ + char_u *s; + int len; + int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND; + expand_T xpc; + int error = FALSE; + char_u *result; +#ifdef BACKSLASH_IN_FILENAME + char_u *p_csl_save = p_csl; +#endif + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_opt_bool_arg(argvars, 1) == FAIL + || (argvars[1].v_type != VAR_UNKNOWN + && check_for_opt_bool_arg(argvars, 2) == FAIL))) + return; + +#ifdef BACKSLASH_IN_FILENAME + // avoid using 'completeslash' here + p_csl = empty_option; +#endif + + rettv->v_type = VAR_STRING; + if (argvars[1].v_type != VAR_UNKNOWN + && argvars[2].v_type != VAR_UNKNOWN + && tv_get_bool_chk(&argvars[2], &error) + && !error) + rettv_list_set(rettv, NULL); + + s = tv_get_string(&argvars[0]); + if (*s == '%' || *s == '#' || *s == '<') + { + char *errormsg = NULL; + + if (p_verbose == 0) + ++emsg_off; + result = eval_vars(s, s, &len, NULL, &errormsg, NULL, FALSE); + if (p_verbose == 0) + --emsg_off; + else if (errormsg != NULL) + emsg(errormsg); + if (rettv->v_type == VAR_LIST) + { + if (rettv_list_alloc(rettv) == OK && result != NULL) + list_append_string(rettv->vval.v_list, result, -1); + vim_free(result); + } + else + rettv->vval.v_string = result; + } + else + { + // When the optional second argument is non-zero, don't remove matches + // for 'wildignore' and don't put matches for 'suffixes' at the end. + if (argvars[1].v_type != VAR_UNKNOWN + && tv_get_bool_chk(&argvars[1], &error)) + options |= WILD_KEEP_ALL; + if (!error) + { + ExpandInit(&xpc); + xpc.xp_context = EXPAND_FILES; + if (p_wic) + options += WILD_ICASE; + if (rettv->v_type == VAR_STRING) + rettv->vval.v_string = ExpandOne(&xpc, s, NULL, + options, WILD_ALL); + else if (rettv_list_alloc(rettv) == OK) + { + ExpandOne(&xpc, s, NULL, options, WILD_ALL_KEEP); + for (int i = 0; i < xpc.xp_numfiles; i++) + list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); + ExpandCleanup(&xpc); + } + } + else + rettv->vval.v_string = NULL; + } +#ifdef BACKSLASH_IN_FILENAME + p_csl = p_csl_save; +#endif +} + +/* + * "expandcmd()" function + * Expand all the special characters in a command string. + */ + static void +f_expandcmd(typval_T *argvars, typval_T *rettv) +{ + exarg_T eap; + char_u *cmdstr; + char *errormsg = NULL; + int emsgoff = TRUE; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_opt_dict_arg(argvars, 1) == FAIL)) + return; + + if (argvars[1].v_type == VAR_DICT + && dict_get_bool(argvars[1].vval.v_dict, "errmsg", VVAL_FALSE)) + emsgoff = FALSE; + + rettv->v_type = VAR_STRING; + cmdstr = vim_strsave(tv_get_string(&argvars[0])); + + CLEAR_FIELD(eap); + eap.cmd = cmdstr; + eap.arg = cmdstr; + eap.argt |= EX_NOSPC; + eap.usefilter = FALSE; + eap.nextcmd = NULL; + eap.cmdidx = CMD_USER; + + if (emsgoff) + ++emsg_off; + if (expand_filename(&eap, &cmdstr, &errormsg) == FAIL) + if (!emsgoff && errormsg != NULL && *errormsg != NUL) + emsg(errormsg); + if (emsgoff) + --emsg_off; + + rettv->vval.v_string = cmdstr; +} + +/* + * "feedkeys()" function + */ + static void +f_feedkeys(typval_T *argvars, typval_T *rettv UNUSED) +{ + int remap = TRUE; + int insert = FALSE; + char_u *keys, *flags; + char_u nbuf[NUMBUFLEN]; + int typed = FALSE; + int execute = FALSE; + int context = FALSE; + int dangerous = FALSE; + int lowlevel = FALSE; + + // This is not allowed in the sandbox. If the commands would still be + // executed in the sandbox it would be OK, but it probably happens later, + // when "sandbox" is no longer set. + if (check_secure()) + return; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_opt_string_arg(argvars, 1) == FAIL)) + return; + + keys = tv_get_string(&argvars[0]); + + if (argvars[1].v_type != VAR_UNKNOWN) + { + flags = tv_get_string_buf(&argvars[1], nbuf); + for ( ; *flags != NUL; ++flags) + { + switch (*flags) + { + case 'n': remap = FALSE; break; + case 'm': remap = TRUE; break; + case 't': typed = TRUE; break; + case 'i': insert = TRUE; break; + case 'x': execute = TRUE; break; + case 'c': context = TRUE; break; + case '!': dangerous = TRUE; break; + case 'L': lowlevel = TRUE; break; + } + } + } + + if (*keys != NUL || execute) + { + if (lowlevel +#ifdef FEAT_VTP + && (!is_term_win32() + || (keys[0] == 3 && ctrl_c_interrupts && typed)) +#endif + ) + { +#ifdef USE_INPUT_BUF + ch_log(NULL, "feedkeys() lowlevel: %s", keys); + + int len = (int)STRLEN(keys); + for (int idx = 0; idx < len; ++idx) + { + // if a CTRL-C was typed, set got_int, similar to what + // happens in fill_input_buf() + if (keys[idx] == 3 && ctrl_c_interrupts && typed) + got_int = TRUE; + add_to_input_buf(keys + idx, 1); + } +#else + emsg(_(e_lowlevel_input_not_supported)); +#endif + } + else + { + // Need to escape K_SPECIAL and CSI before putting the string in + // the typeahead buffer. + char_u *keys_esc = vim_strsave_escape_csi(keys); + if (keys_esc == NULL) + return; + + ch_log(NULL, "feedkeys(%s): %s", typed ? "typed" : "", keys); + + ins_typebuf(keys_esc, (remap ? REMAP_YES : REMAP_NONE), + insert ? 0 : typebuf.tb_len, !typed, FALSE); + if (vgetc_busy +#ifdef FEAT_TIMERS + || timer_busy +#endif + || input_busy) + typebuf_was_filled = TRUE; + + vim_free(keys_esc); + } + + if (execute) + { + int save_msg_scroll = msg_scroll; + sctx_T save_sctx; + + // Avoid a 1 second delay when the keys start Insert mode. + msg_scroll = FALSE; + + ch_log(NULL, "feedkeys() executing"); + + if (context) + { + save_sctx = current_sctx; + current_sctx.sc_sid = 0; + current_sctx.sc_version = 0; + } + + if (!dangerous) + { + ++ex_normal_busy; + ++in_feedkeys; + } + exec_normal(TRUE, lowlevel, TRUE); + if (!dangerous) + { + --ex_normal_busy; + --in_feedkeys; + } + + msg_scroll |= save_msg_scroll; + + if (context) + current_sctx = save_sctx; + } + } +} + +/* + * "fnameescape({string})" function + */ + static void +f_fnameescape(typval_T *argvars, typval_T *rettv) +{ + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + rettv->vval.v_string = vim_strsave_fnameescape( + tv_get_string(&argvars[0]), VSE_NONE); + rettv->v_type = VAR_STRING; +} + +/* + * "foreground()" function + */ + static void +f_foreground(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_GUI + if (gui.in_use) + { + gui_mch_set_foreground(); + return; + } +#endif +#if defined(MSWIN) && (!defined(FEAT_GUI) || defined(VIMDLL)) + win32_set_foreground(); +#endif +} + +/* + * "function()" function + * "funcref()" function + */ + static void +common_function(typval_T *argvars, typval_T *rettv, int is_funcref) +{ + char_u *s; + char_u *name; + int use_string = FALSE; + partial_T *arg_pt = NULL; + char_u *trans_name = NULL; + int is_global = FALSE; + + if (in_vim9script() + && (check_for_string_or_func_arg(argvars, 0) == FAIL + || check_for_opt_list_arg(argvars, 1) == FAIL + || (argvars[1].v_type != VAR_UNKNOWN + && check_for_opt_dict_arg(argvars, 2) == FAIL))) + return; + + if (argvars[0].v_type == VAR_FUNC) + { + // function(MyFunc, [arg], dict) + s = argvars[0].vval.v_string; + } + else if (argvars[0].v_type == VAR_PARTIAL + && argvars[0].vval.v_partial != NULL) + { + // function(dict.MyFunc, [arg]) + arg_pt = argvars[0].vval.v_partial; + s = partial_name(arg_pt); + } + else + { + // function('MyFunc', [arg], dict) + s = tv_get_string(&argvars[0]); + use_string = TRUE; + } + if (s == NULL) + { + semsg(_(e_invalid_argument_str), "NULL"); + return; + } + + if ((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) || is_funcref) + { + name = s; + trans_name = save_function_name(&name, &is_global, FALSE, + TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL); + if (*name != NUL) + s = NULL; + } + + if (s == NULL || *s == NUL || (use_string && VIM_ISDIGIT(*s)) + || (is_funcref && trans_name == NULL)) + semsg(_(e_invalid_argument_str), + use_string ? tv_get_string(&argvars[0]) : s); + // Don't check an autoload name for existence here. + else if (trans_name != NULL && (is_funcref + ? find_func(trans_name, is_global) == NULL + : !translated_function_exists(trans_name, is_global))) + semsg(_(e_unknown_function_str_2), s); + else + { + int dict_idx = 0; + int arg_idx = 0; + list_T *list = NULL; + + if (STRNCMP(s, "s:", 2) == 0 || STRNCMP(s, "<SID>", 5) == 0) + // Expand s: and <SID> into <SNR>nr_, so that the function can + // also be called from another script. Using trans_function_name() + // would also work, but some plugins depend on the name being + // printable text. + name = get_scriptlocal_funcname(s); + else if (trans_name != NULL && *trans_name == K_SPECIAL) + name = alloc_printable_func_name(trans_name); + else + name = vim_strsave(s); + + if (argvars[1].v_type != VAR_UNKNOWN) + { + if (argvars[2].v_type != VAR_UNKNOWN) + { + // function(name, [args], dict) + arg_idx = 1; + dict_idx = 2; + } + else if (argvars[1].v_type == VAR_DICT) + // function(name, dict) + dict_idx = 1; + else + // function(name, [args]) + arg_idx = 1; + if (dict_idx > 0) + { + if (check_for_dict_arg(argvars, dict_idx) == FAIL) + { + vim_free(name); + goto theend; + } + if (argvars[dict_idx].vval.v_dict == NULL) + dict_idx = 0; + } + if (arg_idx > 0) + { + if (argvars[arg_idx].v_type != VAR_LIST) + { + emsg(_(e_second_argument_of_function_must_be_list_or_dict)); + vim_free(name); + goto theend; + } + list = argvars[arg_idx].vval.v_list; + if (list == NULL || list->lv_len == 0) + arg_idx = 0; + else if (list->lv_len > MAX_FUNC_ARGS) + { + emsg_funcname(e_too_many_arguments_for_function_str, s); + vim_free(name); + goto theend; + } + } + } + if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL || is_funcref) + { + partial_T *pt = ALLOC_CLEAR_ONE(partial_T); + + // result is a VAR_PARTIAL + if (pt == NULL) + vim_free(name); + else + { + if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0)) + { + listitem_T *li; + int i = 0; + int arg_len = 0; + int lv_len = 0; + + if (arg_pt != NULL) + arg_len = arg_pt->pt_argc; + if (list != NULL) + lv_len = list->lv_len; + pt->pt_argc = arg_len + lv_len; + pt->pt_argv = ALLOC_MULT(typval_T, pt->pt_argc); + if (pt->pt_argv == NULL) + { + vim_free(pt); + vim_free(name); + goto theend; + } + for (i = 0; i < arg_len; i++) + copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]); + if (lv_len > 0) + { + CHECK_LIST_MATERIALIZE(list); + FOR_ALL_LIST_ITEMS(list, li) + copy_tv(&li->li_tv, &pt->pt_argv[i++]); + } + } + + // For "function(dict.func, [], dict)" and "func" is a partial + // use "dict". That is backwards compatible. + if (dict_idx > 0) + { + // The dict is bound explicitly, pt_auto is FALSE. + pt->pt_dict = argvars[dict_idx].vval.v_dict; + ++pt->pt_dict->dv_refcount; + } + else if (arg_pt != NULL) + { + // If the dict was bound automatically the result is also + // bound automatically. + pt->pt_dict = arg_pt->pt_dict; + pt->pt_auto = arg_pt->pt_auto; + if (pt->pt_dict != NULL) + ++pt->pt_dict->dv_refcount; + pt->pt_obj = arg_pt->pt_obj; + if (pt->pt_obj != NULL) + ++pt->pt_obj->obj_refcount; + } + + pt->pt_refcount = 1; + if (arg_pt != NULL && arg_pt->pt_func != NULL) + { + pt->pt_func = arg_pt->pt_func; + func_ptr_ref(pt->pt_func); + vim_free(name); + } + else if (is_funcref) + { + pt->pt_func = find_func(trans_name, is_global); + func_ptr_ref(pt->pt_func); + vim_free(name); + } + else + { + pt->pt_name = name; + func_ref(name); + } + + if (arg_pt != NULL) + { + pt->pt_outer_partial = arg_pt; + ++arg_pt->pt_refcount; + } + } + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = pt; + } + else + { + // result is a VAR_FUNC + rettv->v_type = VAR_FUNC; + rettv->vval.v_string = name; + func_ref(name); + } + } +theend: + vim_free(trans_name); +} + +/* + * "funcref()" function + */ + static void +f_funcref(typval_T *argvars, typval_T *rettv) +{ + common_function(argvars, rettv, TRUE); +} + +/* + * "function()" function + */ + static void +f_function(typval_T *argvars, typval_T *rettv) +{ + common_function(argvars, rettv, FALSE); +} + +/* + * "garbagecollect()" function + */ + static void +f_garbagecollect(typval_T *argvars, typval_T *rettv UNUSED) +{ + if (in_vim9script() && check_for_opt_bool_arg(argvars, 0) == FAIL) + return; + + // This is postponed until we are back at the toplevel, because we may be + // using Lists and Dicts internally. E.g.: ":echo [garbagecollect()]". + want_garbage_collect = TRUE; + + if (argvars[0].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[0]) == 1) + garbage_collect_at_exit = TRUE; +} + +/* + * "get()" function + */ + static void +f_get(typval_T *argvars, typval_T *rettv) +{ + listitem_T *li; + list_T *l; + dictitem_T *di; + dict_T *d; + typval_T *tv = NULL; + int what_is_dict = FALSE; + + if (argvars[0].v_type == VAR_BLOB) + { + int error = FALSE; + int idx = tv_get_number_chk(&argvars[1], &error); + + if (!error) + { + rettv->v_type = VAR_NUMBER; + if (idx < 0) + idx = blob_len(argvars[0].vval.v_blob) + idx; + if (idx < 0 || idx >= blob_len(argvars[0].vval.v_blob)) + rettv->vval.v_number = -1; + else + { + rettv->vval.v_number = blob_get(argvars[0].vval.v_blob, idx); + tv = rettv; + } + } + } + else if (argvars[0].v_type == VAR_LIST) + { + if ((l = argvars[0].vval.v_list) != NULL) + { + int error = FALSE; + + li = list_find(l, (long)tv_get_number_chk(&argvars[1], &error)); + if (!error && li != NULL) + tv = &li->li_tv; + } + } + else if (argvars[0].v_type == VAR_DICT) + { + if ((d = argvars[0].vval.v_dict) != NULL) + { + di = dict_find(d, tv_get_string(&argvars[1]), -1); + if (di != NULL) + tv = &di->di_tv; + } + } + else if (argvars[0].v_type == VAR_PARTIAL || argvars[0].v_type == VAR_FUNC) + { + partial_T *pt; + partial_T fref_pt; + + if (argvars[0].v_type == VAR_PARTIAL) + pt = argvars[0].vval.v_partial; + else + { + CLEAR_FIELD(fref_pt); + fref_pt.pt_name = argvars[0].vval.v_string; + pt = &fref_pt; + } + + if (pt != NULL) + { + char_u *what = tv_get_string(&argvars[1]); + + if (STRCMP(what, "func") == 0 || STRCMP(what, "name") == 0) + { + char_u *name = partial_name(pt); + + rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING); + if (name == NULL) + rettv->vval.v_string = NULL; + else + { + if (rettv->v_type == VAR_FUNC) + func_ref(name); + if (*what == 'n' && pt->pt_name == NULL + && pt->pt_func != NULL) + // use <SNR> instead of the byte code + name = printable_func_name(pt->pt_func); + rettv->vval.v_string = vim_strsave(name); + } + } + else if (STRCMP(what, "dict") == 0) + { + what_is_dict = TRUE; + if (pt->pt_dict != NULL) + rettv_dict_set(rettv, pt->pt_dict); + } + else if (STRCMP(what, "args") == 0) + { + rettv->v_type = VAR_LIST; + if (rettv_list_alloc(rettv) == OK) + { + int i; + + for (i = 0; i < pt->pt_argc; ++i) + list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]); + } + } + else + semsg(_(e_invalid_argument_str), what); + + // When {what} == "dict" and pt->pt_dict == NULL, evaluate the + // third argument + if (!what_is_dict) + return; + } + } + else + semsg(_(e_argument_of_str_must_be_list_dictionary_or_blob), "get()"); + + if (tv == NULL) + { + if (argvars[2].v_type != VAR_UNKNOWN) + copy_tv(&argvars[2], rettv); + } + else + copy_tv(tv, rettv); +} + +/* + * "getchangelist()" function + */ + static void +f_getchangelist(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf; + int i; + list_T *l; + dict_T *d; + int changelistindex; + + if (rettv_list_alloc(rettv) == FAIL) + return; + + if (in_vim9script() && check_for_opt_buffer_arg(argvars, 0) == FAIL) + return; + + if (argvars[0].v_type == VAR_UNKNOWN) + buf = curbuf; + else + buf = tv_get_buf_from_arg(&argvars[0]); + if (buf == NULL) + return; + + l = list_alloc(); + if (l == NULL) + return; + if (list_append_list(rettv->vval.v_list, l) == FAIL) + { + vim_free(l); + return; + } + + /* + * The current window change list index tracks only the position for the + * current buffer. For other buffers use the stored index for the current + * window, or, if that's not available, the change list length. + */ + if (buf == curwin->w_buffer) + { + changelistindex = curwin->w_changelistidx; + } + else + { + wininfo_T *wip; + + FOR_ALL_BUF_WININFO(buf, wip) + if (wip->wi_win == curwin) + break; + changelistindex = wip != NULL ? wip->wi_changelistidx + : buf->b_changelistlen; + } + list_append_number(rettv->vval.v_list, (varnumber_T)changelistindex); + + for (i = 0; i < buf->b_changelistlen; ++i) + { + if (buf->b_changelist[i].lnum == 0) + continue; + if ((d = dict_alloc()) == NULL) + return; + if (list_append_dict(l, d) == FAIL) + return; + dict_add_number(d, "lnum", (long)buf->b_changelist[i].lnum); + dict_add_number(d, "col", (long)buf->b_changelist[i].col); + dict_add_number(d, "coladd", (long)buf->b_changelist[i].coladd); + } +} + + static void +getpos_both( + typval_T *argvars, + typval_T *rettv, + int getcurpos, + int charcol) +{ + pos_T *fp = NULL; + pos_T pos; + win_T *wp = curwin; + list_T *l; + int fnum = -1; + + if (rettv_list_alloc(rettv) == OK) + { + l = rettv->vval.v_list; + if (getcurpos) + { + if (argvars[0].v_type != VAR_UNKNOWN) + { + wp = find_win_by_nr_or_id(&argvars[0]); + if (wp != NULL) + fp = &wp->w_cursor; + } + else + fp = &curwin->w_cursor; + if (fp != NULL && charcol) + { + pos = *fp; + pos.col = + buf_byteidx_to_charidx(wp->w_buffer, pos.lnum, pos.col); + fp = &pos; + } + } + else + fp = var2fpos(&argvars[0], TRUE, &fnum, charcol); + if (fnum != -1) + list_append_number(l, (varnumber_T)fnum); + else + list_append_number(l, (varnumber_T)0); + list_append_number(l, (fp != NULL) ? (varnumber_T)fp->lnum + : (varnumber_T)0); + list_append_number(l, (fp != NULL) + ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) + : (varnumber_T)0); + list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd : + (varnumber_T)0); + if (getcurpos) + { + int save_set_curswant = curwin->w_set_curswant; + colnr_T save_curswant = curwin->w_curswant; + colnr_T save_virtcol = curwin->w_virtcol; + + if (wp == curwin) + update_curswant(); + list_append_number(l, wp == NULL ? 0 : wp->w_curswant == MAXCOL + ? (varnumber_T)MAXCOL : (varnumber_T)wp->w_curswant + 1); + + // Do not change "curswant", as it is unexpected that a get + // function has a side effect. + if (wp == curwin && save_set_curswant) + { + curwin->w_set_curswant = save_set_curswant; + curwin->w_curswant = save_curswant; + curwin->w_virtcol = save_virtcol; + curwin->w_valid &= ~VALID_VIRTCOL; + } + } + } + else + rettv->vval.v_number = FALSE; +} + +/* + * "getcharpos()" function + */ + static void +f_getcharpos(typval_T *argvars UNUSED, typval_T *rettv) +{ + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + getpos_both(argvars, rettv, FALSE, TRUE); +} + +/* + * "getcharsearch()" function + */ + static void +f_getcharsearch(typval_T *argvars UNUSED, typval_T *rettv) +{ + if (rettv_dict_alloc(rettv) != OK) + return; + + dict_T *dict = rettv->vval.v_dict; + + dict_add_string(dict, "char", last_csearch()); + dict_add_number(dict, "forward", last_csearch_forward()); + dict_add_number(dict, "until", last_csearch_until()); +} + +/* + * "getenv()" function + */ + static void +f_getenv(typval_T *argvars, typval_T *rettv) +{ + int mustfree = FALSE; + char_u *p; + + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + p = vim_getenv(tv_get_string(&argvars[0]), &mustfree); + if (p == NULL) + { + rettv->v_type = VAR_SPECIAL; + rettv->vval.v_number = VVAL_NULL; + return; + } + if (!mustfree) + p = vim_strsave(p); + rettv->vval.v_string = p; + rettv->v_type = VAR_STRING; +} + +/* + * "getfontname()" function + */ + static void +f_getfontname(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL) + return; + +#ifdef FEAT_GUI + if (gui.in_use) + { + GuiFont font; + char_u *name = NULL; + + if (argvars[0].v_type == VAR_UNKNOWN) + { + // Get the "Normal" font. Either the name saved by + // hl_set_font_name() or from the font ID. + font = gui.norm_font; + name = hl_get_font_name(); + } + else + { + name = tv_get_string(&argvars[0]); + if (STRCMP(name, "*") == 0) // don't use font dialog + return; + font = gui_mch_get_font(name, FALSE); + if (font == NOFONT) + return; // Invalid font name, return empty string. + } + rettv->vval.v_string = gui_mch_get_fontname(font, name); + if (argvars[0].v_type != VAR_UNKNOWN) + gui_mch_free_font(font); + } +#endif +} + +/* + * "getjumplist()" function + */ + static void +f_getjumplist(typval_T *argvars, typval_T *rettv) +{ + win_T *wp; + int i; + list_T *l; + dict_T *d; + + if (rettv_list_alloc(rettv) == FAIL) + return; + + if (in_vim9script() + && (check_for_opt_number_arg(argvars, 0) == FAIL + || (argvars[0].v_type != VAR_UNKNOWN + && check_for_opt_number_arg(argvars, 1) == FAIL))) + return; + + wp = find_tabwin(&argvars[0], &argvars[1], NULL); + if (wp == NULL) + return; + + cleanup_jumplist(wp, TRUE); + + l = list_alloc(); + if (l == NULL) + return; + if (list_append_list(rettv->vval.v_list, l) == FAIL) + { + vim_free(l); + return; + } + + list_append_number(rettv->vval.v_list, (varnumber_T)wp->w_jumplistidx); + + for (i = 0; i < wp->w_jumplistlen; ++i) + { + if (wp->w_jumplist[i].fmark.mark.lnum == 0) + continue; + if ((d = dict_alloc()) == NULL) + return; + if (list_append_dict(l, d) == FAIL) + return; + dict_add_number(d, "lnum", (long)wp->w_jumplist[i].fmark.mark.lnum); + dict_add_number(d, "col", (long)wp->w_jumplist[i].fmark.mark.col); + dict_add_number(d, "coladd", (long)wp->w_jumplist[i].fmark.mark.coladd); + dict_add_number(d, "bufnr", (long)wp->w_jumplist[i].fmark.fnum); + if (wp->w_jumplist[i].fname != NULL) + dict_add_string(d, "filename", wp->w_jumplist[i].fname); + } +} + +/* + * "getpid()" function + */ + static void +f_getpid(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = mch_get_pid(); +} + +/* + * "getcurpos()" function + */ + static void +f_getcurpos(typval_T *argvars, typval_T *rettv) +{ + if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL) + return; + + getpos_both(argvars, rettv, TRUE, FALSE); +} + + static void +f_getcursorcharpos(typval_T *argvars, typval_T *rettv) +{ + if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL) + return; + + getpos_both(argvars, rettv, TRUE, TRUE); +} + +/* + * "getpos(string)" function + */ + static void +f_getpos(typval_T *argvars, typval_T *rettv) +{ + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + getpos_both(argvars, rettv, FALSE, FALSE); +} + +/* + * Common between getreg(), getreginfo() and getregtype(): get the register + * name from the first argument. + * Returns zero on error. + */ + static int +getreg_get_regname(typval_T *argvars) +{ + char_u *strregname; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + strregname = tv_get_string_chk(&argvars[0]); + if (strregname != NULL && in_vim9script() && STRLEN(strregname) > 1) + { + semsg(_(e_register_name_must_be_one_char_str), strregname); + strregname = NULL; + } + if (strregname == NULL) // type error; errmsg already given + return 0; + } + else + // Default to v:register + strregname = get_vim_var_str(VV_REG); + + return *strregname == 0 ? '"' : *strregname; +} + +/* + * "getreg()" function + */ + static void +f_getreg(typval_T *argvars, typval_T *rettv) +{ + int regname; + int arg2 = FALSE; + int return_list = FALSE; + + if (in_vim9script() + && (check_for_opt_string_arg(argvars, 0) == FAIL + || (argvars[0].v_type != VAR_UNKNOWN + && (check_for_opt_bool_arg(argvars, 1) == FAIL + || (argvars[1].v_type != VAR_UNKNOWN + && check_for_opt_bool_arg(argvars, 2) == FAIL))))) + return; + + regname = getreg_get_regname(argvars); + if (regname == 0) + return; + + if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_UNKNOWN) + { + int error = FALSE; + + arg2 = (int)tv_get_bool_chk(&argvars[1], &error); + + if (!error && argvars[2].v_type != VAR_UNKNOWN) + return_list = (int)tv_get_bool_chk(&argvars[2], &error); + if (error) + return; + } + + if (return_list) + { + rettv->v_type = VAR_LIST; + rettv->vval.v_list = (list_T *)get_reg_contents(regname, + (arg2 ? GREG_EXPR_SRC : 0) | GREG_LIST); + if (rettv->vval.v_list == NULL) + (void)rettv_list_alloc(rettv); + else + ++rettv->vval.v_list->lv_refcount; + } + else + { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = get_reg_contents(regname, + arg2 ? GREG_EXPR_SRC : 0); + } +} + +/* + * "getregtype()" function + */ + static void +f_getregtype(typval_T *argvars, typval_T *rettv) +{ + int regname; + char_u buf[NUMBUFLEN + 2]; + long reglen = 0; + + // on error return an empty string + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL) + return; + + regname = getreg_get_regname(argvars); + if (regname == 0) + return; + + buf[0] = NUL; + buf[1] = NUL; + switch (get_reg_type(regname, ®len)) + { + case MLINE: buf[0] = 'V'; break; + case MCHAR: buf[0] = 'v'; break; + case MBLOCK: + buf[0] = Ctrl_V; + sprintf((char *)buf + 1, "%ld", reglen + 1); + break; + } + rettv->vval.v_string = vim_strsave(buf); +} + +/* + * "gettagstack()" function + */ + static void +f_gettagstack(typval_T *argvars, typval_T *rettv) +{ + win_T *wp = curwin; // default is current window + + if (rettv_dict_alloc(rettv) == FAIL) + return; + + if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL) + return; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) + return; + } + + get_tagstack(wp, rettv->vval.v_dict); +} + +/* + * "gettext()" function + */ + static void +f_gettext(typval_T *argvars, typval_T *rettv) +{ + if (check_for_nonempty_string_arg(argvars, 0) == FAIL) + return; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave((char_u *)_(argvars[0].vval.v_string)); +} + +// for VIM_VERSION_ defines +#include "version.h" + +/* + * "has()" function + */ + void +f_has(typval_T *argvars, typval_T *rettv) +{ + int i; + char_u *name; + int x = FALSE; + int n = FALSE; + typedef struct { + char *name; + short present; + } has_item_T; + static has_item_T has_list[] = + { + {"amiga", +#ifdef AMIGA + 1 +#else + 0 +#endif + }, + {"arp", +#if defined(AMIGA) && defined(FEAT_ARP) + 1 +#else + 0 +#endif + }, + {"haiku", +#ifdef __HAIKU__ + 1 +#else + 0 +#endif + }, + {"bsd", +#if defined(BSD) && !defined(MACOS_X) + 1 +#else + 0 +#endif + }, + {"hpux", +#ifdef hpux + 1 +#else + 0 +#endif + }, + {"linux", +#ifdef __linux__ + 1 +#else + 0 +#endif + }, + {"mac", // Mac OS X (and, once, Mac OS Classic) +#ifdef MACOS_X + 1 +#else + 0 +#endif + }, + {"osx", // Mac OS X +#ifdef MACOS_X + 1 +#else + 0 +#endif + }, + {"macunix", // Mac OS X, with the darwin feature +#if defined(MACOS_X) && defined(MACOS_X_DARWIN) + 1 +#else + 0 +#endif + }, + {"osxdarwin", // synonym for macunix +#if defined(MACOS_X) && defined(MACOS_X_DARWIN) + 1 +#else + 0 +#endif + }, + {"qnx", +#ifdef __QNX__ + 1 +#else + 0 +#endif + }, + {"sun", +#ifdef SUN_SYSTEM + 1 +#else + 0 +#endif + }, + {"unix", +#ifdef UNIX + 1 +#else + 0 +#endif + }, + {"vms", +#ifdef VMS + 1 +#else + 0 +#endif + }, + {"win32", +#ifdef MSWIN + 1 +#else + 0 +#endif + }, + {"win32unix", +#ifdef WIN32UNIX + 1 +#else + 0 +#endif + }, + {"win64", +#ifdef _WIN64 + 1 +#else + 0 +#endif + }, + {"ebcdic", 0 }, + {"fname_case", +#ifndef CASE_INSENSITIVE_FILENAME + 1 +#else + 0 +#endif + }, + {"acl", +#ifdef HAVE_ACL + 1 +#else + 0 +#endif + }, + {"arabic", +#ifdef FEAT_ARABIC + 1 +#else + 0 +#endif + }, + {"autocmd", 1}, + {"autochdir", +#ifdef FEAT_AUTOCHDIR + 1 +#else + 0 +#endif + }, + {"autoservername", +#ifdef FEAT_AUTOSERVERNAME + 1 +#else + 0 +#endif + }, + {"balloon_eval", +#ifdef FEAT_BEVAL_GUI + 1 +#else + 0 +#endif + }, + {"balloon_multiline", +#ifdef FEAT_BEVAL_GUI + 1 +#else + 0 +#endif + }, + {"balloon_eval_term", +#ifdef FEAT_BEVAL_TERM + 1 +#else + 0 +#endif + }, + {"builtin_terms", 1}, + {"all_builtin_terms", 1}, + {"browsefilter", +#if defined(FEAT_BROWSE) && (defined(USE_FILE_CHOOSER) \ + || defined(FEAT_GUI_MSWIN) \ + || defined(FEAT_GUI_MOTIF)) + 1 +#else + 0 +#endif + }, + {"byte_offset", +#ifdef FEAT_BYTEOFF + 1 +#else + 0 +#endif + }, + {"channel", +#ifdef FEAT_JOB_CHANNEL + 1 +#else + 0 +#endif + }, + {"cindent", 1}, + {"clientserver", +#ifdef FEAT_CLIENTSERVER + 1 +#else + 0 +#endif + }, + {"clipboard", +#ifdef FEAT_CLIPBOARD + 1 +#else + 0 +#endif + }, + {"cmdline_compl", 1}, + {"cmdline_hist", 1}, + {"cmdwin", 1}, + {"comments", 1}, + {"conceal", +#ifdef FEAT_CONCEAL + 1 +#else + 0 +#endif + }, + {"cryptv", +#ifdef FEAT_CRYPT + 1 +#else + 0 +#endif + }, + {"crypt-blowfish", +#ifdef FEAT_CRYPT + 1 +#else + 0 +#endif + }, + {"crypt-blowfish2", +#ifdef FEAT_CRYPT + 1 +#else + 0 +#endif + }, + {"cscope", +#ifdef FEAT_CSCOPE + 1 +#else + 0 +#endif + }, + {"cursorbind", 1}, + {"cursorshape", +#ifdef CURSOR_SHAPE + 1 +#else + 0 +#endif + }, + {"debug", +#ifdef DEBUG + 1 +#else + 0 +#endif + }, + {"dialog_con", +#ifdef FEAT_CON_DIALOG + 1 +#else + 0 +#endif + }, + {"dialog_gui", +#ifdef FEAT_GUI_DIALOG + 1 +#else + 0 +#endif + }, + {"diff", +#ifdef FEAT_DIFF + 1 +#else + 0 +#endif + }, + {"digraphs", +#ifdef FEAT_DIGRAPHS + 1 +#else + 0 +#endif + }, + {"directx", +#ifdef FEAT_DIRECTX + 1 +#else + 0 +#endif + }, + {"dnd", +#ifdef FEAT_DND + 1 +#else + 0 +#endif + }, + {"drop_file", +#ifdef HAVE_DROP_FILE + 1 +#else + 0 +#endif + }, + {"emacs_tags", +#ifdef FEAT_EMACS_TAGS + 1 +#else + 0 +#endif + }, + {"eval", 1}, // always present, of course! + {"ex_extra", 1}, // graduated feature + {"extra_search", +#ifdef FEAT_SEARCH_EXTRA + 1 +#else + 0 +#endif + }, + {"file_in_path", 1}, + {"filterpipe", +#if defined(FEAT_FILTERPIPE) && !defined(VIMDLL) + 1 +#else + 0 +#endif + }, + {"find_in_path", +#ifdef FEAT_FIND_ID + 1 +#else + 0 +#endif + }, + {"float", 1}, + {"folding", +#ifdef FEAT_FOLDING + 1 +#else + 0 +#endif + }, + {"footer", 0}, + {"fork", +#if !defined(USE_SYSTEM) && defined(UNIX) + 1 +#else + 0 +#endif + }, + {"gettext", +#ifdef FEAT_GETTEXT + 1 +#else + 0 +#endif + }, + {"gui", +#ifdef FEAT_GUI + 1 +#else + 0 +#endif + }, + {"gui_neXtaw", 0 }, + {"gui_athena", 0 }, + {"gui_gtk", +#ifdef FEAT_GUI_GTK + 1 +#else + 0 +#endif + }, + {"gui_gtk2", +#if defined(FEAT_GUI_GTK) && !defined(USE_GTK3) + 1 +#else + 0 +#endif + }, + {"gui_gtk3", +#if defined(FEAT_GUI_GTK) && defined(USE_GTK3) + 1 +#else + 0 +#endif + }, + {"gui_gnome", +#ifdef FEAT_GUI_GNOME + 1 +#else + 0 +#endif + }, + {"gui_haiku", +#ifdef FEAT_GUI_HAIKU + 1 +#else + 0 +#endif + }, + {"gui_mac", 0}, + {"gui_motif", +#ifdef FEAT_GUI_MOTIF + 1 +#else + 0 +#endif + }, + {"gui_photon", +#ifdef FEAT_GUI_PHOTON + 1 +#else + 0 +#endif + }, + {"gui_win32", +#ifdef FEAT_GUI_MSWIN + 1 +#else + 0 +#endif + }, + {"iconv", +#if defined(HAVE_ICONV_H) && defined(USE_ICONV) + 1 +#else + 0 +#endif + }, + {"insert_expand", 1}, + {"ipv6", +#ifdef FEAT_IPV6 + 1 +#else + 0 +#endif + }, + {"job", +#ifdef FEAT_JOB_CHANNEL + 1 +#else + 0 +#endif + }, + {"jumplist", 1}, + {"keymap", +#ifdef FEAT_KEYMAP + 1 +#else + 0 +#endif + }, + {"lambda", 1}, // always with FEAT_EVAL, since 7.4.2120 with closure + {"langmap", +#ifdef FEAT_LANGMAP + 1 +#else + 0 +#endif + }, + {"libcall", +#ifdef FEAT_LIBCALL + 1 +#else + 0 +#endif + }, + {"linebreak", +#ifdef FEAT_LINEBREAK + 1 +#else + 0 +#endif + }, + {"lispindent", 1}, + {"listcmds", 1}, + {"localmap", 1}, + {"lua", +#if defined(FEAT_LUA) && !defined(DYNAMIC_LUA) + 1 +#else + 0 +#endif + }, + {"menu", +#ifdef FEAT_MENU + 1 +#else + 0 +#endif + }, + {"mksession", +#ifdef FEAT_SESSION + 1 +#else + 0 +#endif + }, + {"modify_fname", 1}, + {"mouse", 1}, + {"mouseshape", +#ifdef FEAT_MOUSESHAPE + 1 +#else + 0 +#endif + }, + {"mouse_dec", +#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_DEC) + 1 +#else + 0 +#endif + }, + {"mouse_gpm", +#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_GPM) && !defined(DYNAMIC_GPM) + 1 +#else + 0 +#endif + }, + {"mouse_jsbterm", +#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_JSB) + 1 +#else + 0 +#endif + }, + {"mouse_netterm", +#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_NET) + 1 +#else + 0 +#endif + }, + {"mouse_pterm", +#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_PTERM) + 1 +#else + 0 +#endif + }, + {"mouse_sgr", +#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_XTERM) + 1 +#else + 0 +#endif + }, + {"mouse_sysmouse", +#if (defined(UNIX) || defined(VMS)) && defined(FEAT_SYSMOUSE) + 1 +#else + 0 +#endif + }, + {"mouse_urxvt", +#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_URXVT) + 1 +#else + 0 +#endif + }, + {"mouse_xterm", +#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_XTERM) + 1 +#else + 0 +#endif + }, + {"multi_byte", 1}, + {"multi_byte_ime", +#ifdef FEAT_MBYTE_IME + 1 +#else + 0 +#endif + }, + {"multi_lang", +#ifdef FEAT_MULTI_LANG + 1 +#else + 0 +#endif + }, + {"mzscheme", +#if defined(FEAT_MZSCHEME) && !defined(DYNAMIC_MZSCHEME) + 1 +#else + 0 +#endif + }, + {"nanotime", +#ifdef ST_MTIM_NSEC + 1 +#else + 0 +#endif + }, + {"num64", 1}, + {"ole", +#ifdef FEAT_OLE + 1 +#else + 0 +#endif + }, + {"packages", +#ifdef FEAT_EVAL + 1 +#else + 0 +#endif + }, + {"path_extra", 1}, + {"perl", +#if defined(FEAT_PERL) && !defined(DYNAMIC_PERL) + 1 +#else + 0 +#endif + }, + {"persistent_undo", +#ifdef FEAT_PERSISTENT_UNDO + 1 +#else + 0 +#endif + }, + {"python_compiled", +#if defined(FEAT_PYTHON) + 1 +#else + 0 +#endif + }, + {"python_dynamic", +#if defined(FEAT_PYTHON) && defined(DYNAMIC_PYTHON) + 1 +#else + 0 +#endif + }, + {"python", +#if defined(FEAT_PYTHON) && !defined(DYNAMIC_PYTHON) + 1 +#else + 0 +#endif + }, + {"pythonx", +#if (defined(FEAT_PYTHON) && !defined(DYNAMIC_PYTHON)) \ + || (defined(FEAT_PYTHON3) && !defined(DYNAMIC_PYTHON3)) + 1 +#else + 0 +#endif + }, + {"python3_compiled", +#if defined(FEAT_PYTHON3) + 1 +#else + 0 +#endif + }, + {"python3_dynamic", +#if defined(FEAT_PYTHON3) && defined(DYNAMIC_PYTHON3) + 1 +#else + 0 +#endif + }, + {"python3_stable", +#if defined(FEAT_PYTHON3) && defined(DYNAMIC_PYTHON3_STABLE_ABI) + 1 +#else + 0 +#endif + }, + {"python3", +#if defined(FEAT_PYTHON3) && !defined(DYNAMIC_PYTHON3) + 1 +#else + 0 +#endif + }, + {"popupwin", +#ifdef FEAT_PROP_POPUP + 1 +#else + 0 +#endif + }, + {"postscript", +#ifdef FEAT_POSTSCRIPT + 1 +#else + 0 +#endif + }, + {"printer", +#ifdef FEAT_PRINTER + 1 +#else + 0 +#endif + }, + {"profile", +#ifdef FEAT_PROFILE + 1 +#else + 0 +#endif + }, + {"prof_nsec", +#ifdef PROF_NSEC + 1 +#else + 0 +#endif + }, + {"reltime", +#ifdef FEAT_RELTIME + 1 +#else + 0 +#endif + }, + {"quickfix", +#ifdef FEAT_QUICKFIX + 1 +#else + 0 +#endif + }, + {"rightleft", +#ifdef FEAT_RIGHTLEFT + 1 +#else + 0 +#endif + }, + {"ruby", +#if defined(FEAT_RUBY) && !defined(DYNAMIC_RUBY) + 1 +#else + 0 +#endif + }, + {"scrollbind", 1}, + {"showcmd", 1}, + {"cmdline_info", 1}, + {"signs", +#ifdef FEAT_SIGNS + 1 +#else + 0 +#endif + }, + {"smartindent", 1}, + {"startuptime", +#ifdef STARTUPTIME + 1 +#else + 0 +#endif + }, + {"statusline", +#ifdef FEAT_STL_OPT + 1 +#else + 0 +#endif + }, + {"netbeans_intg", +#ifdef FEAT_NETBEANS_INTG + 1 +#else + 0 +#endif + }, + {"sodium", +#if defined(FEAT_SODIUM) && !defined(DYNAMIC_SODIUM) + 1 +#else + 0 +#endif + }, + {"sound", +#ifdef FEAT_SOUND + 1 +#else + 0 +#endif + }, + {"spell", +#ifdef FEAT_SPELL + 1 +#else + 0 +#endif + }, + {"syntax", +#ifdef FEAT_SYN_HL + 1 +#else + 0 +#endif + }, + {"system", +#if defined(USE_SYSTEM) || !defined(UNIX) + 1 +#else + 0 +#endif + }, + {"tag_binary", 1}, // graduated feature + {"tcl", +#if defined(FEAT_TCL) && !defined(DYNAMIC_TCL) + 1 +#else + 0 +#endif + }, + {"termguicolors", +#ifdef FEAT_TERMGUICOLORS + 1 +#else + 0 +#endif + }, + {"terminal", +#if defined(FEAT_TERMINAL) && !defined(MSWIN) + 1 +#else + 0 +#endif + }, + {"terminfo", +#ifdef TERMINFO + 1 +#else + 0 +#endif + }, + {"termresponse", +#ifdef FEAT_TERMRESPONSE + 1 +#else + 0 +#endif + }, + {"textobjects", 1}, + {"textprop", +#ifdef FEAT_PROP_POPUP + 1 +#else + 0 +#endif + }, + {"tgetent", +#ifdef HAVE_TGETENT + 1 +#else + 0 +#endif + }, + {"timers", +#ifdef FEAT_TIMERS + 1 +#else + 0 +#endif + }, + {"title", 1}, + {"toolbar", +#ifdef FEAT_TOOLBAR + 1 +#else + 0 +#endif + }, + {"unnamedplus", +#if defined(FEAT_CLIPBOARD) && defined(FEAT_X11) + 1 +#else + 0 +#endif + }, + {"user-commands", 1}, // was accidentally included in 5.4 + {"user_commands", 1}, + {"vartabs", +#ifdef FEAT_VARTABS + 1 +#else + 0 +#endif + }, + {"vertsplit", 1}, + {"viminfo", +#ifdef FEAT_VIMINFO + 1 +#else + 0 +#endif + }, + {"vim9script", 1}, + {"vimscript-1", 1}, + {"vimscript-2", 1}, + {"vimscript-3", 1}, + {"vimscript-4", 1}, + {"virtualedit", 1}, + {"visual", 1}, + {"visualextra", 1}, + {"vreplace", 1}, + {"vtp", +#ifdef FEAT_VTP + 1 +#else + 0 +#endif + }, + {"wildignore", 1}, + {"wildmenu", 1}, + {"windows", 1}, + {"winaltkeys", +#ifdef FEAT_WAK + 1 +#else + 0 +#endif + }, + {"writebackup", +#ifdef FEAT_WRITEBACKUP + 1 +#else + 0 +#endif + }, + {"xattr", +#ifdef FEAT_XATTR + 1 +#else + 0 +#endif + }, + {"xim", +#ifdef FEAT_XIM + 1 +#else + 0 +#endif + }, + {"xfontset", +#ifdef FEAT_XFONTSET + 1 +#else + 0 +#endif + }, + {"xpm", +#if defined(FEAT_XPM_W32) || defined(HAVE_XPM) + 1 +#else + 0 +#endif + }, + {"xpm_w32", // for backward compatibility +#ifdef FEAT_XPM_W32 + 1 +#else + 0 +#endif + }, + {"xsmp", +#ifdef USE_XSMP + 1 +#else + 0 +#endif + }, + {"xsmp_interact", +#ifdef USE_XSMP_INTERACT + 1 +#else + 0 +#endif + }, + {"xterm_clipboard", +#ifdef FEAT_XCLIPBOARD + 1 +#else + 0 +#endif + }, + {"xterm_save", +#ifdef FEAT_XTERM_SAVE + 1 +#else + 0 +#endif + }, + {"X11", +#if defined(UNIX) && defined(FEAT_X11) + 1 +#else + 0 +#endif + }, + {":tearoff", +// same #ifdef as used for ex_tearoff(). +#if defined(FEAT_GUI_MSWIN) && defined(FEAT_MENU) && defined(FEAT_TEAROFF) + 1 +#else + 0 +#endif + }, + {NULL, 0} + }; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_opt_bool_arg(argvars, 1) == FAIL)) + return; + + name = tv_get_string(&argvars[0]); + for (i = 0; has_list[i].name != NULL; ++i) + if (STRICMP(name, has_list[i].name) == 0) + { + x = TRUE; + n = has_list[i].present; + break; + } + + // features also in has_list[] but sometimes enabled at runtime + if (x == TRUE && n == FALSE) + { + if (0) + { + // intentionally empty + } +#ifdef VIMDLL + else if (STRICMP(name, "filterpipe") == 0) + n = gui.in_use || gui.starting; +#endif +#if defined(USE_ICONV) && defined(DYNAMIC_ICONV) + else if (STRICMP(name, "iconv") == 0) + n = iconv_enabled(FALSE); +#endif +#ifdef DYNAMIC_LUA + else if (STRICMP(name, "lua") == 0) + n = lua_enabled(FALSE); +#endif +#ifdef DYNAMIC_MZSCHEME + else if (STRICMP(name, "mzscheme") == 0) + n = mzscheme_enabled(FALSE); +#endif +#ifdef DYNAMIC_PERL + else if (STRICMP(name, "perl") == 0) + n = perl_enabled(FALSE); +#endif +#ifdef DYNAMIC_PYTHON + else if (STRICMP(name, "python") == 0) + n = python_enabled(FALSE); +#endif +#ifdef DYNAMIC_PYTHON3 + else if (STRICMP(name, "python3") == 0) + n = python3_enabled(FALSE); +#endif +#if defined(DYNAMIC_PYTHON) || defined(DYNAMIC_PYTHON3) + else if (STRICMP(name, "pythonx") == 0) + { +# if defined(DYNAMIC_PYTHON) && defined(DYNAMIC_PYTHON3) + if (p_pyx == 0) + n = python3_enabled(FALSE) || python_enabled(FALSE); + else if (p_pyx == 3) + n = python3_enabled(FALSE); + else if (p_pyx == 2) + n = python_enabled(FALSE); +# elif defined(DYNAMIC_PYTHON) + n = python_enabled(FALSE); +# elif defined(DYNAMIC_PYTHON3) + n = python3_enabled(FALSE); +# endif + } +#endif +#ifdef DYNAMIC_RUBY + else if (STRICMP(name, "ruby") == 0) + n = ruby_enabled(FALSE); +#endif +#ifdef DYNAMIC_TCL + else if (STRICMP(name, "tcl") == 0) + n = tcl_enabled(FALSE); +#endif +#ifdef DYNAMIC_SODIUM + else if (STRICMP(name, "sodium") == 0) + n = sodium_enabled(FALSE); +#endif +#if defined(FEAT_TERMINAL) && defined(MSWIN) + else if (STRICMP(name, "terminal") == 0) + n = terminal_enabled(); +#endif +#ifdef DYNAMIC_GPM + else if (STRICMP(name, "mouse_gpm") == 0) + n = gpm_available(); +#endif + } + + // features not in has_list[] + if (x == FALSE) + { + if (STRNICMP(name, "patch", 5) == 0) + { + x = TRUE; + if (name[5] == '-' + && STRLEN(name) >= 11 + && (name[6] >= '1' && name[6] <= '9')) + { + char *end; + int major, minor; + + // This works for patch-8.1.2, patch-9.0.3, patch-10.0.4, etc. + // Not for patch-9.10.5. + major = (int)strtoul((char *)name + 6, &end, 10); + if (*end == '.' && vim_isdigit(end[1]) + && end[2] == '.' && vim_isdigit(end[3])) + { + minor = atoi(end + 1); + + // Expect "patch-9.9.01234". + n = (major < VIM_VERSION_MAJOR + || (major == VIM_VERSION_MAJOR + && (minor < VIM_VERSION_MINOR + || (minor == VIM_VERSION_MINOR + && has_patch(atoi(end + 3)))))); + } + } + else if (SAFE_isdigit(name[5])) + n = has_patch(atoi((char *)name + 5)); + } + else if (STRICMP(name, "vim_starting") == 0) + { + x = TRUE; + n = (starting != 0); + } + else if (STRICMP(name, "ttyin") == 0) + { + x = TRUE; + n = mch_input_isatty(); + } + else if (STRICMP(name, "ttyout") == 0) + { + x = TRUE; + n = stdout_isatty; + } + else if (STRICMP(name, "multi_byte_encoding") == 0) + { + x = TRUE; + n = has_mbyte; + } + else if (STRICMP(name, "gui_running") == 0) + { + x = TRUE; +#ifdef FEAT_GUI + n = (gui.in_use || gui.starting); +#endif + } + else if (STRICMP(name, "browse") == 0) + { + x = TRUE; +#if defined(FEAT_GUI) && defined(FEAT_BROWSE) + n = gui.in_use; // gui_mch_browse() works when GUI is running +#endif + } + else if (STRICMP(name, "syntax_items") == 0) + { + x = TRUE; +#ifdef FEAT_SYN_HL + n = syntax_present(curwin); +#endif + } + else if (STRICMP(name, "vcon") == 0) + { + x = TRUE; +#ifdef FEAT_VTP + n = is_term_win32() && has_vtp_working(); +#endif + } + else if (STRICMP(name, "netbeans_enabled") == 0) + { + x = TRUE; +#ifdef FEAT_NETBEANS_INTG + n = netbeans_active(); +#endif + } + else if (STRICMP(name, "mouse_gpm_enabled") == 0) + { + x = TRUE; +#ifdef FEAT_MOUSE_GPM + n = gpm_enabled(); +#endif + } + else if (STRICMP(name, "conpty") == 0) + { + x = TRUE; +#if defined(FEAT_TERMINAL) && defined(MSWIN) + n = use_conpty(); +#endif + } + else if (STRICMP(name, "clipboard_working") == 0) + { + x = TRUE; +#ifdef FEAT_CLIPBOARD + n = clip_star.available; +#endif + } + } + + if (argvars[1].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[1])) + // return whether feature could ever be enabled + rettv->vval.v_number = x; + else + // return whether feature is enabled + rettv->vval.v_number = n; +} + +/* + * Return TRUE if "feature" can change later. + * Also when checking for the feature has side effects, such as loading a DLL. + */ + int +dynamic_feature(char_u *feature) +{ + return (feature == NULL +#if defined(FEAT_GUI) && defined(FEAT_BROWSE) + || (STRICMP(feature, "browse") == 0 && !gui.in_use) +#endif +#ifdef VIMDLL + || STRICMP(feature, "filterpipe") == 0 +#endif +#if defined(FEAT_GUI) && !defined(ALWAYS_USE_GUI) && !defined(VIMDLL) + // this can only change on Unix where the ":gui" command could be + // used. + || (STRICMP(feature, "gui_running") == 0 && !gui.in_use) +#endif +#if defined(USE_ICONV) && defined(DYNAMIC_ICONV) + || STRICMP(feature, "iconv") == 0 +#endif +#ifdef DYNAMIC_LUA + || STRICMP(feature, "lua") == 0 +#endif +#ifdef FEAT_MOUSE_GPM + || (STRICMP(feature, "mouse_gpm_enabled") == 0 && !gpm_enabled()) +#endif +#ifdef DYNAMIC_MZSCHEME + || STRICMP(feature, "mzscheme") == 0 +#endif +#ifdef FEAT_NETBEANS_INTG + || STRICMP(feature, "netbeans_enabled") == 0 +#endif +#ifdef DYNAMIC_PERL + || STRICMP(feature, "perl") == 0 +#endif +#ifdef DYNAMIC_PYTHON + || STRICMP(feature, "python") == 0 +#endif +#ifdef DYNAMIC_PYTHON3 + || STRICMP(feature, "python3") == 0 +#endif +#if defined(DYNAMIC_PYTHON) || defined(DYNAMIC_PYTHON3) + || STRICMP(feature, "pythonx") == 0 +#endif +#ifdef DYNAMIC_RUBY + || STRICMP(feature, "ruby") == 0 +#endif +#ifdef FEAT_SYN_HL + || STRICMP(feature, "syntax_items") == 0 +#endif +#ifdef DYNAMIC_TCL + || STRICMP(feature, "tcl") == 0 +#endif + // once "starting" is zero it will stay that way + || (STRICMP(feature, "vim_starting") == 0 && starting != 0) + || STRICMP(feature, "multi_byte_encoding") == 0 +#if defined(FEAT_TERMINAL) && defined(MSWIN) + || STRICMP(feature, "conpty") == 0 +#endif + ); +} + +/* + * "haslocaldir()" function + */ + static void +f_haslocaldir(typval_T *argvars, typval_T *rettv) +{ + tabpage_T *tp = NULL; + win_T *wp = NULL; + + if (in_vim9script() + && (check_for_opt_number_arg(argvars, 0) == FAIL + || (argvars[0].v_type != VAR_UNKNOWN + && check_for_opt_number_arg(argvars, 1) == FAIL))) + return; + + wp = find_tabwin(&argvars[0], &argvars[1], &tp); + + // Check for window-local and tab-local directories + if (wp != NULL && wp->w_localdir != NULL) + rettv->vval.v_number = 1; + else if (tp != NULL && tp->tp_localdir != NULL) + rettv->vval.v_number = 2; + else + rettv->vval.v_number = 0; +} + +/* + * "highlightID(name)" function + */ + static void +f_hlID(typval_T *argvars, typval_T *rettv) +{ + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + rettv->vval.v_number = syn_name2id(tv_get_string(&argvars[0])); +} + +/* + * "highlight_exists()" function + */ + static void +f_hlexists(typval_T *argvars, typval_T *rettv) +{ + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + rettv->vval.v_number = highlight_exists(tv_get_string(&argvars[0])); +} + +/* + * "hostname()" function + */ + static void +f_hostname(typval_T *argvars UNUSED, typval_T *rettv) +{ + char_u hostname[256]; + + mch_get_host_name(hostname, 256); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave(hostname); +} + +/* + * "index()" function + */ + static void +f_index(typval_T *argvars, typval_T *rettv) +{ + list_T *l; + listitem_T *item; + blob_T *b; + long idx = 0; + int ic = FALSE; + int error = FALSE; + + rettv->vval.v_number = -1; + + if (in_vim9script() + && (check_for_list_or_blob_arg(argvars, 0) == FAIL + || (argvars[0].v_type == VAR_BLOB + && check_for_number_arg(argvars, 1) == FAIL) + || check_for_opt_number_arg(argvars, 2) == FAIL + || (argvars[2].v_type != VAR_UNKNOWN + && check_for_opt_bool_arg(argvars, 3) == FAIL))) + return; + + if (argvars[0].v_type == VAR_BLOB) + { + typval_T tv; + int start = 0; + + if (argvars[2].v_type != VAR_UNKNOWN) + { + start = tv_get_number_chk(&argvars[2], &error); + if (error) + return; + } + b = argvars[0].vval.v_blob; + if (b == NULL) + return; + if (start < 0) + { + start = blob_len(b) + start; + if (start < 0) + start = 0; + } + + for (idx = start; idx < blob_len(b); ++idx) + { + tv.v_type = VAR_NUMBER; + tv.vval.v_number = blob_get(b, idx); + if (tv_equal(&tv, &argvars[1], ic, FALSE)) + { + rettv->vval.v_number = idx; + return; + } + } + return; + } + else if (argvars[0].v_type != VAR_LIST) + { + emsg(_(e_list_or_blob_required)); + return; + } + + l = argvars[0].vval.v_list; + if (l == NULL) + return; + + CHECK_LIST_MATERIALIZE(l); + item = l->lv_first; + if (argvars[2].v_type != VAR_UNKNOWN) + { + // Start at specified item. Use the cached index that list_find() + // sets, so that a negative number also works. + item = list_find(l, (long)tv_get_number_chk(&argvars[2], &error)); + idx = l->lv_u.mat.lv_idx; + if (argvars[3].v_type != VAR_UNKNOWN) + ic = (int)tv_get_bool_chk(&argvars[3], &error); + if (error) + item = NULL; + } + + for ( ; item != NULL; item = item->li_next, ++idx) + if (tv_equal(&item->li_tv, &argvars[1], ic, FALSE)) + { + rettv->vval.v_number = idx; + break; + } +} + +/* + * 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 int +indexof_eval_expr(typval_T *expr) +{ + typval_T argv[3]; + typval_T newtv; + varnumber_T found; + int error = FALSE; + + argv[0] = *get_vim_var_tv(VV_KEY); + argv[1] = *get_vim_var_tv(VV_VAL); + newtv.v_type = VAR_UNKNOWN; + + if (eval_expr_typval(expr, FALSE, argv, 2, NULL, &newtv) == FAIL) + return FALSE; + + found = tv_get_bool_chk(&newtv, &error); + clear_tv(&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 int +indexof_blob(blob_T *b, long startidx, typval_T *expr) +{ + long idx = 0; + + if (b == NULL) + return -1; + + if (startidx < 0) + { + // negative index: index from the last byte + startidx = blob_len(b) + startidx; + if (startidx < 0) + startidx = 0; + } + + set_vim_var_type(VV_KEY, VAR_NUMBER); + set_vim_var_type(VV_VAL, VAR_NUMBER); + + for (idx = startidx; idx < blob_len(b); ++idx) + { + set_vim_var_nr(VV_KEY, idx); + set_vim_var_nr(VV_VAL, blob_get(b, 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 int +indexof_list(list_T *l, long startidx, typval_T *expr) +{ + listitem_T *item; + long idx = 0; + int found; + + if (l == NULL) + return -1; + + CHECK_LIST_MATERIALIZE(l); + + if (startidx == 0) + item = l->lv_first; + else + { + // Start at specified item. Use the cached index that list_find() + // sets, so that a negative number also works. + item = list_find(l, startidx); + if (item != NULL) + idx = l->lv_u.mat.lv_idx; + } + + set_vim_var_type(VV_KEY, VAR_NUMBER); + + for ( ; item != NULL; item = item->li_next, ++idx) + { + set_vim_var_nr(VV_KEY, idx); + copy_tv(&item->li_tv, get_vim_var_tv(VV_VAL)); + + found = indexof_eval_expr(expr); + clear_tv(get_vim_var_tv(VV_VAL)); + + if (found) + return idx; + } + + return -1; +} + +/* + * "indexof()" function + */ + static void +f_indexof(typval_T *argvars, typval_T *rettv) +{ + long startidx = 0; + typval_T save_val; + typval_T save_key; + int save_did_emsg; + + rettv->vval.v_number = -1; + + if (check_for_list_or_blob_arg(argvars, 0) == FAIL + || check_for_string_or_func_arg(argvars, 1) == FAIL + || 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; + + if (argvars[2].v_type == VAR_DICT) + startidx = dict_get_number_def(argvars[2].vval.v_dict, "startidx", 0); + + 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. + 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 int inputsecret_flag = 0; + +/* + * "input()" function + * Also handles inputsecret() when inputsecret is set. + */ + static void +f_input(typval_T *argvars, typval_T *rettv) +{ + get_user_input(argvars, rettv, FALSE, inputsecret_flag); +} + +/* + * "inputdialog()" function + */ + static void +f_inputdialog(typval_T *argvars, typval_T *rettv) +{ +#if defined(FEAT_GUI_TEXTDIALOG) + // Use a GUI dialog if the GUI is running and 'c' is not in 'guioptions' + if (gui.in_use && vim_strchr(p_go, GO_CONDIALOG) == NULL) + { + char_u *message; + char_u buf[NUMBUFLEN]; + char_u *defstr = (char_u *)""; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_opt_string_arg(argvars, 1) == FAIL + || (argvars[1].v_type != VAR_UNKNOWN + && check_for_opt_string_arg(argvars, 2) == FAIL))) + return; + + message = tv_get_string_chk(&argvars[0]); + if (argvars[1].v_type != VAR_UNKNOWN + && (defstr = tv_get_string_buf_chk(&argvars[1], buf)) != NULL) + vim_strncpy(IObuff, defstr, IOSIZE - 1); + else + IObuff[0] = NUL; + if (message != NULL && defstr != NULL + && do_dialog(VIM_QUESTION, NULL, message, + (char_u *)_("&OK\n&Cancel"), 1, IObuff, FALSE) == 1) + rettv->vval.v_string = vim_strsave(IObuff); + else + { + if (message != NULL && defstr != NULL + && argvars[1].v_type != VAR_UNKNOWN + && argvars[2].v_type != VAR_UNKNOWN) + rettv->vval.v_string = vim_strsave( + tv_get_string_buf(&argvars[2], buf)); + else + rettv->vval.v_string = NULL; + } + rettv->v_type = VAR_STRING; + } + else +#endif + get_user_input(argvars, rettv, TRUE, inputsecret_flag); +} + +/* + * "inputlist()" function + */ + static void +f_inputlist(typval_T *argvars, typval_T *rettv) +{ + list_T *l; + listitem_T *li; + int selected; + int mouse_used; + +#ifdef NO_CONSOLE_INPUT + // While starting up, there is no place to enter text. When running tests + // with --not-a-term we assume feedkeys() will be used. + if (no_console_input() && !is_not_a_term()) + return; +#endif + if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL) + return; + + if (argvars[0].v_type != VAR_LIST || argvars[0].vval.v_list == NULL) + { + semsg(_(e_argument_of_str_must_be_list), "inputlist()"); + return; + } + + msg_start(); + msg_row = Rows - 1; // for when 'cmdheight' > 1 + lines_left = Rows; // avoid more prompt + msg_scroll = TRUE; + msg_clr_eos(); + + l = argvars[0].vval.v_list; + CHECK_LIST_MATERIALIZE(l); + FOR_ALL_LIST_ITEMS(l, li) + { + msg_puts((char *)tv_get_string(&li->li_tv)); + msg_putchar('\n'); + } + + // Ask for choice. + selected = prompt_for_number(&mouse_used); + if (mouse_used) + selected -= lines_left; + + rettv->vval.v_number = selected; +} + +static garray_T ga_userinput = {0, 0, sizeof(tasave_T), 4, NULL}; + +/* + * "inputrestore()" function + */ + static void +f_inputrestore(typval_T *argvars UNUSED, typval_T *rettv) +{ + if (ga_userinput.ga_len > 0) + { + --ga_userinput.ga_len; + restore_typeahead((tasave_T *)(ga_userinput.ga_data) + + ga_userinput.ga_len, TRUE); + // default return is zero == OK + } + else if (p_verbose > 1) + { + verb_msg(_("called inputrestore() more often than inputsave()")); + rettv->vval.v_number = 1; // Failed + } +} + +/* + * "inputsave()" function + */ + static void +f_inputsave(typval_T *argvars UNUSED, typval_T *rettv) +{ + // Add an entry to the stack of typeahead storage. + if (ga_grow(&ga_userinput, 1) == OK) + { + save_typeahead((tasave_T *)(ga_userinput.ga_data) + + ga_userinput.ga_len); + ++ga_userinput.ga_len; + // default return is zero == OK + } + else + rettv->vval.v_number = 1; // Failed +} + +/* + * "inputsecret()" function + */ + static void +f_inputsecret(typval_T *argvars, typval_T *rettv) +{ + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_opt_string_arg(argvars, 1) == FAIL)) + return; + + ++cmdline_star; + ++inputsecret_flag; + f_input(argvars, rettv); + --cmdline_star; + --inputsecret_flag; +} + +/* + * "interrupt()" function + */ + static void +f_interrupt(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ + got_int = TRUE; +} + +/* + * "invert(expr)" function + */ + static void +f_invert(typval_T *argvars, typval_T *rettv) +{ + if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL) + return; + + rettv->vval.v_number = ~tv_get_number_chk(&argvars[0], NULL); +} + +/* + * Free resources in lval_root allocated by fill_exec_lval_root(). + */ + static void +free_lval_root(lval_root_T *root) +{ + if (root->lr_tv != NULL) + free_tv(root->lr_tv); + class_unref(root->lr_cl_exec); + root->lr_tv = NULL; + root->lr_cl_exec = NULL; +} + +/* + * This is used if executing in a method, the argument string is a + * variable/item expr/reference. It may start with a potential class/object + * variable. + * + * Adjust "root" as needed; lr_tv may be changed or freed. + * + * Always returns OK. + * Free resources and return FAIL if the root should not be used. Otherwise OK. + */ + + static int +fix_variable_reference_lval_root(lval_root_T *root, char_u *name) +{ + + // Check if lr_tv is the name of an object/class reference: name start with + // "this" or name is class variable. Clear lr_tv if neither. + int found_member = FALSE; + if (root->lr_tv->v_type == VAR_OBJECT) + { + if (STRNCMP("this.", name, 5) == 0 ||STRCMP("this", name) == 0) + found_member = TRUE; + } + if (!found_member) // not object member, try class member + { + // Explicitly check if the name is a class member. + // If it's not then do nothing. + char_u *end; + for (end = name; ASCII_ISALNUM(*end) || *end == '_'; ++end) + ; + int idx = class_member_idx(root->lr_cl_exec, name, end - name); + if (idx >= 0) + { + // A class variable, replace lr_tv with it + clear_tv(root->lr_tv); + copy_tv(&root->lr_cl_exec->class_members_tv[idx], root->lr_tv); + found_member = TRUE; + } + } + if (!found_member) + { + free_tv(root->lr_tv); + root->lr_tv = NULL; // Not a member variable + } + // If FAIL, then must free_lval_root(root); + return OK; +} + +/* + * "islocked()" function + */ + static void +f_islocked(typval_T *argvars, typval_T *rettv) +{ + lval_T lv; + char_u *end; + dictitem_T *di; + + rettv->vval.v_number = -1; + + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + char_u *name = tv_get_string(&argvars[0]); +#ifdef LOG_LOCKVAR + ch_log(NULL, "LKVAR: f_islocked(): name: %s", name); +#endif + + lval_root_T aroot; // fully initialized in fill_exec_lval_root + lval_root_T *root = NULL; + + // Set up lval_root if executing in a method. + if (fill_exec_lval_root(&aroot) == OK) + { + // Almost always produces a valid lval_root since lr_cl_exec is used + // for access verification, lr_tv may be set to NULL. + if (fix_variable_reference_lval_root(&aroot, name) == OK) + root = &aroot; + } + + lval_root_T *lval_root_save = lval_root; + lval_root = root; + end = get_lval(name, NULL, &lv, FALSE, FALSE, + GLV_NO_AUTOLOAD | GLV_READ_ONLY | GLV_NO_DECL, + FNE_CHECK_START); + lval_root = lval_root_save; + + if (end != NULL && lv.ll_name != NULL) + { + if (*end != NUL) + { + semsg(_(lv.ll_name == lv.ll_name_end + ? e_invalid_argument_str : e_trailing_characters_str), end); + } + else + { + if (lv.ll_tv == NULL) + { + di = find_var(lv.ll_name, NULL, TRUE); + if (di != NULL) + { + // Consider a variable locked when: + // 1. the variable itself is locked + // 2. the value of the variable is locked. + // 3. the List or Dict value is locked. + rettv->vval.v_number = ((di->di_flags & DI_FLAGS_LOCK) + || tv_islocked(&di->di_tv)); + } + } + else if (lv.ll_is_root) + { + rettv->vval.v_number = tv_islocked(lv.ll_tv); + } + else if (lv.ll_object != NULL) + { + typval_T *tv = ((typval_T *)(lv.ll_object + 1)) + lv.ll_oi; + rettv->vval.v_number = tv_islocked(tv); +#ifdef LOG_LOCKVAR + ch_log(NULL, "LKVAR: f_islocked(): name %s (obj)", lv.ll_name); +#endif + } + else if (lv.ll_class != NULL) + { + typval_T *tv = &lv.ll_class->class_members_tv[lv.ll_oi]; + rettv->vval.v_number = tv_islocked(tv); +#ifdef LOG_LOCKVAR + ch_log(NULL, "LKVAR: f_islocked(): name %s (cl)", lv.ll_name); +#endif + } + else if (lv.ll_range) + emsg(_(e_range_not_allowed)); + else if (lv.ll_newkey != NULL) + semsg(_(e_key_not_present_in_dictionary_str), lv.ll_newkey); + else if (lv.ll_list != NULL) + // List item. + rettv->vval.v_number = tv_islocked(&lv.ll_li->li_tv); + else + // Dictionary item. + rettv->vval.v_number = tv_islocked(&lv.ll_di->di_tv); + } + } + + if (root != NULL) + free_lval_root(root); + clear_lval(&lv); +} + +/* + * "keytrans()" function + */ + static void +f_keytrans(typval_T *argvars, typval_T *rettv) +{ + char_u *escaped; + + rettv->v_type = VAR_STRING; + if (check_for_string_arg(argvars, 0) == FAIL + || argvars[0].vval.v_string == NULL) + return; + // Need to escape K_SPECIAL and CSI for mb_unescape(). + escaped = vim_strsave_escape_csi(argvars[0].vval.v_string); + rettv->vval.v_string = str2special_save(escaped, TRUE, TRUE); + vim_free(escaped); +} + +/* + * "last_buffer_nr()" function. + */ + static void +f_last_buffer_nr(typval_T *argvars UNUSED, typval_T *rettv) +{ + int n = 0; + buf_T *buf; + + FOR_ALL_BUFFERS(buf) + if (n < buf->b_fnum) + n = buf->b_fnum; + + rettv->vval.v_number = n; +} + +/* + * "len()" function + */ + void +f_len(typval_T *argvars, typval_T *rettv) +{ + switch (argvars[0].v_type) + { + case VAR_STRING: + case VAR_NUMBER: + rettv->vval.v_number = (varnumber_T)STRLEN( + tv_get_string(&argvars[0])); + break; + case VAR_BLOB: + rettv->vval.v_number = blob_len(argvars[0].vval.v_blob); + break; + case VAR_LIST: + rettv->vval.v_number = list_len(argvars[0].vval.v_list); + break; + case VAR_DICT: + rettv->vval.v_number = dict_len(argvars[0].vval.v_dict); + break; + case VAR_UNKNOWN: + case VAR_ANY: + case VAR_VOID: + case VAR_BOOL: + case VAR_SPECIAL: + case VAR_FLOAT: + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_JOB: + case VAR_CHANNEL: + case VAR_INSTR: + case VAR_CLASS: + case VAR_OBJECT: + case VAR_TYPEALIAS: + emsg(_(e_invalid_type_for_len)); + break; + } +} + + static void +libcall_common(typval_T *argvars UNUSED, typval_T *rettv, int type) +{ +#ifdef FEAT_LIBCALL + char_u *string_in; + char_u **string_result; + int nr_result; +#endif + + rettv->v_type = type; + if (type != VAR_NUMBER) + rettv->vval.v_string = NULL; + + if (check_restricted() || check_secure()) + return; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_string_arg(argvars, 1) == FAIL + || check_for_string_or_number_arg(argvars, 2) == FAIL)) + return; + +#ifdef FEAT_LIBCALL + // The first two args must be strings, otherwise it's meaningless + if (argvars[0].v_type == VAR_STRING && argvars[1].v_type == VAR_STRING) + { + string_in = NULL; + if (argvars[2].v_type == VAR_STRING) + string_in = argvars[2].vval.v_string; + if (type == VAR_NUMBER) + { + string_result = NULL; + } + else + { + rettv->vval.v_string = NULL; + string_result = &rettv->vval.v_string; + } + if (mch_libcall(argvars[0].vval.v_string, + argvars[1].vval.v_string, + string_in, + argvars[2].vval.v_number, + string_result, + &nr_result) == OK + && type == VAR_NUMBER) + rettv->vval.v_number = nr_result; + } +#endif +} + +/* + * "libcall()" function + */ + static void +f_libcall(typval_T *argvars, typval_T *rettv) +{ + libcall_common(argvars, rettv, VAR_STRING); +} + +/* + * "libcallnr()" function + */ + static void +f_libcallnr(typval_T *argvars, typval_T *rettv) +{ + libcall_common(argvars, rettv, VAR_NUMBER); +} + +/* + * "line(string, [winid])" function + */ + static void +f_line(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum = 0; + pos_T *fp = NULL; + int fnum; + int id; + tabpage_T *tp; + win_T *wp; + switchwin_T switchwin; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_opt_number_arg(argvars, 1) == FAIL)) + return; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + // use window specified in the second argument + id = (int)tv_get_number(&argvars[1]); + wp = win_id2wp_tp(id, &tp); + if (wp != NULL && tp != NULL) + { + if (switch_win_noblock(&switchwin, wp, tp, TRUE) == OK) + { + check_cursor(); + fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE); + } + restore_win_noblock(&switchwin, TRUE); + } + } + else + // use current window + fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE); + + if (fp != NULL) + lnum = fp->lnum; + rettv->vval.v_number = lnum; +} + +/* + * "line2byte(lnum)" function + */ + static void +f_line2byte(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifndef FEAT_BYTEOFF + rettv->vval.v_number = -1; +#else + linenr_T lnum; + + if (in_vim9script() && check_for_lnum_arg(argvars, 0) == FAIL) + return; + + lnum = tv_get_lnum(argvars); + if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) + rettv->vval.v_number = -1; + else + rettv->vval.v_number = ml_find_line_or_offset(curbuf, lnum, NULL); + if (rettv->vval.v_number >= 0) + ++rettv->vval.v_number; +#endif +} + +#ifdef FEAT_LUA +/* + * "luaeval()" function + */ + static void +f_luaeval(typval_T *argvars, typval_T *rettv) +{ + char_u *str; + char_u buf[NUMBUFLEN]; + + if (check_restricted() || check_secure()) + return; + + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + str = tv_get_string_buf(&argvars[0], buf); + do_luaeval(str, argvars + 1, rettv); +} +#endif + +typedef enum +{ + MATCH_END, // matchend() + MATCH_MATCH, // match() + MATCH_STR, // matchstr() + MATCH_LIST, // matchlist() + MATCH_POS // matchstrpos() +} matchtype_T; + + static void +find_some_match(typval_T *argvars, typval_T *rettv, matchtype_T type) +{ + char_u *str = NULL; + long len = 0; + char_u *expr = NULL; + char_u *pat; + regmatch_T regmatch; + char_u patbuf[NUMBUFLEN]; + char_u strbuf[NUMBUFLEN]; + char_u *save_cpo; + long start = 0; + long nth = 1; + colnr_T startcol = 0; + int match = 0; + list_T *l = NULL; + listitem_T *li = NULL; + long idx = 0; + char_u *tofree = NULL; + + // Make 'cpoptions' empty, the 'l' flag should not be used here. + save_cpo = p_cpo; + p_cpo = empty_option; + + rettv->vval.v_number = -1; + if (type == MATCH_LIST || type == MATCH_POS) + { + // type MATCH_LIST: return empty list when there are no matches. + // type MATCH_POS: return ["", -1, -1, -1] + if (rettv_list_alloc(rettv) == FAIL) + goto theend; + if (type == MATCH_POS + && (list_append_string(rettv->vval.v_list, + (char_u *)"", 0) == FAIL + || list_append_number(rettv->vval.v_list, + (varnumber_T)-1) == FAIL + || list_append_number(rettv->vval.v_list, + (varnumber_T)-1) == FAIL + || list_append_number(rettv->vval.v_list, + (varnumber_T)-1) == FAIL)) + { + list_free(rettv->vval.v_list); + rettv->vval.v_list = NULL; + goto theend; + } + } + else if (type == MATCH_STR) + { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + } + + if (in_vim9script() + && (check_for_string_or_list_arg(argvars, 0) == FAIL + || check_for_string_arg(argvars, 1) == FAIL + || check_for_opt_number_arg(argvars, 2) == FAIL + || (argvars[2].v_type != VAR_UNKNOWN + && check_for_opt_number_arg(argvars, 3) == FAIL))) + goto theend; + + if (argvars[0].v_type == VAR_LIST) + { + if ((l = argvars[0].vval.v_list) == NULL) + goto theend; + CHECK_LIST_MATERIALIZE(l); + li = l->lv_first; + } + else + { + expr = str = tv_get_string(&argvars[0]); + len = (long)STRLEN(str); + } + + pat = tv_get_string_buf_chk(&argvars[1], patbuf); + if (pat == NULL) + goto theend; + + if (argvars[2].v_type != VAR_UNKNOWN) + { + int error = FALSE; + + start = (long)tv_get_number_chk(&argvars[2], &error); + if (error) + goto theend; + if (l != NULL) + { + li = list_find(l, start); + if (li == NULL) + goto theend; + idx = l->lv_u.mat.lv_idx; // use the cached index + } + else + { + if (start < 0) + start = 0; + if (start > len) + goto theend; + // When "count" argument is there ignore matches before "start", + // otherwise skip part of the string. Differs when pattern is "^" + // or "\<". + if (argvars[3].v_type != VAR_UNKNOWN) + startcol = start; + else + { + str += start; + len -= start; + } + } + + if (argvars[3].v_type != VAR_UNKNOWN) + nth = (long)tv_get_number_chk(&argvars[3], &error); + if (error) + goto theend; + } + + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog != NULL) + { + regmatch.rm_ic = p_ic; + + for (;;) + { + if (l != NULL) + { + if (li == NULL) + { + match = FALSE; + break; + } + vim_free(tofree); + expr = str = echo_string(&li->li_tv, &tofree, strbuf, 0); + if (str == NULL) + break; + } + + match = vim_regexec_nl(®match, str, startcol); + + if (match && --nth <= 0) + break; + if (l == NULL && !match) + break; + + // Advance to just after the match. + if (l != NULL) + { + li = li->li_next; + ++idx; + } + else + { + startcol = (colnr_T)(regmatch.startp[0] + + (*mb_ptr2len)(regmatch.startp[0]) - str); + if (startcol > (colnr_T)len + || str + startcol <= regmatch.startp[0]) + { + match = FALSE; + break; + } + } + } + + if (match) + { + if (type == MATCH_POS) + { + listitem_T *li1 = rettv->vval.v_list->lv_first; + listitem_T *li2 = li1->li_next; + listitem_T *li3 = li2->li_next; + listitem_T *li4 = li3->li_next; + + vim_free(li1->li_tv.vval.v_string); + li1->li_tv.vval.v_string = vim_strnsave(regmatch.startp[0], + regmatch.endp[0] - regmatch.startp[0]); + li3->li_tv.vval.v_number = + (varnumber_T)(regmatch.startp[0] - expr); + li4->li_tv.vval.v_number = + (varnumber_T)(regmatch.endp[0] - expr); + if (l != NULL) + li2->li_tv.vval.v_number = (varnumber_T)idx; + } + else if (type == MATCH_LIST) + { + int i; + + // return list with matched string and submatches + for (i = 0; i < NSUBEXP; ++i) + { + if (regmatch.endp[i] == NULL) + { + if (list_append_string(rettv->vval.v_list, + (char_u *)"", 0) == FAIL) + break; + } + else if (list_append_string(rettv->vval.v_list, + regmatch.startp[i], + (int)(regmatch.endp[i] - regmatch.startp[i])) + == FAIL) + break; + } + } + else if (type == MATCH_STR) + { + // return matched string + if (l != NULL) + copy_tv(&li->li_tv, rettv); + else + rettv->vval.v_string = vim_strnsave(regmatch.startp[0], + regmatch.endp[0] - regmatch.startp[0]); + } + else if (l != NULL) + rettv->vval.v_number = idx; + else + { + if (type != MATCH_END) + rettv->vval.v_number = + (varnumber_T)(regmatch.startp[0] - str); + else + rettv->vval.v_number = + (varnumber_T)(regmatch.endp[0] - str); + rettv->vval.v_number += (varnumber_T)(str - expr); + } + } + vim_regfree(regmatch.regprog); + } + +theend: + if (type == MATCH_POS && l == NULL && rettv->vval.v_list != NULL) + // matchstrpos() without a list: drop the second item. + listitem_remove(rettv->vval.v_list, + rettv->vval.v_list->lv_first->li_next); + vim_free(tofree); + p_cpo = save_cpo; +} + +/* + * Return all the matches in string "str" for pattern "rmp". + * The matches are returned in the List "mlist". + * If "submatches" is TRUE, then submatch information is also returned. + * "matchbuf" is TRUE when called for matchbufline(). + */ + static int +get_matches_in_str( + char_u *str, + regmatch_T *rmp, + list_T *mlist, + int idx, + int submatches, + int matchbuf) +{ + long len = (long)STRLEN(str); + int match = 0; + colnr_T startidx = 0; + + for (;;) + { + match = vim_regexec_nl(rmp, str, startidx); + if (!match) + break; + + dict_T *d = dict_alloc(); + if (d == NULL) + return FAIL; + if (list_append_dict(mlist, d) == FAIL) + return FAIL;; + + if (dict_add_number(d, matchbuf ? "lnum" : "idx", idx) == FAIL) + return FAIL; + + if (dict_add_number(d, "byteidx", + (colnr_T)(rmp->startp[0] - str)) == FAIL) + return FAIL; + + if (dict_add_string_len(d, "text", rmp->startp[0], + (int)(rmp->endp[0] - rmp->startp[0])) == FAIL) + return FAIL; + + if (submatches) + { + list_T *sml = list_alloc(); + if (sml == NULL) + return FAIL; + + if (dict_add_list(d, "submatches", sml) == FAIL) + return FAIL; + + // return a list with the submatches + for (int i = 1; i < NSUBEXP; ++i) + { + if (rmp->endp[i] == NULL) + { + if (list_append_string(sml, (char_u *)"", 0) == FAIL) + return FAIL; + } + else if (list_append_string(sml, rmp->startp[i], + (int)(rmp->endp[i] - rmp->startp[i])) == FAIL) + return FAIL; + } + } + startidx = (colnr_T)(rmp->endp[0] - str); + if (startidx >= (colnr_T)len || str + startidx <= rmp->startp[0]) + break; + } + + return OK; +} + +/* + * "matchbufline()" function + */ + static void +f_matchbufline(typval_T *argvars, typval_T *rettv) +{ + list_T *retlist = NULL; + char_u *save_cpo; + char_u patbuf[NUMBUFLEN]; + regmatch_T regmatch; + + rettv->vval.v_number = -1; + if (rettv_list_alloc(rettv) != OK) + return; + retlist = rettv->vval.v_list; + + if (check_for_buffer_arg(argvars, 0) == FAIL + || check_for_string_arg(argvars, 1) == FAIL + || check_for_lnum_arg(argvars, 2) == FAIL + || check_for_lnum_arg(argvars, 3) == FAIL + || check_for_opt_dict_arg(argvars, 4) == FAIL) + return; + + int prev_did_emsg = did_emsg; + buf_T *buf = tv_get_buf(&argvars[0], FALSE); + if (buf == NULL) + { + if (did_emsg == prev_did_emsg) + semsg(_(e_invalid_buffer_name_str), tv_get_string(&argvars[0])); + return; + } + if (buf->b_ml.ml_mfp == NULL) + { + emsg(_(e_buffer_is_not_loaded)); + return; + } + + char_u *pat = tv_get_string_buf(&argvars[1], patbuf); + + int did_emsg_before = did_emsg; + linenr_T slnum = tv_get_lnum_buf(&argvars[2], buf); + if (did_emsg > did_emsg_before) + return; + if (slnum < 1) + { + semsg(_(e_invalid_value_for_argument_str), "lnum"); + return; + } + + linenr_T elnum = tv_get_lnum_buf(&argvars[3], buf); + if (did_emsg > did_emsg_before) + return; + if (elnum < 1 || elnum < slnum) + { + semsg(_(e_invalid_value_for_argument_str), "end_lnum"); + return; + } + + if (elnum > buf->b_ml.ml_line_count) + elnum = buf->b_ml.ml_line_count; + + int submatches = FALSE; + if (argvars[4].v_type != VAR_UNKNOWN) + { + dict_T *d = argvars[4].vval.v_dict; + if (d != NULL) + { + dictitem_T *di = dict_find(d, (char_u *)"submatches", -1); + if (di != NULL) + { + if (di->di_tv.v_type != VAR_BOOL) + { + semsg(_(e_invalid_value_for_argument_str), "submatches"); + return; + } + submatches = tv_get_bool(&di->di_tv); + } + } + } + + // Make 'cpoptions' empty, the 'l' flag should not be used here. + save_cpo = p_cpo; + p_cpo = empty_option; + + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog == NULL) + goto theend; + regmatch.rm_ic = p_ic; + + while (slnum <= elnum) + { + char_u *str = ml_get_buf(buf, slnum, FALSE); + if (get_matches_in_str(str, ®match, retlist, slnum, submatches, + TRUE) == FAIL) + goto cleanup; + slnum++; + } + +cleanup: + vim_regfree(regmatch.regprog); + +theend: + p_cpo = save_cpo; +} + +/* + * "match()" function + */ + static void +f_match(typval_T *argvars, typval_T *rettv) +{ + find_some_match(argvars, rettv, MATCH_MATCH); +} + +/* + * "matchend()" function + */ + static void +f_matchend(typval_T *argvars, typval_T *rettv) +{ + find_some_match(argvars, rettv, MATCH_END); +} + +/* + * "matchlist()" function + */ + static void +f_matchlist(typval_T *argvars, typval_T *rettv) +{ + find_some_match(argvars, rettv, MATCH_LIST); +} + +/* + * "matchstr()" function + */ + static void +f_matchstr(typval_T *argvars, typval_T *rettv) +{ + find_some_match(argvars, rettv, MATCH_STR); +} + +/* + * "matchstrlist()" function + */ + static void +f_matchstrlist(typval_T *argvars, typval_T *rettv) +{ + list_T *retlist = NULL; + char_u *save_cpo; + list_T *l = NULL; + listitem_T *li = NULL; + char_u patbuf[NUMBUFLEN]; + regmatch_T regmatch; + + rettv->vval.v_number = -1; + if (rettv_list_alloc(rettv) != OK) + return; + retlist = rettv->vval.v_list; + + if (check_for_list_arg(argvars, 0) == FAIL + || check_for_string_arg(argvars, 1) == FAIL + || check_for_opt_dict_arg(argvars, 2) == FAIL) + return; + + if ((l = argvars[0].vval.v_list) == NULL) + return; + + char_u *pat = tv_get_string_buf_chk(&argvars[1], patbuf); + if (pat == NULL) + return; + + // Make 'cpoptions' empty, the 'l' flag should not be used here. + save_cpo = p_cpo; + p_cpo = empty_option; + + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog == NULL) + goto theend; + regmatch.rm_ic = p_ic; + + int submatches = FALSE; + if (argvars[2].v_type != VAR_UNKNOWN) + { + dict_T *d = argvars[2].vval.v_dict; + if (d != NULL) + { + dictitem_T *di = dict_find(d, (char_u *)"submatches", -1); + if (di != NULL) + { + if (di->di_tv.v_type != VAR_BOOL) + { + semsg(_(e_invalid_value_for_argument_str), "submatches"); + goto cleanup; + } + submatches = tv_get_bool(&di->di_tv); + } + } + } + + int idx = 0; + CHECK_LIST_MATERIALIZE(l); + FOR_ALL_LIST_ITEMS(l, li) + { + if (li->li_tv.v_type == VAR_STRING && li->li_tv.vval.v_string != NULL) + { + char_u *str = li->li_tv.vval.v_string; + if (get_matches_in_str(str, ®match, retlist, idx, submatches, + FALSE) == FAIL) + goto cleanup; + } + idx++; + } + +cleanup: + vim_regfree(regmatch.regprog); + +theend: + p_cpo = save_cpo; +} + +/* + * "matchstrpos()" function + */ + static void +f_matchstrpos(typval_T *argvars, typval_T *rettv) +{ + find_some_match(argvars, rettv, MATCH_POS); +} + + static void +max_min(typval_T *argvars, typval_T *rettv, int domax) +{ + varnumber_T n = 0; + varnumber_T i; + int error = FALSE; + + if (in_vim9script() && check_for_list_or_dict_arg(argvars, 0) == FAIL) + return; + + if (argvars[0].v_type == VAR_LIST) + { + list_T *l; + listitem_T *li; + + l = argvars[0].vval.v_list; + if (l != NULL && l->lv_len > 0) + { + if (l->lv_first == &range_list_item) + { + if ((l->lv_u.nonmat.lv_stride > 0) ^ domax) + n = l->lv_u.nonmat.lv_start; + else + n = l->lv_u.nonmat.lv_start + ((varnumber_T)l->lv_len - 1) + * l->lv_u.nonmat.lv_stride; + } + else + { + li = l->lv_first; + if (li != NULL) + { + n = tv_get_number_chk(&li->li_tv, &error); + if (error) + return; // type error; errmsg already given + for (;;) + { + li = li->li_next; + if (li == NULL) + break; + i = tv_get_number_chk(&li->li_tv, &error); + if (error) + return; // type error; errmsg already given + if (domax ? i > n : i < n) + n = i; + } + } + } + } + } + else if (argvars[0].v_type == VAR_DICT) + { + dict_T *d; + int first = TRUE; + hashitem_T *hi; + int todo; + + d = argvars[0].vval.v_dict; + if (d != NULL) + { + todo = (int)d->dv_hashtab.ht_used; + FOR_ALL_HASHTAB_ITEMS(&d->dv_hashtab, hi, todo) + { + if (!HASHITEM_EMPTY(hi)) + { + --todo; + i = tv_get_number_chk(&HI2DI(hi)->di_tv, &error); + if (error) + return; // type error; errmsg already given + if (first) + { + n = i; + first = FALSE; + } + else if (domax ? i > n : i < n) + n = i; + } + } + } + } + else + semsg(_(e_argument_of_str_must_be_list_or_dictionary), domax ? "max()" : "min()"); + + rettv->vval.v_number = n; +} + +/* + * "max()" function + */ + static void +f_max(typval_T *argvars, typval_T *rettv) +{ + max_min(argvars, rettv, TRUE); +} + +/* + * "min()" function + */ + static void +f_min(typval_T *argvars, typval_T *rettv) +{ + max_min(argvars, rettv, FALSE); +} + +#if defined(FEAT_MZSCHEME) || defined(PROTO) +/* + * "mzeval()" function + */ + static void +f_mzeval(typval_T *argvars, typval_T *rettv) +{ + char_u *str; + char_u buf[NUMBUFLEN]; + + if (check_restricted() || check_secure()) + return; + + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + str = tv_get_string_buf(&argvars[0], buf); + do_mzeval(str, rettv); +} + + void +mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv) +{ + typval_T argvars[3]; + + argvars[0].v_type = VAR_STRING; + argvars[0].vval.v_string = name; + copy_tv(args, &argvars[1]); + argvars[2].v_type = VAR_UNKNOWN; + f_call(argvars, rettv); + clear_tv(&argvars[1]); +} +#endif + +/* + * "nextnonblank()" function + */ + static void +f_nextnonblank(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum; + + if (in_vim9script() && check_for_lnum_arg(argvars, 0) == FAIL) + return; + + for (lnum = tv_get_lnum(argvars); ; ++lnum) + { + if (lnum < 0 || lnum > curbuf->b_ml.ml_line_count) + { + lnum = 0; + break; + } + if (*skipwhite(ml_get(lnum)) != NUL) + break; + } + rettv->vval.v_number = lnum; +} + +/* + * "nr2char()" function + */ + static void +f_nr2char(typval_T *argvars, typval_T *rettv) +{ + char_u buf[NUMBUFLEN]; + + if (in_vim9script() + && (check_for_number_arg(argvars, 0) == FAIL + || check_for_opt_bool_arg(argvars, 1) == FAIL)) + return; + + if (has_mbyte) + { + int utf8 = 0; + + if (argvars[1].v_type != VAR_UNKNOWN) + utf8 = (int)tv_get_bool_chk(&argvars[1], NULL); + if (utf8) + buf[utf_char2bytes((int)tv_get_number(&argvars[0]), buf)] = NUL; + else + buf[(*mb_char2bytes)((int)tv_get_number(&argvars[0]), buf)] = NUL; + } + else + { + buf[0] = (char_u)tv_get_number(&argvars[0]); + buf[1] = NUL; + } + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave(buf); +} + +/* + * "or(expr, expr)" function + */ + static void +f_or(typval_T *argvars, typval_T *rettv) +{ + if (in_vim9script() + && (check_for_number_arg(argvars, 0) == FAIL + || check_for_number_arg(argvars, 1) == FAIL)) + return; + + rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) + | tv_get_number_chk(&argvars[1], NULL); +} + +#ifdef FEAT_PERL +/* + * "perleval()" function + */ + static void +f_perleval(typval_T *argvars, typval_T *rettv) +{ + char_u *str; + char_u buf[NUMBUFLEN]; + + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + str = tv_get_string_buf(&argvars[0], buf); + do_perleval(str, rettv); +} +#endif + +/* + * "prevnonblank()" function + */ + static void +f_prevnonblank(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum; + + if (in_vim9script() && check_for_lnum_arg(argvars, 0) == FAIL) + return; + + lnum = tv_get_lnum(argvars); + if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) + lnum = 0; + else + while (lnum >= 1 && *skipwhite(ml_get(lnum)) == NUL) + --lnum; + rettv->vval.v_number = lnum; +} + +// This dummy va_list is here because: +// - passing a NULL pointer doesn't work when va_list isn't a pointer +// - locally in the function results in a "used before set" warning +// - using va_start() to initialize it gives "function with fixed args" error +static va_list ap; + +/* + * "printf()" function + */ + static void +f_printf(typval_T *argvars, typval_T *rettv) +{ + char_u buf[NUMBUFLEN]; + int len; + char_u *s; + int saved_did_emsg = did_emsg; + char *fmt; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + if (in_vim9script() && check_for_string_or_number_arg(argvars, 0) == FAIL) + return; + + // Get the required length, allocate the buffer and do it for real. + did_emsg = FALSE; + fmt = (char *)tv_get_string_buf(&argvars[0], buf); + len = vim_vsnprintf_typval(NULL, 0, fmt, ap, argvars + 1); + if (!did_emsg) + { + s = alloc(len + 1); + if (s != NULL) + { + rettv->vval.v_string = s; + (void)vim_vsnprintf_typval((char *)s, len + 1, fmt, + ap, argvars + 1); + } + } + did_emsg |= saved_did_emsg; +} + +/* + * "pum_getpos()" function + */ + static void +f_pum_getpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ + if (rettv_dict_alloc(rettv) == FAIL) + return; + pum_set_event_info(rettv->vval.v_dict); +} + +/* + * "pumvisible()" function + */ + static void +f_pumvisible(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ + if (pum_visible()) + rettv->vval.v_number = 1; +} + +#ifdef FEAT_PYTHON3 +/* + * "py3eval()" function + */ + static void +f_py3eval(typval_T *argvars, typval_T *rettv) +{ + char_u *str; + char_u buf[NUMBUFLEN]; + + if (check_restricted() || check_secure()) + return; + + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + if (p_pyx == 0) + p_pyx = 3; + + str = tv_get_string_buf(&argvars[0], buf); + do_py3eval(str, rettv); +} +#endif + +#ifdef FEAT_PYTHON +/* + * "pyeval()" function + */ + static void +f_pyeval(typval_T *argvars, typval_T *rettv) +{ + char_u *str; + char_u buf[NUMBUFLEN]; + + if (check_restricted() || check_secure()) + return; + + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + if (p_pyx == 0) + p_pyx = 2; + + str = tv_get_string_buf(&argvars[0], buf); + do_pyeval(str, rettv); +} +#endif + +#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) +/* + * "pyxeval()" function + */ + static void +f_pyxeval(typval_T *argvars, typval_T *rettv) +{ + if (check_restricted() || check_secure()) + return; + + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + +# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3) + init_pyxversion(); + if (p_pyx == 2) + f_pyeval(argvars, rettv); + else + f_py3eval(argvars, rettv); +# elif defined(FEAT_PYTHON) + f_pyeval(argvars, rettv); +# elif defined(FEAT_PYTHON3) + f_py3eval(argvars, rettv); +# endif +} +#endif + +static UINT32_T srand_seed_for_testing = 0; +static int srand_seed_for_testing_is_used = FALSE; + + static void +f_test_srand_seed(typval_T *argvars, typval_T *rettv UNUSED) +{ + if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL) + return; + + if (argvars[0].v_type == VAR_UNKNOWN) + srand_seed_for_testing_is_used = FALSE; + else + { + srand_seed_for_testing = (UINT32_T)tv_get_number(&argvars[0]); + srand_seed_for_testing_is_used = TRUE; + } +} + + static void +init_srand(UINT32_T *x) +{ +#ifndef MSWIN + static int dev_urandom_state = NOTDONE; // FAIL or OK once tried +#endif + + if (srand_seed_for_testing_is_used) + { + *x = srand_seed_for_testing; + return; + } +#ifndef MSWIN + if (dev_urandom_state != FAIL) + { + int fd = open("/dev/urandom", O_RDONLY); + struct { + union { + UINT32_T number; + char bytes[sizeof(UINT32_T)]; + } contents; + } buf; + + // Attempt reading /dev/urandom. + if (fd == -1) + dev_urandom_state = FAIL; + else + { + buf.contents.number = 0; + if (read(fd, buf.contents.bytes, sizeof(UINT32_T)) + != sizeof(UINT32_T)) + dev_urandom_state = FAIL; + else + { + dev_urandom_state = OK; + *x = buf.contents.number; + } + close(fd); + } + } + if (dev_urandom_state != OK) +#endif + { + // Reading /dev/urandom doesn't work, fall back to: + // - randombytes_random() + // - reltime() or time() + // - XOR with process ID +#if defined(FEAT_SODIUM) + if (crypt_sodium_init() >= 0) + *x = crypt_sodium_randombytes_random(); + else +#endif + { +#if defined(FEAT_RELTIME) + proftime_T res; + profile_start(&res); +# if defined(MSWIN) + *x = (UINT32_T)res.LowPart; +# else + *x = (UINT32_T)res.tv_fsec; +# endif +#else + *x = vim_time(); +#endif + *x ^= mch_get_pid(); + } + } +} + +#define ROTL(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) +#define SPLITMIX32(x, z) ( \ + (z) = ((x) += 0x9e3779b9), \ + (z) = ((z) ^ ((z) >> 16)) * 0x85ebca6b, \ + (z) = ((z) ^ ((z) >> 13)) * 0xc2b2ae35, \ + (z) ^ ((z) >> 16) \ + ) +#define SHUFFLE_XOSHIRO128STARSTAR(x, y, z, w) \ + result = ROTL((y) * 5, 7) * 9; \ + t = (y) << 9; \ + (z) ^= (x); \ + (w) ^= (y); \ + (y) ^= (z), (x) ^= (w); \ + (z) ^= t; \ + (w) = ROTL(w, 11); + +/* + * "rand()" function + */ + static void +f_rand(typval_T *argvars, typval_T *rettv) +{ + list_T *l = NULL; + static UINT32_T gx, gy, gz, gw; + static int initialized = FALSE; + listitem_T *lx, *ly, *lz, *lw; + UINT32_T x = 0, y, z, w, t, result; + + if (in_vim9script() && check_for_opt_list_arg(argvars, 0) == FAIL) + return; + + if (argvars[0].v_type == VAR_UNKNOWN) + { + // When no argument is given use the global seed list. + if (initialized == FALSE) + { + // Initialize the global seed list. + init_srand(&x); + + gx = SPLITMIX32(x, z); + gy = SPLITMIX32(x, z); + gz = SPLITMIX32(x, z); + gw = SPLITMIX32(x, z); + initialized = TRUE; + } + + SHUFFLE_XOSHIRO128STARSTAR(gx, gy, gz, gw); + } + else if (argvars[0].v_type == VAR_LIST) + { + l = argvars[0].vval.v_list; + if (l == NULL || list_len(l) != 4) + goto theend; + + lx = list_find(l, 0L); + ly = list_find(l, 1L); + lz = list_find(l, 2L); + lw = list_find(l, 3L); + if (lx->li_tv.v_type != VAR_NUMBER) goto theend; + if (ly->li_tv.v_type != VAR_NUMBER) goto theend; + if (lz->li_tv.v_type != VAR_NUMBER) goto theend; + if (lw->li_tv.v_type != VAR_NUMBER) goto theend; + x = (UINT32_T)lx->li_tv.vval.v_number; + y = (UINT32_T)ly->li_tv.vval.v_number; + z = (UINT32_T)lz->li_tv.vval.v_number; + w = (UINT32_T)lw->li_tv.vval.v_number; + + SHUFFLE_XOSHIRO128STARSTAR(x, y, z, w); + + lx->li_tv.vval.v_number = (varnumber_T)x; + ly->li_tv.vval.v_number = (varnumber_T)y; + lz->li_tv.vval.v_number = (varnumber_T)z; + lw->li_tv.vval.v_number = (varnumber_T)w; + } + else + goto theend; + + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = (varnumber_T)result; + return; + +theend: + semsg(_(e_invalid_argument_str), tv_get_string(&argvars[0])); + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = -1; +} + +/* + * "srand()" function + */ + static void +f_srand(typval_T *argvars, typval_T *rettv) +{ + UINT32_T x = 0, z; + + if (rettv_list_alloc(rettv) == FAIL) + return; + + if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL) + return; + + if (argvars[0].v_type == VAR_UNKNOWN) + { + init_srand(&x); + } + else + { + int error = FALSE; + + x = (UINT32_T)tv_get_number_chk(&argvars[0], &error); + if (error) + return; + } + + list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32(x, z)); + list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32(x, z)); + list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32(x, z)); + list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32(x, z)); +} + +#undef ROTL +#undef SPLITMIX32 +#undef SHUFFLE_XOSHIRO128STARSTAR + +/* + * "range()" function + */ + static void +f_range(typval_T *argvars, typval_T *rettv) +{ + varnumber_T start; + varnumber_T end; + varnumber_T stride = 1; + int error = FALSE; + + if (rettv_list_alloc(rettv) == FAIL) + return; + + if (in_vim9script() + && (check_for_number_arg(argvars, 0) == FAIL + || check_for_opt_number_arg(argvars, 1) == FAIL + || (argvars[1].v_type != VAR_UNKNOWN + && check_for_opt_number_arg(argvars, 2) == FAIL))) + return; + + start = tv_get_number_chk(&argvars[0], &error); + if (argvars[1].v_type == VAR_UNKNOWN) + { + end = start - 1; + start = 0; + } + else + { + end = tv_get_number_chk(&argvars[1], &error); + if (argvars[2].v_type != VAR_UNKNOWN) + stride = tv_get_number_chk(&argvars[2], &error); + } + + if (error) + return; // type error; errmsg already given + if (stride == 0) + { + emsg(_(e_stride_is_zero)); + return; + } + if (stride > 0 ? end + 1 < start : end - 1 > start) + { + emsg(_(e_start_past_end)); + return; + } + + list_T *list = rettv->vval.v_list; + + // Create a non-materialized list. This is much more efficient and + // works with ":for". If used otherwise CHECK_LIST_MATERIALIZE() must + // be called. + list->lv_first = &range_list_item; + list->lv_u.nonmat.lv_start = start; + list->lv_u.nonmat.lv_end = end; + list->lv_u.nonmat.lv_stride = stride; + if (stride > 0 ? end < start : end > start) + list->lv_len = 0; + else + list->lv_len = (end - start) / stride + 1; +} + +/* + * Materialize "list". + * Do not call directly, use CHECK_LIST_MATERIALIZE() + */ + void +range_list_materialize(list_T *list) +{ + varnumber_T start = list->lv_u.nonmat.lv_start; + varnumber_T end = list->lv_u.nonmat.lv_end; + int stride = list->lv_u.nonmat.lv_stride; + varnumber_T i; + + list->lv_first = NULL; + list->lv_u.mat.lv_last = NULL; + list->lv_len = 0; + list->lv_u.mat.lv_idx_item = NULL; + for (i = start; stride > 0 ? i <= end : i >= end; i += stride) + { + if (list_append_number(list, i) == FAIL) + break; + if (list->lv_lock & VAR_ITEMS_LOCKED) + list->lv_u.mat.lv_last->li_tv.v_lock = VAR_LOCKED; + } + list->lv_lock &= ~VAR_ITEMS_LOCKED; +} + +/* + * "getreginfo()" function + */ + static void +f_getreginfo(typval_T *argvars, typval_T *rettv) +{ + int regname; + char_u buf[NUMBUFLEN + 2]; + long reglen = 0; + dict_T *dict; + list_T *list; + + if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL) + return; + + regname = getreg_get_regname(argvars); + if (regname == 0) + return; + + if (regname == '@') + regname = '"'; + + if (rettv_dict_alloc(rettv) == FAIL) + return; + dict = rettv->vval.v_dict; + + list = (list_T *)get_reg_contents(regname, GREG_EXPR_SRC | GREG_LIST); + if (list == NULL) + return; + (void)dict_add_list(dict, "regcontents", list); + + buf[0] = NUL; + buf[1] = NUL; + switch (get_reg_type(regname, ®len)) + { + case MLINE: buf[0] = 'V'; break; + case MCHAR: buf[0] = 'v'; break; + case MBLOCK: + vim_snprintf((char *)buf, sizeof(buf), "%c%ld", Ctrl_V, + reglen + 1); + break; + } + (void)dict_add_string(dict, (char *)"regtype", buf); + + buf[0] = get_register_name(get_unname_register()); + buf[1] = NUL; + if (regname == '"') + (void)dict_add_string(dict, (char *)"points_to", buf); + else + { + dictitem_T *item = dictitem_alloc((char_u *)"isunnamed"); + + if (item != NULL) + { + item->di_tv.v_type = VAR_BOOL; + item->di_tv.vval.v_number = regname == buf[0] + ? VVAL_TRUE : VVAL_FALSE; + (void)dict_add(dict, item); + } + } +} + + static void +return_register(int regname, typval_T *rettv) +{ + char_u buf[2] = {0, 0}; + + buf[0] = (char_u)regname; + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave(buf); +} + +/* + * "reg_executing()" function + */ + static void +f_reg_executing(typval_T *argvars UNUSED, typval_T *rettv) +{ + return_register(reg_executing, rettv); +} + +/* + * "reg_recording()" function + */ + static void +f_reg_recording(typval_T *argvars UNUSED, typval_T *rettv) +{ + return_register(reg_recording, rettv); +} + +/* + * "rename({from}, {to})" function + */ + static void +f_rename(typval_T *argvars, typval_T *rettv) +{ + char_u buf[NUMBUFLEN]; + + rettv->vval.v_number = -1; + if (check_restricted() || check_secure()) + return; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_string_arg(argvars, 1) == FAIL)) + return; + + rettv->vval.v_number = vim_rename(tv_get_string(&argvars[0]), + tv_get_string_buf(&argvars[1], buf)); +} + +/* + * "repeat()" function + */ + static void +f_repeat(typval_T *argvars, typval_T *rettv) +{ + char_u *p; + varnumber_T n; + int slen; + int len; + char_u *r; + int i; + + if (in_vim9script() + && (check_for_string_or_number_or_list_or_blob_arg(argvars, 0) + == FAIL + || check_for_number_arg(argvars, 1) == FAIL)) + return; + + n = tv_get_number(&argvars[1]); + if (argvars[0].v_type == VAR_LIST) + { + if (rettv_list_alloc(rettv) == OK && argvars[0].vval.v_list != NULL) + while (n-- > 0) + if (list_extend(rettv->vval.v_list, + argvars[0].vval.v_list, NULL) == FAIL) + break; + } + else if (argvars[0].v_type == VAR_BLOB) + { + if (rettv_blob_alloc(rettv) == FAIL + || argvars[0].vval.v_blob == NULL + || n <= 0) + return; + + slen = argvars[0].vval.v_blob->bv_ga.ga_len; + len = (int)slen * n; + if (len <= 0) + return; + + if (ga_grow(&rettv->vval.v_blob->bv_ga, len) == FAIL) + return; + + rettv->vval.v_blob->bv_ga.ga_len = len; + + for (i = 0; i < slen; ++i) + if (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) + blob_set_range(rettv->vval.v_blob, + (long)i * slen, ((long)i + 1) * slen - 1, argvars); + } + else + { + p = tv_get_string(&argvars[0]); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + slen = (int)STRLEN(p); + len = slen * n; + if (len <= 0) + return; + + r = alloc(len + 1); + if (r != NULL) + { + for (i = 0; i < n; i++) + mch_memmove(r + i * slen, p, (size_t)slen); + r[len] = NUL; + } + + rettv->vval.v_string = r; + } +} + +#define SP_NOMOVE 0x01 // don't move cursor +#define SP_REPEAT 0x02 // repeat to find outer pair +#define SP_RETCOUNT 0x04 // return matchcount +#define SP_SETPCMARK 0x08 // set previous context mark +#define SP_START 0x10 // accept match at start position +#define SP_SUBPAT 0x20 // return nr of matching sub-pattern +#define SP_END 0x40 // leave cursor at end of match +#define SP_COLUMN 0x80 // start at cursor column + +/* + * Get flags for a search function. + * Possibly sets "p_ws". + * Returns BACKWARD, FORWARD or zero (for an error). + */ + static int +get_search_arg(typval_T *varp, int *flagsp) +{ + int dir = FORWARD; + char_u *flags; + char_u nbuf[NUMBUFLEN]; + int mask; + + if (varp->v_type == VAR_UNKNOWN) + return FORWARD; + + flags = tv_get_string_buf_chk(varp, nbuf); + if (flags == NULL) + return 0; // type error; errmsg already given + while (*flags != NUL) + { + switch (*flags) + { + case 'b': dir = BACKWARD; break; + case 'w': p_ws = TRUE; break; + case 'W': p_ws = FALSE; break; + default: mask = 0; + if (flagsp != NULL) + switch (*flags) + { + case 'c': mask = SP_START; break; + case 'e': mask = SP_END; break; + case 'm': mask = SP_RETCOUNT; break; + case 'n': mask = SP_NOMOVE; break; + case 'p': mask = SP_SUBPAT; break; + case 'r': mask = SP_REPEAT; break; + case 's': mask = SP_SETPCMARK; break; + case 'z': mask = SP_COLUMN; break; + } + if (mask == 0) + { + semsg(_(e_invalid_argument_str), flags); + dir = 0; + } + else + *flagsp |= mask; + } + if (dir == 0) + break; + ++flags; + } + return dir; +} + +/* + * Shared by search() and searchpos() functions. + */ + static int +search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) +{ + int flags; + char_u *pat; + pos_T pos; + pos_T save_cursor; + int save_p_ws = p_ws; + int dir; + int retval = 0; // default: FAIL + long lnum_stop = 0; +#ifdef FEAT_RELTIME + long time_limit = 0; +#endif + int options = SEARCH_KEEP; + int subpatnum; + searchit_arg_T sia; + int use_skip = FALSE; + pos_T firstpos; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_opt_string_arg(argvars, 1) == FAIL + || (argvars[1].v_type != VAR_UNKNOWN + && (check_for_opt_number_arg(argvars, 2) == FAIL + || (argvars[2].v_type != VAR_UNKNOWN + && check_for_opt_number_arg(argvars, 3) == FAIL))))) + goto theend; + + pat = tv_get_string(&argvars[0]); + dir = get_search_arg(&argvars[1], flagsp); // may set p_ws + if (dir == 0) + goto theend; + flags = *flagsp; + if (flags & SP_START) + options |= SEARCH_START; + if (flags & SP_END) + options |= SEARCH_END; + if (flags & SP_COLUMN) + options |= SEARCH_COL; + + // 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 = (long)tv_get_number_chk(&argvars[2], NULL); + if (lnum_stop < 0) + goto theend; + if (argvars[3].v_type != VAR_UNKNOWN) + { +#ifdef FEAT_RELTIME + time_limit = (long)tv_get_number_chk(&argvars[3], NULL); + if (time_limit < 0) + goto theend; +#endif + use_skip = eval_expr_valid_arg(&argvars[4]); + } + } + + /* + * This function does not accept SP_REPEAT and SP_RETCOUNT flags. + * Check to make sure only those flags are set. + * Also, Only the SP_NOMOVE or the SP_SETPCMARK flag can be set. Both + * flags cannot be set. Check for that condition also. + */ + if (((flags & (SP_REPEAT | SP_RETCOUNT)) != 0) + || ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) + { + semsg(_(e_invalid_argument_str), tv_get_string(&argvars[1])); + goto theend; + } + + pos = save_cursor = curwin->w_cursor; + CLEAR_FIELD(firstpos); + CLEAR_FIELD(sia); + sia.sa_stop_lnum = (linenr_T)lnum_stop; +#ifdef FEAT_RELTIME + sia.sa_tm = time_limit; +#endif + + // Repeat until {skip} returns FALSE. + for (;;) + { + subpatnum = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1L, + options, RE_SEARCH, &sia); + // finding the first match again means there is no match where {skip} + // evaluates to zero. + if (firstpos.lnum != 0 && EQUAL_POS(pos, firstpos)) + subpatnum = FAIL; + + if (subpatnum == FAIL || !use_skip) + // didn't find it or no skip argument + break; + if (firstpos.lnum == 0) + firstpos = pos; + + // If the skip expression matches, ignore this match. + { + int do_skip; + int err; + pos_T save_pos = curwin->w_cursor; + + curwin->w_cursor = pos; + err = FALSE; + do_skip = eval_expr_to_bool(&argvars[4], &err); + curwin->w_cursor = save_pos; + if (err) + { + // Evaluating {skip} caused an error, break here. + subpatnum = FAIL; + break; + } + if (!do_skip) + break; + } + + // clear the start flag to avoid getting stuck here + options &= ~SEARCH_START; + } + + if (subpatnum != FAIL) + { + if (flags & SP_SUBPAT) + retval = subpatnum; + else + retval = pos.lnum; + if (flags & SP_SETPCMARK) + setpcmark(); + curwin->w_cursor = pos; + if (match_pos != NULL) + { + // Store the match cursor position + match_pos->lnum = pos.lnum; + match_pos->col = pos.col + 1; + } + // "/$" will put the cursor after the end of the line, may need to + // correct that here + check_cursor(); + } + + // If 'n' flag is used: restore cursor position. + if (flags & SP_NOMOVE) + curwin->w_cursor = save_cursor; + else + curwin->w_set_curswant = TRUE; +theend: + p_ws = save_p_ws; + + return retval; +} + +#ifdef FEAT_RUBY +/* + * "rubyeval()" function + */ + static void +f_rubyeval(typval_T *argvars, typval_T *rettv) +{ + char_u *str; + char_u buf[NUMBUFLEN]; + + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + str = tv_get_string_buf(&argvars[0], buf); + do_rubyeval(str, rettv); +} +#endif + +/* + * "screenattr()" function + */ + static void +f_screenattr(typval_T *argvars, typval_T *rettv) +{ + int row; + int col; + int c; + + if (in_vim9script() + && (check_for_number_arg(argvars, 0) == FAIL + || check_for_number_arg(argvars, 1) == FAIL)) + return; + + row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; + if (row < 0 || row >= screen_Rows + || col < 0 || col >= screen_Columns) + c = -1; + else + c = ScreenAttrs[LineOffset[row] + col]; + rettv->vval.v_number = c; +} + +/* + * "screenchar()" function + */ + static void +f_screenchar(typval_T *argvars, typval_T *rettv) +{ + int row; + int col; + int c; + + if (in_vim9script() + && (check_for_number_arg(argvars, 0) == FAIL + || check_for_number_arg(argvars, 1) == FAIL)) + return; + + row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; + if (row < 0 || row >= screen_Rows || col < 0 || col >= screen_Columns) + c = -1; + else + { + char_u buf[MB_MAXBYTES + 1]; + screen_getbytes(row, col, buf, NULL); + c = (*mb_ptr2char)(buf); + } + rettv->vval.v_number = c; +} + +/* + * "screenchars()" function + */ + static void +f_screenchars(typval_T *argvars, typval_T *rettv) +{ + int row; + int col; + int c; + int i; + + if (rettv_list_alloc(rettv) == FAIL) + return; + + if (in_vim9script() + && (check_for_number_arg(argvars, 0) == FAIL + || check_for_number_arg(argvars, 1) == FAIL)) + return; + + row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; + if (row < 0 || row >= screen_Rows || col < 0 || col >= screen_Columns) + return; + + char_u buf[MB_MAXBYTES + 1]; + screen_getbytes(row, col, buf, NULL); + int pcc[MAX_MCO]; + if (enc_utf8) + c = utfc_ptr2char(buf, pcc); + else + c = (*mb_ptr2char)(buf); + list_append_number(rettv->vval.v_list, (varnumber_T)c); + + if (enc_utf8) + for (i = 0; i < Screen_mco && pcc[i] != 0; ++i) + list_append_number(rettv->vval.v_list, (varnumber_T)pcc[i]); +} + +/* + * "screencol()" function + * + * First column is 1 to be consistent with virtcol(). + */ + static void +f_screencol(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = screen_screencol() + 1; +} + +/* + * "screenrow()" function + */ + static void +f_screenrow(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = screen_screenrow() + 1; +} + +/* + * "screenstring()" function + */ + static void +f_screenstring(typval_T *argvars, typval_T *rettv) +{ + int row; + int col; + char_u buf[MB_MAXBYTES + 1]; + + rettv->vval.v_string = NULL; + rettv->v_type = VAR_STRING; + + if (in_vim9script() + && (check_for_number_arg(argvars, 0) == FAIL + || check_for_number_arg(argvars, 1) == FAIL)) + return; + + row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; + if (row < 0 || row >= screen_Rows || col < 0 || col >= screen_Columns) + return; + + screen_getbytes(row, col, buf, NULL); + rettv->vval.v_string = vim_strsave(buf); +} + +/* + * "search()" function + */ + static void +f_search(typval_T *argvars, typval_T *rettv) +{ + int flags = 0; + + rettv->vval.v_number = search_cmn(argvars, NULL, &flags); +} + +/* + * "searchdecl()" function + */ + static void +f_searchdecl(typval_T *argvars, typval_T *rettv) +{ + int locally = TRUE; + int thisblock = FALSE; + int error = FALSE; + char_u *name; + + rettv->vval.v_number = 1; // default: FAIL + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_opt_bool_arg(argvars, 1) == FAIL + || (argvars[1].v_type != VAR_UNKNOWN + && check_for_opt_bool_arg(argvars, 2) == FAIL))) + return; + + name = tv_get_string_chk(&argvars[0]); + if (argvars[1].v_type != VAR_UNKNOWN) + { + locally = !(int)tv_get_bool_chk(&argvars[1], &error); + if (!error && argvars[2].v_type != VAR_UNKNOWN) + thisblock = (int)tv_get_bool_chk(&argvars[2], &error); + } + if (!error && name != NULL) + rettv->vval.v_number = find_decl(name, (int)STRLEN(name), + locally, thisblock, SEARCH_KEEP) == FAIL; +} + +/* + * Used by searchpair() and searchpairpos() + */ + static int +searchpair_cmn(typval_T *argvars, pos_T *match_pos) +{ + char_u *spat, *mpat, *epat; + typval_T *skip; + int save_p_ws = p_ws; + int dir; + int flags = 0; + char_u nbuf1[NUMBUFLEN]; + char_u nbuf2[NUMBUFLEN]; + int retval = 0; // default: FAIL + long lnum_stop = 0; + long time_limit = 0; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_string_arg(argvars, 1) == FAIL + || check_for_string_arg(argvars, 2) == FAIL + || check_for_opt_string_arg(argvars, 3) == FAIL + || (argvars[3].v_type != VAR_UNKNOWN + && argvars[4].v_type != VAR_UNKNOWN + && (check_for_opt_number_arg(argvars, 5) == FAIL + || (argvars[5].v_type != VAR_UNKNOWN + && check_for_opt_number_arg(argvars, 6) == FAIL))))) + goto theend; + + // Get the three pattern arguments: start, middle, end. Will result in an + // error if not a valid argument. + spat = tv_get_string_chk(&argvars[0]); + mpat = tv_get_string_buf_chk(&argvars[1], nbuf1); + epat = tv_get_string_buf_chk(&argvars[2], nbuf2); + if (spat == NULL || mpat == NULL || epat == NULL) + goto theend; // type error + + // Handle the optional fourth argument: flags + dir = get_search_arg(&argvars[3], &flags); // may set p_ws + if (dir == 0) + goto theend; + + // Don't accept SP_END or SP_SUBPAT. + // Only one of the SP_NOMOVE or SP_SETPCMARK flags can be set. + if ((flags & (SP_END | SP_SUBPAT)) != 0 + || ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) + { + semsg(_(e_invalid_argument_str), tv_get_string(&argvars[3])); + goto theend; + } + + // Using 'r' implies 'W', otherwise it doesn't work. + if (flags & SP_REPEAT) + p_ws = FALSE; + + // Optional fifth argument: skip expression + if (argvars[3].v_type == VAR_UNKNOWN + || argvars[4].v_type == VAR_UNKNOWN) + skip = NULL; + else + { + // Type is checked later. + skip = &argvars[4]; + + if (argvars[5].v_type != VAR_UNKNOWN) + { + lnum_stop = (long)tv_get_number_chk(&argvars[5], NULL); + if (lnum_stop < 0) + { + semsg(_(e_invalid_argument_str), tv_get_string(&argvars[5])); + goto theend; + } +#ifdef FEAT_RELTIME + if (argvars[6].v_type != VAR_UNKNOWN) + { + time_limit = (long)tv_get_number_chk(&argvars[6], NULL); + if (time_limit < 0) + { + semsg(_(e_invalid_argument_str), tv_get_string(&argvars[6])); + goto theend; + } + } +#endif + } + } + + retval = do_searchpair(spat, mpat, epat, dir, skip, flags, + match_pos, lnum_stop, time_limit); + +theend: + p_ws = save_p_ws; + + return retval; +} + +/* + * "searchpair()" function + */ + static void +f_searchpair(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = searchpair_cmn(argvars, NULL); +} + +/* + * "searchpairpos()" function + */ + static void +f_searchpairpos(typval_T *argvars, typval_T *rettv) +{ + pos_T match_pos; + int lnum = 0; + int col = 0; + + if (rettv_list_alloc(rettv) == FAIL) + return; + + if (searchpair_cmn(argvars, &match_pos) > 0) + { + lnum = match_pos.lnum; + col = match_pos.col; + } + + list_append_number(rettv->vval.v_list, (varnumber_T)lnum); + list_append_number(rettv->vval.v_list, (varnumber_T)col); +} + +/* + * Search for a start/middle/end thing. + * Used by searchpair(), see its documentation for the details. + * Returns 0 or -1 for no match, + */ + long +do_searchpair( + char_u *spat, // start pattern + char_u *mpat, // middle pattern + char_u *epat, // end pattern + int dir, // BACKWARD or FORWARD + typval_T *skip, // skip expression + int flags, // SP_SETPCMARK and other SP_ values + pos_T *match_pos, + linenr_T lnum_stop, // stop at this line if not zero + long time_limit UNUSED) // stop after this many msec +{ + char_u *save_cpo; + char_u *pat, *pat2 = NULL, *pat3 = NULL; + long retval = 0; + pos_T pos; + pos_T firstpos; + pos_T foundpos; + pos_T save_cursor; + pos_T save_pos; + int n; + int r; + int nest = 1; + int use_skip = FALSE; + int err; + int options = SEARCH_KEEP; + + // Make 'cpoptions' empty, the 'l' flag should not be used here. + save_cpo = p_cpo; + p_cpo = empty_option; + + // Make two search patterns: start/end (pat2, for in nested pairs) and + // start/middle/end (pat3, for the top pair). + pat2 = alloc(STRLEN(spat) + STRLEN(epat) + 17); + pat3 = alloc(STRLEN(spat) + STRLEN(mpat) + STRLEN(epat) + 25); + if (pat2 == NULL || pat3 == NULL) + goto theend; + sprintf((char *)pat2, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat); + if (*mpat == NUL) + STRCPY(pat3, pat2); + else + sprintf((char *)pat3, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)", + spat, epat, mpat); + if (flags & SP_START) + options |= SEARCH_START; + + if (skip != NULL) + use_skip = eval_expr_valid_arg(skip); + +#ifdef FEAT_RELTIME + if (time_limit > 0) + init_regexp_timeout(time_limit); +#endif + save_cursor = curwin->w_cursor; + pos = curwin->w_cursor; + CLEAR_POS(&firstpos); + CLEAR_POS(&foundpos); + pat = pat3; + for (;;) + { + searchit_arg_T sia; + + CLEAR_FIELD(sia); + sia.sa_stop_lnum = lnum_stop; + n = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1L, + options, RE_SEARCH, &sia); + if (n == FAIL || (firstpos.lnum != 0 && EQUAL_POS(pos, firstpos))) + // didn't find it or found the first match again: FAIL + break; + + if (firstpos.lnum == 0) + firstpos = pos; + if (EQUAL_POS(pos, foundpos)) + { + // Found the same position again. Can happen with a pattern that + // has "\zs" at the end and searching backwards. Advance one + // character and try again. + if (dir == BACKWARD) + decl(&pos); + else + incl(&pos); + } + foundpos = pos; + + // clear the start flag to avoid getting stuck here + options &= ~SEARCH_START; + + // If the skip pattern matches, ignore this match. + if (use_skip) + { + save_pos = curwin->w_cursor; + curwin->w_cursor = pos; + err = FALSE; + r = eval_expr_to_bool(skip, &err); + curwin->w_cursor = save_pos; + if (err) + { + // Evaluating {skip} caused an error, break here. + curwin->w_cursor = save_cursor; + retval = -1; + break; + } + if (r) + continue; + } + + if ((dir == BACKWARD && n == 3) || (dir == FORWARD && n == 2)) + { + // Found end when searching backwards or start when searching + // forward: nested pair. + ++nest; + pat = pat2; // nested, don't search for middle + } + else + { + // Found end when searching forward or start when searching + // backward: end of (nested) pair; or found middle in outer pair. + if (--nest == 1) + pat = pat3; // outer level, search for middle + } + + if (nest == 0) + { + // Found the match: return matchcount or line number. + if (flags & SP_RETCOUNT) + ++retval; + else + retval = pos.lnum; + if (flags & SP_SETPCMARK) + setpcmark(); + curwin->w_cursor = pos; + if (!(flags & SP_REPEAT)) + break; + nest = 1; // search for next unmatched + } + } + + if (match_pos != NULL) + { + // Store the match cursor position + match_pos->lnum = curwin->w_cursor.lnum; + match_pos->col = curwin->w_cursor.col + 1; + } + + // If 'n' flag is used or search failed: restore cursor position. + if ((flags & SP_NOMOVE) || retval == 0) + curwin->w_cursor = save_cursor; + +theend: +#ifdef FEAT_RELTIME + if (time_limit > 0) + disable_regexp_timeout(); +#endif + vim_free(pat2); + vim_free(pat3); + if (p_cpo == empty_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((char_u *)"cpo", 0L, save_cpo, 0); + free_string_option(save_cpo); + } + + return retval; +} + +/* + * "searchpos()" function + */ + static void +f_searchpos(typval_T *argvars, typval_T *rettv) +{ + pos_T match_pos; + int lnum = 0; + int col = 0; + int n; + int flags = 0; + + if (rettv_list_alloc(rettv) == FAIL) + return; + + n = search_cmn(argvars, &match_pos, &flags); + if (n > 0) + { + lnum = match_pos.lnum; + col = match_pos.col; + } + + list_append_number(rettv->vval.v_list, (varnumber_T)lnum); + list_append_number(rettv->vval.v_list, (varnumber_T)col); + if (flags & SP_SUBPAT) + list_append_number(rettv->vval.v_list, (varnumber_T)n); +} + +/* + * Set the cursor or mark position. + * 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, int charpos) +{ + pos_T pos; + int fnum; + char_u *name; + colnr_T curswant = -1; + + rettv->vval.v_number = -1; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_list_arg(argvars, 1) == FAIL)) + return; + + name = tv_get_string_chk(argvars); + if (name == NULL) + return; + + if (list2fpos(&argvars[1], &pos, &fnum, &curswant, charpos) != OK) + return; + + if (pos.col != MAXCOL && --pos.col < 0) + pos.col = 0; + if ((name[0] == '.' && name[1] == NUL)) + { + // set cursor; "fnum" is ignored + curwin->w_cursor = pos; + if (curswant >= 0) + { + curwin->w_curswant = curswant - 1; + curwin->w_set_curswant = FALSE; + } + check_cursor(); + rettv->vval.v_number = 0; + } + else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) + { + // set mark + if (setmark_pos(name[1], &pos, fnum) == OK) + rettv->vval.v_number = 0; + } + else + emsg(_(e_invalid_argument)); +} +/* + * "setcharpos()" function + */ + static void +f_setcharpos(typval_T *argvars, typval_T *rettv) +{ + set_position(argvars, rettv, TRUE); +} + + static void +f_setcharsearch(typval_T *argvars, typval_T *rettv UNUSED) +{ + dict_T *d; + dictitem_T *di; + char_u *csearch; + + if (check_for_dict_arg(argvars, 0) == FAIL) + return; + + if ((d = argvars[0].vval.v_dict) == NULL) + return; + + csearch = dict_get_string(d, "char", FALSE); + if (csearch != NULL) + { + if (enc_utf8) + { + int pcc[MAX_MCO]; + int c = utfc_ptr2char(csearch, pcc); + + set_last_csearch(c, csearch, utfc_ptr2len(csearch)); + } + else + set_last_csearch(PTR2CHAR(csearch), + csearch, mb_ptr2len(csearch)); + } + + di = dict_find(d, (char_u *)"forward", -1); + if (di != NULL) + set_csearch_direction((int)tv_get_number(&di->di_tv) + ? FORWARD : BACKWARD); + + di = dict_find(d, (char_u *)"until", -1); + if (di != NULL) + set_csearch_until(!!tv_get_number(&di->di_tv)); +} + +/* + * "setcursorcharpos" function + */ + static void +f_setcursorcharpos(typval_T *argvars, typval_T *rettv) +{ + set_cursorpos(argvars, rettv, TRUE); +} + +/* + * "setenv()" function + */ + static void +f_setenv(typval_T *argvars, typval_T *rettv UNUSED) +{ + char_u namebuf[NUMBUFLEN]; + char_u valbuf[NUMBUFLEN]; + char_u *name; + + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + // 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_restricted() || check_secure()) + return; + + name = tv_get_string_buf(&argvars[0], namebuf); + if (argvars[1].v_type == VAR_SPECIAL + && argvars[1].vval.v_number == VVAL_NULL) + vim_unsetenv_ext(name); + else + vim_setenv_ext(name, tv_get_string_buf(&argvars[1], valbuf)); +} + +/* + * "setfperm({fname}, {mode})" function + */ + static void +f_setfperm(typval_T *argvars, typval_T *rettv) +{ + char_u *fname; + char_u modebuf[NUMBUFLEN]; + char_u *mode_str; + int i; + int mask; + int mode = 0; + + rettv->vval.v_number = 0; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_string_arg(argvars, 1) == FAIL)) + return; + + fname = tv_get_string_chk(&argvars[0]); + if (fname == NULL) + return; + mode_str = tv_get_string_buf_chk(&argvars[1], modebuf); + if (mode_str == NULL) + return; + if (STRLEN(mode_str) != 9) + { + semsg(_(e_invalid_argument_str), mode_str); + return; + } + + mask = 1; + for (i = 8; i >= 0; --i) + { + if (mode_str[i] != '-') + mode |= mask; + mask = mask << 1; + } + rettv->vval.v_number = mch_setperm(fname, mode) == OK; +} + +/* + * "setpos()" function + */ + static void +f_setpos(typval_T *argvars, typval_T *rettv) +{ + set_position(argvars, rettv, FALSE); +} + +/* + * Translate a register type string to the yank type and block length + */ + static int +get_yank_type(char_u **pp, char_u *yank_type, long *block_len) +{ + char_u *stropt = *pp; + switch (*stropt) + { + case 'v': case 'c': // character-wise selection + *yank_type = MCHAR; + break; + case 'V': case 'l': // line-wise selection + *yank_type = MLINE; + break; + case 'b': case Ctrl_V: // block-wise selection + *yank_type = MBLOCK; + if (VIM_ISDIGIT(stropt[1])) + { + ++stropt; + *block_len = getdigits(&stropt) - 1; + --stropt; + } + break; + default: + return FAIL; + } + *pp = stropt; + return OK; +} + +/* + * "setreg()" function + */ + static void +f_setreg(typval_T *argvars, typval_T *rettv) +{ + int regname; + char_u *strregname; + char_u *stropt; + char_u *strval; + int append; + char_u yank_type; + long block_len; + typval_T *regcontents; + int pointreg; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_opt_string_arg(argvars, 2) == FAIL)) + return; + + pointreg = 0; + regcontents = NULL; + block_len = -1; + yank_type = MAUTO; + append = FALSE; + + strregname = tv_get_string_chk(argvars); + rettv->vval.v_number = 1; // FAIL is default + + if (strregname == NULL) + return; // type error; errmsg already given + if (in_vim9script() && STRLEN(strregname) > 1) + { + semsg(_(e_register_name_must_be_one_char_str), strregname); + return; + } + regname = *strregname; + if (regname == 0 || regname == '@') + regname = '"'; + + if (argvars[1].v_type == VAR_DICT) + { + dict_T *d = argvars[1].vval.v_dict; + dictitem_T *di; + + if (d == NULL || d->dv_hashtab.ht_used == 0) + { + // Empty dict, clear the register (like setreg(0, [])) + char_u *lstval[2] = {NULL, NULL}; + write_reg_contents_lst(regname, lstval, 0, FALSE, MAUTO, -1); + return; + } + + di = dict_find(d, (char_u *)"regcontents", -1); + if (di != NULL) + regcontents = &di->di_tv; + + stropt = dict_get_string(d, "regtype", FALSE); + if (stropt != NULL) + { + int ret = get_yank_type(&stropt, &yank_type, &block_len); + + if (ret == FAIL || *++stropt != NUL) + { + semsg(_(e_invalid_value_for_argument_str), "value"); + return; + } + } + + if (regname == '"') + { + stropt = dict_get_string(d, "points_to", FALSE); + if (stropt != NULL) + { + pointreg = *stropt; + regname = pointreg; + } + } + else if (dict_get_bool(d, "isunnamed", -1) > 0) + pointreg = regname; + } + else + regcontents = &argvars[1]; + + if (argvars[2].v_type != VAR_UNKNOWN) + { + if (yank_type != MAUTO) + { + semsg(_(e_too_many_arguments_for_function_str), "setreg"); + return; + } + + stropt = tv_get_string_chk(&argvars[2]); + if (stropt == NULL) + return; // type error + for (; *stropt != NUL; ++stropt) + switch (*stropt) + { + case 'a': case 'A': // append + append = TRUE; + break; + default: + get_yank_type(&stropt, &yank_type, &block_len); + } + } + + if (regcontents && regcontents->v_type == VAR_LIST) + { + char_u **lstval; + char_u **allocval; + char_u buf[NUMBUFLEN]; + char_u **curval; + char_u **curallocval; + list_T *ll = regcontents->vval.v_list; + listitem_T *li; + int len; + + // If the list is NULL handle like an empty list. + len = ll == NULL ? 0 : ll->lv_len; + + // First half: use for pointers to result lines; second half: use for + // pointers to allocated copies. + lstval = ALLOC_MULT(char_u *, (len + 1) * 2); + if (lstval == NULL) + return; + curval = lstval; + allocval = lstval + len + 2; + curallocval = allocval; + + if (ll != NULL) + { + CHECK_LIST_MATERIALIZE(ll); + FOR_ALL_LIST_ITEMS(ll, li) + { + strval = tv_get_string_buf_chk(&li->li_tv, buf); + if (strval == NULL) + goto free_lstval; + if (strval == buf) + { + // Need to make a copy, next tv_get_string_buf_chk() will + // overwrite the string. + strval = vim_strsave(buf); + if (strval == NULL) + goto free_lstval; + *curallocval++ = strval; + } + *curval++ = strval; + } + } + *curval++ = NULL; + + write_reg_contents_lst(regname, lstval, -1, + append, yank_type, block_len); +free_lstval: + while (curallocval > allocval) + vim_free(*--curallocval); + vim_free(lstval); + } + else if (regcontents) + { + strval = tv_get_string_chk(regcontents); + if (strval == NULL) + return; + write_reg_contents_ex(regname, strval, -1, + append, yank_type, block_len); + } + if (pointreg != 0) + get_yank_register(pointreg, TRUE); + + rettv->vval.v_number = 0; +} + +/* + * "settagstack()" function + */ + static void +f_settagstack(typval_T *argvars, typval_T *rettv) +{ + win_T *wp; + dict_T *d; + int action = 'r'; + + rettv->vval.v_number = -1; + + if (in_vim9script() + && (check_for_number_arg(argvars, 0) == FAIL + || check_for_dict_arg(argvars, 1) == FAIL + || check_for_opt_string_arg(argvars, 2) == FAIL)) + return; + + // first argument: window number or id + wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) + return; + + // second argument: dict with items to set in the tag stack + if (check_for_dict_arg(argvars, 1) == FAIL) + return; + d = argvars[1].vval.v_dict; + if (d == NULL) + return; + + // 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 (check_for_string_arg(argvars, 2) == FAIL) + return; + else + { + char_u *actstr; + actstr = tv_get_string_chk(&argvars[2]); + if (actstr == NULL) + return; + if ((*actstr == 'r' || *actstr == 'a' || *actstr == 't') + && actstr[1] == NUL) + action = *actstr; + else + { + semsg(_(e_invalid_action_str_2), actstr); + return; + } + } + + if (set_tagstack(wp, d, action) == OK) + rettv->vval.v_number = 0; +} + +#ifdef FEAT_CRYPT +/* + * "sha256({string})" function + */ + static void +f_sha256(typval_T *argvars, typval_T *rettv) +{ + char_u *p; + + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + p = tv_get_string(&argvars[0]); + rettv->vval.v_string = vim_strsave( + sha256_bytes(p, (int)STRLEN(p), NULL, 0)); + rettv->v_type = VAR_STRING; +} +#endif // FEAT_CRYPT + +/* + * "shellescape({string})" function + */ + static void +f_shellescape(typval_T *argvars, typval_T *rettv) +{ + int do_special; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_opt_bool_arg(argvars, 1) == FAIL)) + return; + + do_special = non_zero_arg(&argvars[1]); + rettv->vval.v_string = vim_strsave_shellescape( + tv_get_string(&argvars[0]), do_special, do_special); + rettv->v_type = VAR_STRING; +} + +/* + * shiftwidth() function + */ + static void +f_shiftwidth(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = 0; + + if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL) + return; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + long col; + + col = (long)tv_get_number_chk(argvars, NULL); + if (col < 0) + return; // type error; errmsg already given +#ifdef FEAT_VARTABS + rettv->vval.v_number = get_sw_value_col(curbuf, col); + return; +#endif + } + + rettv->vval.v_number = get_sw_value(curbuf); +} + +/* + * "soundfold({word})" function + */ + static void +f_soundfold(typval_T *argvars, typval_T *rettv) +{ + char_u *s; + + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + rettv->v_type = VAR_STRING; + s = tv_get_string(&argvars[0]); +#ifdef FEAT_SPELL + rettv->vval.v_string = eval_soundfold(s); +#else + rettv->vval.v_string = vim_strsave(s); +#endif +} + +/* + * "spellbadword()" function + */ + static void +f_spellbadword(typval_T *argvars UNUSED, typval_T *rettv) +{ + char_u *word = (char_u *)""; + hlf_T attr = HLF_COUNT; + int len = 0; +#ifdef FEAT_SPELL + int wo_spell_save = curwin->w_p_spell; + + if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL) + return; + + if (!curwin->w_p_spell) + { + parse_spelllang(curwin); + curwin->w_p_spell = TRUE; + } + + if (*curwin->w_s->b_p_spl == NUL) + { + emsg(_(e_spell_checking_is_not_possible)); + curwin->w_p_spell = wo_spell_save; + return; + } +#endif + + if (rettv_list_alloc(rettv) == FAIL) + { +#ifdef FEAT_SPELL + curwin->w_p_spell = wo_spell_save; +#endif + return; + } + +#ifdef FEAT_SPELL + if (argvars[0].v_type == VAR_UNKNOWN) + { + // Find the start and length of the badly spelled word. + len = spell_move_to(curwin, FORWARD, TRUE, TRUE, &attr); + if (len != 0) + { + word = ml_get_cursor(); + curwin->w_set_curswant = TRUE; + } + } + else if (*curbuf->b_s.b_p_spl != NUL) + { + char_u *str = tv_get_string_chk(&argvars[0]); + int capcol = -1; + + if (str != NULL) + { + // Check the argument for spelling. + while (*str != NUL) + { + len = spell_check(curwin, str, &attr, &capcol, FALSE); + if (attr != HLF_COUNT) + { + word = str; + break; + } + str += len; + capcol -= len; + len = 0; + } + } + } + curwin->w_p_spell = wo_spell_save; +#endif + + list_append_string(rettv->vval.v_list, word, len); + list_append_string(rettv->vval.v_list, (char_u *)( + attr == HLF_SPB ? "bad" : + attr == HLF_SPR ? "rare" : + attr == HLF_SPL ? "local" : + attr == HLF_SPC ? "caps" : + ""), -1); +} + +/* + * "spellsuggest()" function + */ + static void +f_spellsuggest(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_SPELL + char_u *str; + int typeerr = FALSE; + int maxcount; + garray_T ga; + int i; + listitem_T *li; + int need_capital = FALSE; + int wo_spell_save = curwin->w_p_spell; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_opt_number_arg(argvars, 1) == FAIL + || (argvars[1].v_type != VAR_UNKNOWN + && check_for_opt_bool_arg(argvars, 2) == FAIL))) + return; + + if (!curwin->w_p_spell) + { + parse_spelllang(curwin); + curwin->w_p_spell = TRUE; + } + + if (*curwin->w_s->b_p_spl == NUL) + { + emsg(_(e_spell_checking_is_not_possible)); + curwin->w_p_spell = wo_spell_save; + return; + } +#endif + + if (rettv_list_alloc(rettv) == FAIL) + { +#ifdef FEAT_SPELL + curwin->w_p_spell = wo_spell_save; +#endif + return; + } + +#ifdef FEAT_SPELL + str = tv_get_string(&argvars[0]); + if (argvars[1].v_type != VAR_UNKNOWN) + { + maxcount = (int)tv_get_number_chk(&argvars[1], &typeerr); + if (maxcount <= 0) + return; + if (argvars[2].v_type != VAR_UNKNOWN) + { + need_capital = (int)tv_get_bool_chk(&argvars[2], &typeerr); + if (typeerr) + return; + } + } + else + maxcount = 25; + + spell_suggest_list(&ga, str, maxcount, need_capital, FALSE); + + for (i = 0; i < ga.ga_len; ++i) + { + str = ((char_u **)ga.ga_data)[i]; + + li = listitem_alloc(); + if (li == NULL) + vim_free(str); + else + { + li->li_tv.v_type = VAR_STRING; + li->li_tv.v_lock = 0; + li->li_tv.vval.v_string = str; + list_append(rettv->vval.v_list, li); + } + } + ga_clear(&ga); + curwin->w_p_spell = wo_spell_save; +#endif +} + + static void +f_split(typval_T *argvars, typval_T *rettv) +{ + char_u *str; + char_u *end; + char_u *pat = NULL; + regmatch_T regmatch; + char_u patbuf[NUMBUFLEN]; + char_u *save_cpo; + int match; + colnr_T col = 0; + int keepempty = FALSE; + int typeerr = FALSE; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_opt_string_arg(argvars, 1) == FAIL + || (argvars[1].v_type != VAR_UNKNOWN + && check_for_opt_bool_arg(argvars, 2) == FAIL))) + return; + + // Make 'cpoptions' empty, the 'l' flag should not be used here. + save_cpo = p_cpo; + p_cpo = empty_option; + + str = tv_get_string(&argvars[0]); + if (argvars[1].v_type != VAR_UNKNOWN) + { + pat = tv_get_string_buf_chk(&argvars[1], patbuf); + if (pat == NULL) + typeerr = TRUE; + if (argvars[2].v_type != VAR_UNKNOWN) + keepempty = (int)tv_get_bool_chk(&argvars[2], &typeerr); + } + if (pat == NULL || *pat == NUL) + pat = (char_u *)"[\\x01- ]\\+"; + + if (rettv_list_alloc(rettv) == FAIL) + goto theend; + if (typeerr) + goto theend; + + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog != NULL) + { + regmatch.rm_ic = FALSE; + while (*str != NUL || keepempty) + { + if (*str == NUL) + match = FALSE; // empty item at the end + else + match = vim_regexec_nl(®match, str, col); + if (match) + end = regmatch.startp[0]; + else + end = str + STRLEN(str); + if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0 + && *str != NUL && match && end < regmatch.endp[0])) + { + if (list_append_string(rettv->vval.v_list, str, + (int)(end - str)) == FAIL) + break; + } + if (!match) + break; + // Advance to just after the match. + if (regmatch.endp[0] > str) + col = 0; + else + // Don't get stuck at the same match. + col = (*mb_ptr2len)(regmatch.endp[0]); + str = regmatch.endp[0]; + } + + vim_regfree(regmatch.regprog); + } + +theend: + p_cpo = save_cpo; +} + +/* + * "submatch()" function + */ + static void +f_submatch(typval_T *argvars, typval_T *rettv) +{ + int error = FALSE; + int no; + int retList = 0; + + if (in_vim9script() + && (check_for_number_arg(argvars, 0) == FAIL + || check_for_opt_bool_arg(argvars, 1) == FAIL)) + return; + + no = (int)tv_get_number_chk(&argvars[0], &error); + if (error) + return; + if (no < 0 || no >= NSUBEXP) + { + semsg(_(e_invalid_submatch_number_nr), no); + return; + } + if (argvars[1].v_type != VAR_UNKNOWN) + retList = (int)tv_get_bool_chk(&argvars[1], &error); + if (error) + return; + + if (retList == 0) + { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = reg_submatch(no); + } + else + { + rettv->v_type = VAR_LIST; + rettv->vval.v_list = reg_submatch_list(no); + } +} + +/* + * "substitute()" function + */ + static void +f_substitute(typval_T *argvars, typval_T *rettv) +{ + char_u patbuf[NUMBUFLEN]; + char_u subbuf[NUMBUFLEN]; + char_u flagsbuf[NUMBUFLEN]; + char_u *str; + char_u *pat; + char_u *sub = NULL; + typval_T *expr = NULL; + char_u *flg; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_string_arg(argvars, 1) == FAIL + || check_for_string_arg(argvars, 3) == FAIL)) + return; + + str = tv_get_string_chk(&argvars[0]); + pat = tv_get_string_buf_chk(&argvars[1], patbuf); + flg = tv_get_string_buf_chk(&argvars[3], flagsbuf); + + if (argvars[2].v_type == VAR_FUNC + || argvars[2].v_type == VAR_PARTIAL + || argvars[2].v_type == VAR_INSTR + || argvars[2].v_type == VAR_CLASS + || argvars[2].v_type == VAR_OBJECT) + expr = &argvars[2]; + else + sub = tv_get_string_buf_chk(&argvars[2], subbuf); + + rettv->v_type = VAR_STRING; + if (str == NULL || pat == NULL || (sub == NULL && expr == NULL) + || flg == NULL) + rettv->vval.v_string = NULL; + else + rettv->vval.v_string = do_string_sub(str, pat, sub, expr, flg); +} + +/* + * "swapfilelist()" function + */ + static void +f_swapfilelist(typval_T *argvars UNUSED, typval_T *rettv) +{ + if (rettv_list_alloc(rettv) == FAIL) + return; + recover_names(NULL, FALSE, rettv->vval.v_list, 0, NULL); +} + +/* + * "swapinfo(swap_filename)" function + */ + static void +f_swapinfo(typval_T *argvars, typval_T *rettv) +{ + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + + if (rettv_dict_alloc(rettv) == OK) + get_b0_dict(tv_get_string(argvars), rettv->vval.v_dict); +} + +/* + * "swapname(expr)" function + */ + static void +f_swapname(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf; + + rettv->v_type = VAR_STRING; + + if (in_vim9script() && check_for_buffer_arg(argvars, 0) == FAIL) + return; + + buf = tv_get_buf(&argvars[0], FALSE); + if (buf == NULL || buf->b_ml.ml_mfp == NULL + || buf->b_ml.ml_mfp->mf_fname == NULL) + rettv->vval.v_string = NULL; + else + rettv->vval.v_string = vim_strsave(buf->b_ml.ml_mfp->mf_fname); +} + +/* + * "synID(lnum, col, trans)" function + */ + static void +f_synID(typval_T *argvars UNUSED, typval_T *rettv) +{ + int id = 0; +#ifdef FEAT_SYN_HL + linenr_T lnum; + colnr_T col; + int trans; + int transerr = FALSE; + + if (in_vim9script() + && (check_for_lnum_arg(argvars, 0) == FAIL + || check_for_number_arg(argvars, 1) == FAIL + || check_for_bool_arg(argvars, 2) == FAIL)) + return; + + lnum = tv_get_lnum(argvars); // -1 on type error + col = (linenr_T)tv_get_number(&argvars[1]) - 1; // -1 on type error + trans = (int)tv_get_bool_chk(&argvars[2], &transerr); + + if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count + && col >= 0 && col < (long)STRLEN(ml_get(lnum))) + id = syn_get_id(curwin, lnum, col, trans, NULL, FALSE); +#endif + + rettv->vval.v_number = id; +} + +/* + * "synIDattr(id, what [, mode])" function + */ + static void +f_synIDattr(typval_T *argvars UNUSED, typval_T *rettv) +{ + char_u *p = NULL; +#ifdef FEAT_SYN_HL + int id; + char_u *what; + char_u *mode; + char_u modebuf[NUMBUFLEN]; + int modec; + + if (in_vim9script() + && (check_for_number_arg(argvars, 0) == FAIL + || check_for_string_arg(argvars, 1) == FAIL + || check_for_opt_string_arg(argvars, 2) == FAIL)) + return; + + id = (int)tv_get_number(&argvars[0]); + what = tv_get_string(&argvars[1]); + if (argvars[2].v_type != VAR_UNKNOWN) + { + mode = tv_get_string_buf(&argvars[2], modebuf); + modec = TOLOWER_ASC(mode[0]); + if (modec != 't' && modec != 'c' && modec != 'g') + modec = 0; // replace invalid with current + } + else + { +#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) + if (USE_24BIT) + modec = 'g'; + else +#endif + if (t_colors > 1) + modec = 'c'; + else + modec = 't'; + } + + switch (TOLOWER_ASC(what[0])) + { + case 'b': + if (TOLOWER_ASC(what[1]) == 'g') // bg[#] + p = highlight_color(id, what, modec); + else // bold + p = highlight_has_attr(id, HL_BOLD, modec); + break; + + case 'f': // fg[#] or font + p = highlight_color(id, what, modec); + break; + + case 'i': + if (TOLOWER_ASC(what[1]) == 'n') // inverse + p = highlight_has_attr(id, HL_INVERSE, modec); + else // italic + p = highlight_has_attr(id, HL_ITALIC, modec); + break; + + case 'n': + if (TOLOWER_ASC(what[1]) == 'o') // nocombine + p = highlight_has_attr(id, HL_NOCOMBINE, modec); + else // name + p = get_highlight_name_ext(NULL, id - 1, FALSE); + break; + + case 'r': // reverse + p = highlight_has_attr(id, HL_INVERSE, modec); + break; + + case 's': + if (TOLOWER_ASC(what[1]) == 'p') // sp[#] + p = highlight_color(id, what, modec); + // strikeout + else if (TOLOWER_ASC(what[1]) == 't' && + TOLOWER_ASC(what[2]) == 'r') + p = highlight_has_attr(id, HL_STRIKETHROUGH, modec); + else // standout + p = highlight_has_attr(id, HL_STANDOUT, modec); + break; + + case 'u': + if (STRLEN(what) >= 9) + { + if (TOLOWER_ASC(what[5]) == 'l') + // underline + p = highlight_has_attr(id, HL_UNDERLINE, modec); + else if (TOLOWER_ASC(what[5]) != 'd') + // undercurl + p = highlight_has_attr(id, HL_UNDERCURL, modec); + else if (TOLOWER_ASC(what[6]) != 'o') + // underdashed + p = highlight_has_attr(id, HL_UNDERDASHED, modec); + else if (TOLOWER_ASC(what[7]) == 'u') + // underdouble + p = highlight_has_attr(id, HL_UNDERDOUBLE, modec); + else + // underdotted + p = highlight_has_attr(id, HL_UNDERDOTTED, modec); + } + else + // ul + p = highlight_color(id, what, modec); + break; + } + + if (p != NULL) + p = vim_strsave(p); +#endif + rettv->v_type = VAR_STRING; + rettv->vval.v_string = p; +} + +/* + * "synIDtrans(id)" function + */ + static void +f_synIDtrans(typval_T *argvars UNUSED, typval_T *rettv) +{ + int id; + +#ifdef FEAT_SYN_HL + if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL) + return; + + id = (int)tv_get_number(&argvars[0]); + + if (id > 0) + id = syn_get_final_id(id); + else +#endif + id = 0; + + rettv->vval.v_number = id; +} + +/* + * "synconcealed(lnum, col)" function + */ + static void +f_synconcealed(typval_T *argvars UNUSED, typval_T *rettv) +{ +#if defined(FEAT_SYN_HL) && defined(FEAT_CONCEAL) + linenr_T lnum; + colnr_T col; + int syntax_flags = 0; + int cchar; + int matchid = 0; + char_u str[NUMBUFLEN]; +#endif + + rettv_list_set(rettv, NULL); + + if (in_vim9script() + && (check_for_lnum_arg(argvars, 0) == FAIL + || check_for_number_arg(argvars, 1) == FAIL)) + return; + +#if defined(FEAT_SYN_HL) && defined(FEAT_CONCEAL) + lnum = tv_get_lnum(argvars); // -1 on type error + col = (colnr_T)tv_get_number(&argvars[1]) - 1; // -1 on type error + + CLEAR_FIELD(str); + + if (rettv_list_alloc(rettv) == OK) + { + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count + && col >= 0 && col <= (long)STRLEN(ml_get(lnum)) + && curwin->w_p_cole > 0) + { + (void)syn_get_id(curwin, lnum, col, FALSE, NULL, FALSE); + syntax_flags = get_syntax_info(&matchid); + + // get the conceal character + if ((syntax_flags & HL_CONCEAL) && curwin->w_p_cole < 3) + { + cchar = syn_get_sub_char(); + if (cchar == NUL && curwin->w_p_cole == 1) + cchar = (curwin->w_lcs_chars.conceal == NUL) ? ' ' + : curwin->w_lcs_chars.conceal; + if (cchar != NUL) + { + if (has_mbyte) + (*mb_char2bytes)(cchar, str); + else + str[0] = cchar; + } + } + } + + list_append_number(rettv->vval.v_list, + (syntax_flags & HL_CONCEAL) != 0); + // -1 to auto-determine strlen + list_append_string(rettv->vval.v_list, str, -1); + list_append_number(rettv->vval.v_list, matchid); + } +#endif +} + +/* + * "synstack(lnum, col)" function + */ + static void +f_synstack(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_SYN_HL + linenr_T lnum; + colnr_T col; + int i; + int id; +#endif + + rettv_list_set(rettv, NULL); + + if (in_vim9script() + && (check_for_lnum_arg(argvars, 0) == FAIL + || check_for_number_arg(argvars, 1) == FAIL)) + return; + +#ifdef FEAT_SYN_HL + lnum = tv_get_lnum(argvars); // -1 on type error + col = (colnr_T)tv_get_number(&argvars[1]) - 1; // -1 on type error + + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count + && col >= 0 && col <= (long)STRLEN(ml_get(lnum)) + && rettv_list_alloc(rettv) == OK) + { + (void)syn_get_id(curwin, lnum, col, FALSE, NULL, TRUE); + for (i = 0; ; ++i) + { + id = syn_get_stack_item(i); + if (id < 0) + break; + if (list_append_number(rettv->vval.v_list, id) == FAIL) + break; + } + } +#endif +} + +/* + * "tabpagebuflist()" function + */ + static void +f_tabpagebuflist(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ + tabpage_T *tp; + win_T *wp = NULL; + + if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL) + return; + + if (argvars[0].v_type == VAR_UNKNOWN) + wp = firstwin; + else + { + tp = find_tabpage((int)tv_get_number(&argvars[0])); + if (tp != NULL) + wp = (tp == curtab) ? firstwin : tp->tp_firstwin; + } + if (wp != NULL && rettv_list_alloc(rettv) == OK) + { + for (; wp != NULL; wp = wp->w_next) + if (list_append_number(rettv->vval.v_list, + wp->w_buffer->b_fnum) == FAIL) + break; + } +} + +/* + * "tagfiles()" function + */ + static void +f_tagfiles(typval_T *argvars UNUSED, typval_T *rettv) +{ + char_u *fname; + tagname_T tn; + int first; + + if (rettv_list_alloc(rettv) == FAIL) + return; + fname = alloc(MAXPATHL); + if (fname == NULL) + return; + + for (first = TRUE; ; first = FALSE) + if (get_tagfname(&tn, first, fname) == FAIL + || list_append_string(rettv->vval.v_list, fname, -1) == FAIL) + break; + tagname_free(&tn); + vim_free(fname); +} + +/* + * "taglist()" function + */ + static void +f_taglist(typval_T *argvars, typval_T *rettv) +{ + char_u *fname = NULL; + char_u *tag_pattern; + + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_opt_string_arg(argvars, 1) == FAIL)) + return; + + tag_pattern = tv_get_string(&argvars[0]); + + rettv->vval.v_number = FALSE; + if (*tag_pattern == NUL) + return; + + if (argvars[1].v_type != VAR_UNKNOWN) + fname = tv_get_string(&argvars[1]); + if (rettv_list_alloc(rettv) == OK) + (void)get_tags(rettv->vval.v_list, tag_pattern, fname); +} + +/* + * "type(expr)" function + */ + static void +f_type(typval_T *argvars, typval_T *rettv) +{ + int n = -1; + + switch (argvars[0].v_type) + { + case VAR_NUMBER: n = VAR_TYPE_NUMBER; break; + case VAR_STRING: n = VAR_TYPE_STRING; break; + case VAR_PARTIAL: + case VAR_FUNC: n = VAR_TYPE_FUNC; break; + case VAR_LIST: n = VAR_TYPE_LIST; break; + case VAR_DICT: n = VAR_TYPE_DICT; break; + case VAR_FLOAT: n = VAR_TYPE_FLOAT; break; + case VAR_BOOL: n = VAR_TYPE_BOOL; break; + case VAR_SPECIAL: n = VAR_TYPE_NONE; break; + case VAR_JOB: n = VAR_TYPE_JOB; break; + case VAR_CHANNEL: n = VAR_TYPE_CHANNEL; break; + case VAR_BLOB: n = VAR_TYPE_BLOB; break; + case VAR_INSTR: n = VAR_TYPE_INSTR; break; + case VAR_CLASS: n = VAR_TYPE_CLASS; break; + case VAR_OBJECT: n = VAR_TYPE_OBJECT; break; + case VAR_TYPEALIAS: n = VAR_TYPE_TYPEALIAS; break; + case VAR_UNKNOWN: + case VAR_ANY: + case VAR_VOID: + internal_error_no_abort("f_type(UNKNOWN)"); + n = -1; + break; + } + rettv->vval.v_number = n; +} + +/* + * "virtcol({expr}, [, {list} [, {winid}]])" function + */ + static void +f_virtcol(typval_T *argvars, typval_T *rettv) +{ + colnr_T vcol_start = 0; + colnr_T vcol_end = 0; + pos_T *fp; + switchwin_T switchwin; + int winchanged = FALSE; + int len; + + if (in_vim9script() + && (check_for_string_or_list_arg(argvars, 0) == FAIL + || (argvars[1].v_type != VAR_UNKNOWN + && (check_for_bool_arg(argvars, 1) == FAIL + || check_for_opt_number_arg(argvars, 2) == FAIL)))) + return; + + if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) + { + tabpage_T *tp; + win_T *wp; + + // use the window specified in the third argument + wp = win_id2wp_tp((int)tv_get_number(&argvars[2]), &tp); + if (wp == NULL || tp == NULL) + goto theend; + + if (switch_win_noblock(&switchwin, wp, tp, TRUE) != OK) + goto theend; + + check_cursor(); + winchanged = TRUE; + } + + int fnum = curbuf->b_fnum; + fp = var2fpos(&argvars[0], FALSE, &fnum, FALSE); + if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count + && fnum == curbuf->b_fnum) + { + // Limit the column to a valid value, getvvcol() doesn't check. + if (fp->col < 0) + fp->col = 0; + else + { + len = (int)STRLEN(ml_get(fp->lnum)); + if (fp->col > len) + fp->col = len; + } + getvvcol(curwin, fp, &vcol_start, NULL, &vcol_end); + ++vcol_start; + ++vcol_end; + } + +theend: + if (argvars[1].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[1])) + { + if (rettv_list_alloc(rettv) == OK) + { + list_append_number(rettv->vval.v_list, vcol_start); + list_append_number(rettv->vval.v_list, vcol_end); + } + else + rettv->vval.v_number = 0; + } + else + rettv->vval.v_number = vcol_end; + + if (winchanged) + restore_win_noblock(&switchwin, TRUE); +} + +/* + * "visualmode()" function + */ + static void +f_visualmode(typval_T *argvars, typval_T *rettv) +{ + char_u str[2]; + + if (in_vim9script() && check_for_opt_bool_arg(argvars, 0) == FAIL) + return; + + rettv->v_type = VAR_STRING; + str[0] = curbuf->b_visual_mode_eval; + str[1] = NUL; + rettv->vval.v_string = vim_strsave(str); + + // A non-zero number or non-empty string argument: reset mode. + if (non_zero_arg(&argvars[0])) + curbuf->b_visual_mode_eval = NUL; +} + +/* + * "wildmenumode()" function + */ + static void +f_wildmenumode(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ + if (wild_menu_showing || ((State & MODE_CMDLINE) && cmdline_pum_active())) + rettv->vval.v_number = 1; +} + +/* + * "windowsversion()" function + */ + static void +f_windowsversion(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave((char_u *)windowsVersion); +} + +/* + * "wordcount()" function + */ + static void +f_wordcount(typval_T *argvars UNUSED, typval_T *rettv) +{ + if (rettv_dict_alloc(rettv) == FAIL) + return; + cursor_pos_info(rettv->vval.v_dict); +} + +/* + * "xor(expr, expr)" function + */ + static void +f_xor(typval_T *argvars, typval_T *rettv) +{ + if (in_vim9script() + && (check_for_number_arg(argvars, 0) == FAIL + || check_for_number_arg(argvars, 1) == FAIL)) + return; + + rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) + ^ tv_get_number_chk(&argvars[1], NULL); +} + +#endif // FEAT_EVAL |