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