vim/src/evalfunc.c
Yegappan Lakshmanan 166b1754a9
patch 9.1.1025: wrong return type of blob2str()
Problem:  wrong return type of blob2str()
Solution: update return to list of string
          (Yegappan Lakshmanan)

closes: #16461

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
2025-01-17 11:48:12 +01:00

12309 lines
301 KiB
C

/* 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_base64_encode(typval_T *argvars, typval_T *rettv);
static void f_base64_decode(typval_T *argvars, typval_T *rettv);
static void f_bindtextdomain(typval_T *argvars, typval_T *rettv);
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_getcellpixels(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_getcurpos(typval_T *argvars, typval_T *rettv);
static void f_getcursorcharpos(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_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_getregion(typval_T *argvars, typval_T *rettv);
static void f_getregionpos(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_id(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(), filter(), foreach().
*/
static int
check_map_filter_arg2(type_T *type, argcontext_T *context,
filtermap_T filtermap)
{
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 (filtermap == FILTERMAP_MAP)
t_func_exp.tt_member = expected_member == NULL
|| type_any_or_unknown(type->tt_member)
? &t_any : expected_member;
else if (filtermap == FILTERMAP_FILTER)
t_func_exp.tt_member = &t_bool;
else // filtermap == FILTERMAP_FOREACH
t_func_exp.tt_member = &t_unknown;
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, FILTERMAP_FILTER);
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, FILTERMAP_MAP);
semsg(_(e_string_or_function_required_for_argument_nr), 2);
return FAIL;
}
/*
* Check second argument of foreach(), the function.
*/
static int
arg_foreach_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, FILTERMAP_FOREACH);
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->tt_type == VAR_OBJECT
|| 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_list_string_dict[] = {arg_list_string, arg_dict_any};
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_buffer[] = {arg_number, arg_buffer};
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 arg2_blob_dict[] = {arg_blob, arg_dict_any};
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_diff[] = {arg_list_string, arg_list_string, arg_dict_any};
static argcheck_T arg3_list_string_dict[] = {arg_list_any, arg_string, arg_dict_any};
static argcheck_T arg3_list_list_dict[] = {arg_list_any, arg_list_any, 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_foreach[] = {arg_list_or_dict_or_blob_or_string, arg_foreach_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_list_regionpos(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type)
{
*decl_type = &t_list_any;
return &t_list_list_list_number;
}
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
},
{"base64_decode", 1, 1, FEARG_1, arg1_string,
ret_blob, f_base64_decode},
{"base64_encode", 1, 1, FEARG_1, arg1_blob,
ret_string, f_base64_encode},
{"bindtextdomain", 2, 2, 0, arg2_string,
ret_bool, f_bindtextdomain},
{"blob2list", 1, 1, FEARG_1, arg1_blob,
ret_list_number, f_blob2list},
{"blob2str", 1, 2, FEARG_1, arg2_blob_dict,
ret_list_string, f_blob2str},
{"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", 2, 3, FEARG_1, arg3_diff,
ret_any, f_diff},
{"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},
{"filecopy", 2, 2, FEARG_1, arg2_string,
ret_number_bool, f_filecopy},
{"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},
{"foreach", 2, 2, FEARG_1, arg2_foreach,
ret_first_arg, f_foreach},
{"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},
{"getcellpixels", 0, 0, 0, NULL,
ret_list_any, f_getcellpixels},
{"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},
{"getcmdcomplpat", 0, 0, 0, NULL,
ret_string, f_getcmdcomplpat},
{"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},
{"getcmdprompt", 0, 0, 0, NULL,
ret_string, f_getcmdprompt},
{"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},
{"getregion", 2, 3, FEARG_1, arg3_list_list_dict,
ret_list_string, f_getregion},
{"getregionpos", 2, 3, FEARG_1, arg3_list_list_dict,
ret_list_regionpos, f_getregionpos},
{"getregtype", 0, 1, FEARG_1, arg1_string,
ret_string, f_getregtype},
{"getscriptinfo", 0, 1, 0, arg1_dict_any,
ret_list_dict_any, f_getscriptinfo},
{"getstacktrace", 0, 0, 0, NULL,
ret_list_dict_any, f_getstacktrace},
{"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, 2, FEARG_1, arg2_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},
{"id", 1, 1, FEARG_1, NULL,
ret_string, f_id},
{"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_setbuf", 2, 2, FEARG_1, arg2_number_buffer,
ret_number_bool, PROP_FUNC(f_popup_setbuf)},
{"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, 2, FEARG_1, arg2_string_dict,
ret_any,
#ifdef FEAT_PYTHON3
f_py3eval
#else
NULL
#endif
},
{"pyeval", 1, 2, FEARG_1, arg2_string_dict,
ret_any,
#ifdef FEAT_PYTHON
f_pyeval
#else
NULL
#endif
},
{"pyxeval", 1, 2, FEARG_1, arg2_string_dict,
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},
{"str2blob", 1, 2, FEARG_1, arg2_list_string_dict,
ret_blob, f_str2blob},
{"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
// Base64 character set
static const char_u base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// Base64 decoding table (initialized in init_base64_dec_table() below)
static char_u base64_dec_table[256];
/*
* Initialize the base64 decoding table
*/
static void
init_base64_dec_table(void)
{
static int base64_dec_tbl_initialized = FALSE;
if (base64_dec_tbl_initialized)
return;
// Unsupported characters are set to 0xFF
vim_memset(base64_dec_table, 0xFF, sizeof(base64_dec_table));
// Initialize the index for the base64 alphabets
for (size_t i = 0; i < sizeof(base64_table) - 1; i++)
base64_dec_table[(char_u)base64_table[i]] = (char_u)i;
// base64 padding character
base64_dec_table['='] = 0;
base64_dec_tbl_initialized = TRUE;
}
/*
* Encode the bytes in "blob" using base-64 encoding.
*/
static char_u *
base64_encode(blob_T *blob)
{
size_t input_len = blob->bv_ga.ga_len;
size_t encoded_len = ((input_len + 2) / 3) * 4;
char_u *data = blob->bv_ga.ga_data;
char_u *encoded = alloc(encoded_len + 1);
if (encoded == NULL)
return NULL;
size_t i, j;
for (i = 0, j = 0; i < input_len;)
{
int_u octet_a = i < input_len ? data[i++] : 0;
int_u octet_b = i < input_len ? data[i++] : 0;
int_u octet_c = i < input_len ? data[i++] : 0;
int_u triple = (octet_a << 16) | (octet_b << 8) | octet_c;
encoded[j++] = base64_table[(triple >> 18) & 0x3F];
encoded[j++] = base64_table[(triple >> 12) & 0x3F];
encoded[j++] = (!octet_b && i >= input_len) ? '='
: base64_table[(triple >> 6) & 0x3F];
encoded[j++] = (!octet_c && i >= input_len) ? '='
: base64_table[triple & 0x3F];
}
encoded[j] = NUL;
return encoded;
}
/*
* Decode the string "data" using base-64 encoding.
*/
static void
base64_decode(const char_u *data, blob_T *blob)
{
size_t input_len = STRLEN(data);
if (input_len == 0)
return;
if (input_len % 4 != 0)
{
// Invalid input length
semsg(_(e_invalid_argument_str), data);
return;
}
init_base64_dec_table();
size_t decoded_len = (input_len / 4) * 3;
if (data[input_len - 1] == '=')
decoded_len--;
if (data[input_len - 2] == '=')
decoded_len--;
size_t i, j;
for (i = 0, j = 0; i < input_len;)
{
int_u sextet_a = base64_dec_table[(char_u)data[i++]];
int_u sextet_b = base64_dec_table[(char_u)data[i++]];
int_u sextet_c = base64_dec_table[(char_u)data[i++]];
int_u sextet_d = base64_dec_table[(char_u)data[i++]];
if (sextet_a == 0xFF || sextet_b == 0xFF || sextet_c == 0xFF
|| sextet_d == 0xFF)
{
// Invalid character
semsg(_(e_invalid_argument_str), data);
ga_clear(&blob->bv_ga);
return;
}
int_u triple = (sextet_a << 18) | (sextet_b << 12)
| (sextet_c << 6) | sextet_d;
if (j < decoded_len)
{
ga_append(&blob->bv_ga, (triple >> 16) & 0xFF);
j++;
}
if (j < decoded_len)
{
ga_append(&blob->bv_ga, (triple >> 8) & 0xFF);
j++;
}
if (j < decoded_len)
{
ga_append(&blob->bv_ga, triple & 0xFF);
j++;
}
if (j == decoded_len)
{
// Check for invalid padding bytes (based on the
// "Base64 Malleability in Practice" ACM paper).
if ((data[input_len - 2] == '=' && ((sextet_b & 0xF) != 0))
|| ((data[input_len - 1] == '=') && ((sextet_c & 0x3) != 0)))
{
semsg(_(e_invalid_argument_str), data);
ga_clear(&blob->bv_ga);
return;
}
}
}
}
/*
* "base64_decode(string)" function
*/
static void
f_base64_decode(typval_T *argvars, typval_T *rettv)
{
if (check_for_string_arg(argvars, 0) == FAIL)
return;
if (rettv_blob_alloc(rettv) == FAIL)
return;
char_u *str = tv_get_string_chk(&argvars[0]);
if (str != NULL)
base64_decode(str, rettv->vval.v_blob);
}
/*
* "base64_encode(blob)" function
*/
static void
f_base64_encode(typval_T *argvars, typval_T *rettv)
{
if (check_for_blob_arg(argvars, 0) == FAIL)
return;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
blob_T *blob = argvars->vval.v_blob;
if (blob != NULL)
rettv->vval.v_string = base64_encode(blob);
}
/*
* 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;
}
/*
* "bindtextdomain(package, path)" function
*/
static void
f_bindtextdomain(typval_T *argvars, typval_T *rettv)
{
rettv->v_type = VAR_BOOL;
rettv->vval.v_number = VVAL_TRUE;
if (check_for_nonempty_string_arg(argvars, 0) == FAIL
|| check_for_nonempty_string_arg(argvars, 1) == FAIL)
return;
if (strcmp((const char *)argvars[0].vval.v_string, VIMPACKAGE) == 0)
semsg(_(e_invalid_argument_str), tv_get_string(&argvars[0]));
else
{
if (bindtextdomain((const char *)argvars[0].vval.v_string, (const char *)argvars[1].vval.v_string) == NULL)
{
do_outofmem_msg((long)0);
rettv->vval.v_number = VVAL_FALSE;
}
}
return;
}
/*
* "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 *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
if (argvars[0].v_type == VAR_STRING)
{
char_u *p = func;
tofree = trans_function_name(&p, NULL, FALSE, TFN_INT|TFN_QUIET);
if (tofree == NULL)
{
emsg_funcname(e_unknown_function_str, func);
return;
}
func = tofree;
}
if (argvars[2].v_type != VAR_UNKNOWN)
{
if (check_for_dict_arg(argvars, 2) == FAIL)
goto done;
selfdict = argvars[2].vval.v_dict;
}
(void)func_call(func, &argvars[1], partial, selfdict, rettv);
done:
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 = ml_get_len(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 != MAXCOL && --col < 0)
col = 0;
curwin->w_cursor.col = col;
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 = curbuf->b_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 = argvars[0].vval.v_partial == NULL;
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 = object_empty(argvars[0].vval.v_object);
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;
size_t 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 if (STRCMP(what, "arity") == 0)
{
int required = 0, optional = 0, varargs = FALSE;
char_u *name = partial_name(pt);
get_func_arity(name, &required, &optional, &varargs);
rettv->v_type = VAR_DICT;
if (rettv_dict_alloc(rettv) == OK)
{
dict_T *dict = rettv->vval.v_dict;
// Take into account the arguments of the partial, if any.
// Note that it is possible to supply more arguments than the function
// accepts.
if (pt->pt_argc >= required + optional)
required = optional = 0;
else if (pt->pt_argc > required)
{
optional -= pt->pt_argc - required;
required = 0;
}
else
required -= pt->pt_argc;
dict_add_number(dict, "required", required);
dict_add_number(dict, "optional", optional);
dict_add_bool(dict, "varargs", varargs);
}
}
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);
}
/*
* "getcellpixels()" function
*/
static void
f_getcellpixels(typval_T *argvars UNUSED, typval_T *rettv)
{
if (rettv_list_alloc(rettv) == FAIL)
return;
#if defined(FEAT_GUI)
if (gui.in_use)
{
// success pixel size and no gui.
list_append_number(rettv->vval.v_list, (varnumber_T)gui.char_width);
list_append_number(rettv->vval.v_list, (varnumber_T)gui.char_height);
}
else
#endif
{
struct cellsize cs;
#if defined(UNIX)
mch_calc_cell_size(&cs);
#else
// Non-Unix CUIs are not supported, so set this to -1x-1.
cs.cs_xpixel = -1;
cs.cs_ypixel = -1;
#endif
// failed get pixel size.
if (cs.cs_xpixel == -1)
return;
// success pixel size and no gui.
list_append_number(rettv->vval.v_list, (varnumber_T)cs.cs_xpixel);
list_append_number(rettv->vval.v_list, (varnumber_T)cs.cs_ypixel);
}
}
/*
* "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);
}
/*
* Convert from block_def to string
*/
static char_u *
block_def2str(struct block_def *bd)
{
char_u *p, *ret;
size_t size = bd->startspaces + bd->endspaces + bd->textlen;
ret = alloc(size + 1);
if (ret != NULL)
{
p = ret;
vim_memset(p, ' ', bd->startspaces);
p += bd->startspaces;
mch_memmove(p, bd->textstart, bd->textlen);
p += bd->textlen;
vim_memset(p, ' ', bd->endspaces);
*(p + bd->endspaces) = NUL;
}
return ret;
}
static int
getregionpos(
typval_T *argvars,
typval_T *rettv,
pos_T *p1,
pos_T *p2,
int *inclusive,
int *region_type,
oparg_T *oap)
{
int fnum1 = -1, fnum2 = -1;
char_u *type;
buf_T *findbuf;
char_u default_type[] = "v";
int block_width = 0;
int is_select_exclusive;
int l;
if (rettv_list_alloc(rettv) == FAIL)
return FAIL;
if (check_for_list_arg(argvars, 0) == FAIL
|| check_for_list_arg(argvars, 1) == FAIL
|| check_for_opt_dict_arg(argvars, 2) == FAIL)
return FAIL;
if (list2fpos(&argvars[0], p1, &fnum1, NULL, FALSE) != OK
|| list2fpos(&argvars[1], p2, &fnum2, NULL, FALSE) != OK
|| fnum1 != fnum2)
return FAIL;
if (argvars[2].v_type == VAR_DICT)
{
is_select_exclusive = dict_get_bool(
argvars[2].vval.v_dict, "exclusive", *p_sel == 'e');
type = dict_get_string(
argvars[2].vval.v_dict, "type", FALSE);
if (type == NULL)
type = default_type;
}
else
{
is_select_exclusive = *p_sel == 'e';
type = default_type;
}
if (type[0] == 'v' && type[1] == NUL)
*region_type = MCHAR;
else if (type[0] == 'V' && type[1] == NUL)
*region_type = MLINE;
else if (type[0] == Ctrl_V)
{
char_u *p = type + 1;
if (*p != NUL && ((block_width = getdigits(&p)) <= 0 || *p != NUL))
{
semsg(_(e_invalid_value_for_argument_str_str), "type", type);
return FAIL;
}
*region_type = MBLOCK;
}
else
{
semsg(_(e_invalid_value_for_argument_str_str), "type", type);
return FAIL;
}
findbuf = fnum1 != 0 ? buflist_findnr(fnum1) : curbuf;
if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL)
{
emsg(_(e_buffer_is_not_loaded));
return FAIL;
}
if (p1->lnum < 1 || p1->lnum > findbuf->b_ml.ml_line_count)
{
semsg(_(e_invalid_line_number_nr), p1->lnum);
return FAIL;
}
if (p1->col == MAXCOL)
p1->col = ml_get_buf_len(findbuf, p1->lnum) + 1;
else if (p1->col < 1 || p1->col > ml_get_buf_len(findbuf, p1->lnum) + 1)
{
semsg(_(e_invalid_column_number_nr), p1->col);
return FAIL;
}
if (p2->lnum < 1 || p2->lnum > findbuf->b_ml.ml_line_count)
{
semsg(_(e_invalid_line_number_nr), p2->lnum);
return FAIL;
}
if (p2->col == MAXCOL)
p2->col = ml_get_buf_len(findbuf, p2->lnum) + 1;
else if (p2->col < 1 || p2->col > ml_get_buf_len(findbuf, p2->lnum) + 1)
{
semsg(_(e_invalid_column_number_nr), p2->col);
return FAIL;
}
curbuf = findbuf;
curwin->w_buffer = curbuf;
virtual_op = virtual_active();
// NOTE: Adjustment is needed.
p1->col--;
p2->col--;
if (!LT_POS(*p1, *p2))
{
// swap position
pos_T p;
p = *p1;
*p1 = *p2;
*p2 = p;
}
if (*region_type == MCHAR)
{
// Handle 'selection' == "exclusive".
if (is_select_exclusive && !EQUAL_POS(*p1, *p2))
// When backing up to previous line, inclusive becomes false.
*inclusive = !unadjust_for_sel_inner(p2);
// If p2 is on NUL (end of line), inclusive becomes false.
if (*inclusive && !virtual_op && *ml_get_pos(p2) == NUL)
*inclusive = FALSE;
}
else if (*region_type == MBLOCK)
{
colnr_T sc1, ec1, sc2, ec2;
getvvcol(curwin, p1, &sc1, NULL, &ec1);
getvvcol(curwin, p2, &sc2, NULL, &ec2);
oap->motion_type = MBLOCK;
oap->inclusive = TRUE;
oap->op_type = OP_NOP;
oap->start = *p1;
oap->end = *p2;
oap->start_vcol = MIN(sc1, sc2);
if (block_width > 0)
oap->end_vcol = oap->start_vcol + block_width - 1;
else if (is_select_exclusive && ec1 < sc2 && 0 < sc2 && ec2 > ec1)
oap->end_vcol = sc2 - 1;
else
oap->end_vcol = MAX(ec1, ec2);
}
// Include the trailing byte of a multi-byte char.
l = mb_ptr2len((char_u *)ml_get_pos(p2));
if (l > 1)
p2->col += l - 1;
return OK;
}
/*
* "getregion()" function
*/
static void
f_getregion(typval_T *argvars, typval_T *rettv)
{
pos_T p1, p2;
int inclusive = TRUE;
int region_type = -1;
oparg_T oa;
buf_T *save_curbuf;
int save_virtual;
char_u *akt = NULL;
linenr_T lnum;
save_curbuf = curbuf;
save_virtual = virtual_op;
if (getregionpos(argvars, rettv,
&p1, &p2, &inclusive, &region_type, &oa) == FAIL)
return;
for (lnum = p1.lnum; lnum <= p2.lnum; lnum++)
{
int ret = 0;
struct block_def bd;
if (region_type == MLINE)
akt = vim_strsave(ml_get(lnum));
else if (region_type == MBLOCK)
{
block_prep(&oa, &bd, lnum, FALSE);
akt = block_def2str(&bd);
}
else if (p1.lnum < lnum && lnum < p2.lnum)
akt = vim_strsave(ml_get(lnum));
else
{
charwise_block_prep(p1, p2, &bd, lnum, inclusive);
akt = block_def2str(&bd);
}
if (akt)
{
ret = list_append_string(rettv->vval.v_list, akt, -1);
vim_free(akt);
}
if (akt == NULL || ret == FAIL)
{
clear_tv(rettv);
(void)rettv_list_alloc(rettv);
break;
}
}
// getregionpos() may change curbuf and virtual_op
curbuf = save_curbuf;
curwin->w_buffer = curbuf;
virtual_op = save_virtual;
}
static void
add_regionpos_range(typval_T *rettv, pos_T p1, pos_T p2)
{
list_T *l1, *l2, *l3;
l1 = list_alloc();
if (l1 == NULL)
return;
if (list_append_list(rettv->vval.v_list, l1) == FAIL)
{
vim_free(l1);
return;
}
l2 = list_alloc();
if (l2 == NULL)
{
vim_free(l1);
return;
}
if (list_append_list(l1, l2) == FAIL)
{
vim_free(l1);
vim_free(l2);
return;
}
l3 = list_alloc();
if (l3 == NULL)
{
vim_free(l1);
vim_free(l2);
return;
}
if (list_append_list(l1, l3) == FAIL)
{
vim_free(l1);
vim_free(l2);
vim_free(l3);
return;
}
list_append_number(l2, curbuf->b_fnum);
list_append_number(l2, p1.lnum);
list_append_number(l2, p1.col);
list_append_number(l2, p1.coladd);
list_append_number(l3, curbuf->b_fnum);
list_append_number(l3, p2.lnum);
list_append_number(l3, p2.col);
list_append_number(l3, p2.coladd);
}
/*
* "getregionpos()" function
*/
static void
f_getregionpos(typval_T *argvars, typval_T *rettv)
{
pos_T p1, p2;
int inclusive = TRUE;
int region_type = -1;
int allow_eol = FALSE;
oparg_T oa;
int lnum;
buf_T *save_curbuf;
int save_virtual;
save_curbuf = curbuf;
save_virtual = virtual_op;
if (getregionpos(argvars, rettv,
&p1, &p2, &inclusive, &region_type, &oa) == FAIL)
return;
if (argvars[2].v_type == VAR_DICT)
allow_eol = dict_get_bool(argvars[2].vval.v_dict, "eol", FALSE);
for (lnum = p1.lnum; lnum <= p2.lnum; lnum++)
{
pos_T ret_p1, ret_p2;
char_u *line = ml_get(lnum);
colnr_T line_len = ml_get_len(lnum);
if (region_type == MLINE)
{
ret_p1.col = 1;
ret_p1.coladd = 0;
ret_p2.col = MAXCOL;
ret_p2.coladd = 0;
}
else
{
struct block_def bd;
if (region_type == MBLOCK)
block_prep(&oa, &bd, lnum, FALSE);
else
charwise_block_prep(p1, p2, &bd, lnum, inclusive);
if (bd.is_oneChar) // selection entirely inside one char
{
if (region_type == MBLOCK)
{
ret_p1.col = mb_prevptr(line, bd.textstart) - line + 1;
ret_p1.coladd = bd.start_char_vcols
- (bd.start_vcol - oa.start_vcol);
}
else
{
ret_p1.col = p1.col + 1;
ret_p1.coladd = p1.coladd;
}
}
else if (region_type == MBLOCK && oa.start_vcol > bd.start_vcol)
{
// blockwise selection entirely beyond end of line
ret_p1.col = MAXCOL;
ret_p1.coladd = oa.start_vcol - bd.start_vcol;
bd.is_oneChar = TRUE;
}
else if (bd.startspaces > 0)
{
ret_p1.col = mb_prevptr(line, bd.textstart) - line + 1;
ret_p1.coladd = bd.start_char_vcols - bd.startspaces;
}
else
{
ret_p1.col = bd.textcol + 1;
ret_p1.coladd = 0;
}
if (bd.is_oneChar) // selection entirely inside one char
{
ret_p2.col = ret_p1.col;
ret_p2.coladd = ret_p1.coladd + bd.startspaces + bd.endspaces;
}
else if (bd.endspaces > 0)
{
ret_p2.col = bd.textcol + bd.textlen + 1;
ret_p2.coladd = bd.endspaces;
}
else
{
ret_p2.col = bd.textcol + bd.textlen;
ret_p2.coladd = 0;
}
}
if (!allow_eol && ret_p1.col > line_len)
{
ret_p1.col = 0;
ret_p1.coladd = 0;
}
else if (ret_p1.col > line_len + 1)
ret_p1.col = line_len + 1;
if (!allow_eol && ret_p2.col > line_len)
{
ret_p2.col = ret_p1.col == 0 ? 0 : line_len;
ret_p2.coladd = 0;
}
else if (ret_p2.col > line_len + 1)
ret_p2.col = line_len + 1;
ret_p1.lnum = lnum;
ret_p2.lnum = lnum;
add_regionpos_range(rettv, ret_p1, ret_p2);
}
// getregionpos() may change curbuf and virtual_op
curbuf = save_curbuf;
curwin->w_buffer = curbuf;
virtual_op = save_virtual;
}
/*
* 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, &reglen))
{
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 defined(HAVE_BIND_TEXTDOMAIN_CODESET)
char *prev = NULL;
#endif
if (check_for_nonempty_string_arg(argvars, 0) == FAIL
|| check_for_opt_string_arg(argvars, 1) == FAIL)
return;
rettv->v_type = VAR_STRING;
if (argvars[1].v_type == VAR_STRING &&
argvars[1].vval.v_string != NULL &&
*(argvars[1].vval.v_string) != NUL)
{
#if defined(HAVE_BIND_TEXTDOMAIN_CODESET)
prev = bind_textdomain_codeset((const char *)argvars[1].vval.v_string, (char *)p_enc);
#endif
#if defined(HAVE_DGETTEXT)
rettv->vval.v_string = vim_strsave((char_u *)dgettext((const char *)argvars[1].vval.v_string, (const char *)argvars[0].vval.v_string));
#else
textdomain((const char *)argvars[1].vval.v_string);
rettv->vval.v_string = vim_strsave((char_u *)_(argvars[0].vval.v_string));
textdomain(VIMPACKAGE);
#endif
#if defined(HAVE_BIND_TEXTDOMAIN_CODESET)
if (prev != NULL)
bind_textdomain_codeset((const char *)argvars[1].vval.v_string, prev);
#endif
}
else
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_con_gui",
#if defined(FEAT_CON_DIALOG) && defined(FEAT_GUI_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);
}
/*
* "id()" function
* Identity. Return address of item as a hex string, %p format.
* Currently only valid for object/container types.
* Return empty string if not an object.
*/
#ifdef VMS // VMS does not have defined uintptr_t
# if defined(HAVE_NO_LONG_LONG)
typedef unsigned int uintptr_t;
# else
typedef unsigned long long uintptr_t;
# endif
#endif // VMS
static void
f_id(typval_T *argvars, typval_T *rettv)
{
char numbuf[NUMBUFLEN];
char *p = numbuf;
switch (argvars[0].v_type)
{
case VAR_LIST:
case VAR_DICT:
case VAR_OBJECT:
case VAR_JOB:
case VAR_CHANNEL:
case VAR_BLOB:
// Assume pointer value in typval_T vval union at common location.
if (argvars[0].vval.v_object != NULL)
{
// "v" gets the address as an integer
uintptr_t v = (uintptr_t)(void *)argvars[0].vval.v_object;
// Build a hex string from the item's address; it is in
// reverse order. Ignore trailing zeros.
for (; p < numbuf + sizeof(uintptr_t) * 2 && v != 0;
++p, v >>= 4)
*p = "0123456789abcdef"[v & 0xf];
}
default:
break;
}
*p = NUL;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = vim_strsave((char_u *)numbuf);
}
/*
* "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))
{
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))
{
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);
int called_emsg_start = called_emsg;
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;
if (called_emsg != called_emsg_start)
return -1;
}
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);
int called_emsg_start = called_emsg;
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;
if (called_emsg != called_emsg_start)
return -1;
}
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].vval.v_string == NUL))
|| (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_OBJECT:
rettv->vval.v_number = object_len(argvars[0].vval.v_object);
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_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)
{
#ifdef FEAT_DIFF
// in diff mode, prevent that the window scrolls
// and keep the topline
if (curwin->w_p_diff && switchwin.sw_curwin->w_p_diff)
skip_update_topline = TRUE;
#endif
check_cursor();
fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE);
}
#ifdef FEAT_DIFF
if (curwin->w_p_diff && switchwin.sw_curwin->w_p_diff)
skip_update_topline = FALSE;
#endif
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(&regmatch, 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, &regmatch, 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, &regmatch, 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];
dict_T *locals;
if (check_restricted() || check_secure())
return;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_dict_arg(argvars, 1) == FAIL))
return;
if (p_pyx == 0)
p_pyx = 3;
if (argvars[1].v_type == VAR_DICT)
{
locals = argvars[1].vval.v_dict;
}
else if (argvars[1].v_type != VAR_UNKNOWN)
{
emsg(_(e_dictionary_required));
return;
}
else
{
locals = NULL;
}
str = tv_get_string_buf(&argvars[0], buf);
do_py3eval(str, locals, rettv);
}
#endif
#ifdef FEAT_PYTHON
/*
* "pyeval()" function
*/
static void
f_pyeval(typval_T *argvars, typval_T *rettv)
{
char_u *str;
char_u buf[NUMBUFLEN];
dict_T *locals;
if (check_restricted() || check_secure())
return;
if (in_vim9script() && (
check_for_string_arg(argvars, 0) == FAIL ||
check_for_opt_dict_arg(argvars, 1) == FAIL ) )
return;
if (p_pyx == 0)
p_pyx = 2;
if (argvars[1].v_type == VAR_DICT)
{
locals = argvars[1].vval.v_dict;
}
else if (argvars[1].v_type != VAR_UNKNOWN)
{
emsg( "Invalid argument: must be dict" );
return;
}
else
{
locals = NULL;
}
str = tv_get_string_buf(&argvars[0], buf);
do_pyeval(str, locals, 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
|| check_for_opt_dict_arg(argvars, 1) == 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)
{
struct {
union {
UINT32_T number;
char_u bytes[sizeof(UINT32_T)];
} contents;
} buf;
if (srand_seed_for_testing_is_used)
{
*x = srand_seed_for_testing;
return;
}
if (mch_get_random(buf.contents.bytes, sizeof(buf.contents.bytes)) == OK)
{
*x = buf.contents.number;
return;
}
// The system's random number generator 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, &reglen))
{
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;
size_t patlen;
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
patlen = STRLEN(pat);
// Repeat until {skip} returns FALSE.
for (;;)
{
subpatnum = searchit(curwin, curbuf, &pos, NULL, dir, pat, patlen, 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;
size_t patlen;
size_t spatlen;
size_t epatlen;
size_t pat2size;
size_t pat2len;
size_t pat3size;
size_t pat3len;
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).
spatlen = STRLEN(spat);
epatlen = STRLEN(epat);
pat2size = spatlen + epatlen + 17;
pat2 = alloc(pat2size);
if (pat2 == NULL)
goto theend;
pat3size = spatlen + STRLEN(mpat) + epatlen + 25;
pat3 = alloc(pat3size);
if (pat3 == NULL)
goto theend;
pat2len = vim_snprintf((char *)pat2, pat2size, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat);
if (*mpat == NUL)
{
STRCPY(pat3, pat2);
pat3len = pat2len;
}
else
pat3len = vim_snprintf((char *)pat3, pat3size, "\\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;
patlen = pat3len;
for (;;)
{
searchit_arg_T sia;
CLEAR_FIELD(sia);
sia.sa_stop_lnum = lnum_stop;
n = searchit(curwin, curbuf, &pos, NULL, dir, pat, patlen, 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, FALSE);
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, SMT_ALL, 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(&regmatch, 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, STRLEN(str), pat, sub, expr, flg, NULL);
}
/*
* "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)ml_get_len(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)ml_get_len(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)ml_get_len(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_TYPEALIAS: n = VAR_TYPE_TYPEALIAS; break;
case VAR_CLASS:
{
class_T *cl = argvars[0].vval.v_class;
if (cl != NULL && IS_ENUM(cl))
n = VAR_TYPE_ENUM;
else
n = VAR_TYPE_CLASS;
break;
}
case VAR_OBJECT:
{
object_T *obj = argvars[0].vval.v_object;
if (obj == NULL)
n = VAR_TYPE_OBJECT;
else
{
class_T *cl = argvars[0].vval.v_object->obj_class;
if (IS_ENUM(cl))
n = VAR_TYPE_ENUMVALUE;
else
n = VAR_TYPE_OBJECT;
}
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)ml_get_len(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