Mercurial > vim
view src/evalfunc.c @ 32196:d6771e63521c v9.0.1429
patch 9.0.1429: invalid memory access when ending insert mode
Commit: https://github.com/vim/vim/commit/1a08a3e2a584889f19b84a27672134649b73da58
Author: Bram Moolenaar <Bram@vim.org>
Date: Sun Mar 26 21:27:24 2023 +0100
patch 9.0.1429: invalid memory access when ending insert mode
Problem: Invalid memory access when ending insert mode.
Solution: Check if the insert_skip value is valid.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sun, 26 Mar 2023 22:30:04 +0200 |
parents | 98774a275d6d |
children | b2cce50602ca |
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_escape(typval_T *argvars, typval_T *rettv); static void f_eval(typval_T *argvars, typval_T *rettv); static void f_eventhandler(typval_T *argvars, typval_T *rettv); static void f_execute(typval_T *argvars, typval_T *rettv); static void f_exists_compiled(typval_T *argvars, typval_T *rettv); static void f_expand(typval_T *argvars, typval_T *rettv); static void f_expandcmd(typval_T *argvars, typval_T *rettv); static void f_feedkeys(typval_T *argvars, typval_T *rettv); static void f_fnameescape(typval_T *argvars, typval_T *rettv); static void f_foreground(typval_T *argvars, typval_T *rettv); static void f_funcref(typval_T *argvars, typval_T *rettv); static void f_function(typval_T *argvars, typval_T *rettv); static void f_garbagecollect(typval_T *argvars, typval_T *rettv); static void f_get(typval_T *argvars, typval_T *rettv); static void f_getchangelist(typval_T *argvars, typval_T *rettv); static void f_getcharpos(typval_T *argvars, typval_T *rettv); static void f_getcharsearch(typval_T *argvars, typval_T *rettv); static void f_getenv(typval_T *argvars, typval_T *rettv); static void f_getfontname(typval_T *argvars, typval_T *rettv); static void f_getjumplist(typval_T *argvars, typval_T *rettv); static void f_getpid(typval_T *argvars, typval_T *rettv); static void f_getcurpos(typval_T *argvars, typval_T *rettv); static void f_getcursorcharpos(typval_T *argvars, typval_T *rettv); static void f_getpos(typval_T *argvars, typval_T *rettv); static void f_getreg(typval_T *argvars, typval_T *rettv); static void f_getreginfo(typval_T *argvars, typval_T *rettv); static void f_getregtype(typval_T *argvars, typval_T *rettv); static void f_gettagstack(typval_T *argvars, typval_T *rettv); static void f_gettext(typval_T *argvars, typval_T *rettv); static void f_haslocaldir(typval_T *argvars, typval_T *rettv); static void f_hlID(typval_T *argvars, typval_T *rettv); static void f_hlexists(typval_T *argvars, typval_T *rettv); static void f_hostname(typval_T *argvars, typval_T *rettv); static void f_index(typval_T *argvars, typval_T *rettv); static void f_indexof(typval_T *argvars, typval_T *rettv); static void f_input(typval_T *argvars, typval_T *rettv); static void f_inputdialog(typval_T *argvars, typval_T *rettv); static void f_inputlist(typval_T *argvars, typval_T *rettv); static void f_inputrestore(typval_T *argvars, typval_T *rettv); static void f_inputsave(typval_T *argvars, typval_T *rettv); static void f_inputsecret(typval_T *argvars, typval_T *rettv); static void f_interrupt(typval_T *argvars, typval_T *rettv); static void f_invert(typval_T *argvars, typval_T *rettv); static void f_islocked(typval_T *argvars, typval_T *rettv); static void f_keytrans(typval_T *argvars, typval_T *rettv); static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv); static void f_libcall(typval_T *argvars, typval_T *rettv); static void f_libcallnr(typval_T *argvars, typval_T *rettv); static void f_line(typval_T *argvars, typval_T *rettv); static void f_line2byte(typval_T *argvars, typval_T *rettv); #ifdef FEAT_LUA static void f_luaeval(typval_T *argvars, typval_T *rettv); #endif static void f_match(typval_T *argvars, typval_T *rettv); static void f_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_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. 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; } /* * 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 a dict of 'any'. */ static int arg_dict_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { return check_arg_type(&t_dict_any, type, context); } /* * Check "type" is a list of 'any'. */ static int arg_list_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { return check_arg_type(&t_list_any, type, context); } /* * Check "type" is a list of 'any' and modifiable */ static int arg_list_any_mod( type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { return check_arg_type_mod(&t_list_any, type, context); } /* * Check "type" is a list of numbers. */ static int arg_list_number(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { return check_arg_type(&t_list_number, type, context); } /* * Check "type" is a list of strings. */ static int arg_list_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { return check_arg_type(&t_list_string, type, context); } /* * Check "type" is a string. */ static int arg_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { return check_arg_type(&t_string, type, context); } /* * Check "type" is a blob */ static int arg_blob(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { return check_arg_type(&t_blob, type, context); } /* * Check "type" is a bool or number 0 or 1. */ static int arg_bool(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { return check_arg_type(&t_bool, type, context); } /* * Check "type" is a list of 'any' or a blob. */ static int arg_list_or_blob(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_LIST || type->tt_type == VAR_BLOB || type_any_or_unknown(type)) return OK; arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); return FAIL; } /* * Check "type" is a modifiable list of 'any' or a blob. */ static int arg_list_or_blob_mod( type_T *type, type_T *decl_type, argcontext_T *context) { if (arg_list_or_blob(type, decl_type, context) == FAIL) return FAIL; return arg_type_modifiable(type, context->arg_idx + 1); } /* * Check "type" is a string or a number */ static int arg_string_or_nr(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_STRING || type->tt_type == VAR_NUMBER || type_any_or_unknown(type)) return OK; arg_type_mismatch(&t_string, type, context->arg_idx + 1); return FAIL; } /* * Check "type" is a buffer (string or a number) */ static int arg_buffer(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_STRING || type->tt_type == VAR_NUMBER || type_any_or_unknown(type)) return OK; arg_type_mismatch(&t_string, type, context->arg_idx + 1); return FAIL; } /* * Check "type" is a buffer or a dict of any */ static int arg_buffer_or_dict_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_STRING || type->tt_type == VAR_NUMBER || type->tt_type == VAR_DICT || type_any_or_unknown(type)) return OK; arg_type_mismatch(&t_string, type, context->arg_idx + 1); return FAIL; } /* * Check "type" is a line (string or a number) */ static int arg_lnum(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_STRING || type->tt_type == VAR_NUMBER || type_any_or_unknown(type)) return OK; arg_type_mismatch(&t_string, type, context->arg_idx + 1); return FAIL; } /* * Check "type" is a string or a list of strings. */ static int arg_string_or_list_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_STRING || type_any_or_unknown(type)) return OK; if (type->tt_type != VAR_LIST) { arg_type_mismatch(&t_string, type, context->arg_idx + 1); return FAIL; } if (type->tt_member->tt_type == VAR_ANY || type->tt_member->tt_type == VAR_STRING) return OK; arg_type_mismatch(&t_list_string, type, context->arg_idx + 1); return FAIL; } /* * Check "type" is a string or a list of 'any' */ static int arg_string_or_list_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_STRING || type->tt_type == VAR_LIST || type_any_or_unknown(type)) return OK; arg_type_mismatch(&t_string, type, context->arg_idx + 1); return FAIL; } /* * Check "type" is a string or a dict of 'any' */ static int arg_string_or_dict_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_STRING || type->tt_type == VAR_DICT || type_any_or_unknown(type)) return OK; arg_type_mismatch(&t_string, type, context->arg_idx + 1); return FAIL; } /* * Check "type" is a string or a blob */ static int arg_string_or_blob(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_STRING || type->tt_type == VAR_BLOB || type_any_or_unknown(type)) return OK; arg_type_mismatch(&t_string, type, context->arg_idx + 1); return FAIL; } /* * Check "type" is a list of 'any' or a dict of 'any'. */ static int arg_list_or_dict(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_LIST || type->tt_type == VAR_DICT || type_any_or_unknown(type)) return OK; arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); return FAIL; } /* * Check "type" is a list of 'any' or a dict of 'any'. And modifiable. */ static int arg_list_or_dict_mod( type_T *type, type_T *decl_type, argcontext_T *context) { if (arg_list_or_dict(type, decl_type, context) == FAIL) return FAIL; return arg_type_modifiable(type, context->arg_idx + 1); } /* * Check "type" is a list of 'any' or a dict of 'any' or a blob. * Also check if "type" is modifiable. */ static int arg_list_or_dict_or_blob_mod( type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_LIST || type->tt_type == VAR_DICT || type->tt_type == VAR_BLOB || type_any_or_unknown(type)) return arg_type_modifiable(type, context->arg_idx + 1); arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); return FAIL; } /* * Check "type" is a list of 'any' or a dict of 'any' or a blob or a string. */ static int arg_list_or_dict_or_blob_or_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_LIST || type->tt_type == VAR_DICT || type->tt_type == VAR_BLOB || type->tt_type == VAR_STRING || type_any_or_unknown(type)) return OK; arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); return FAIL; } /* * Check "type" is a list of 'any' or a dict of 'any' or a blob or a string. * Also check the value is modifiable. */ static int arg_list_or_dict_or_blob_or_string_mod( type_T *type, type_T *decl_type, argcontext_T *context) { if (arg_list_or_dict_or_blob_or_string(type, decl_type, context) == FAIL) return FAIL; return arg_type_modifiable(type, context->arg_idx + 1); } /* * Check second argument of map() or filter(). */ static int check_map_filter_arg2(type_T *type, argcontext_T *context, int is_map) { type_T *expected_member = NULL; type_T *(args[2]); type_T t_func_exp = {VAR_FUNC, 2, 0, 0, NULL, NULL, args}; if (context->arg_types[0].type_curr->tt_type == VAR_LIST || context->arg_types[0].type_curr->tt_type == VAR_DICT) { // Use the declared type if possible, so that an error is given if // a declared list changes type, but not if a constant list changes // type. if (context->arg_types[0].type_decl->tt_type == VAR_LIST || context->arg_types[0].type_decl->tt_type == VAR_DICT) expected_member = context->arg_types[0].type_decl->tt_member; else expected_member = context->arg_types[0].type_curr->tt_member; } else if (context->arg_types[0].type_curr->tt_type == VAR_STRING) expected_member = &t_string; else if (context->arg_types[0].type_curr->tt_type == VAR_BLOB) expected_member = &t_number; args[0] = NULL; args[1] = &t_unknown; if (type->tt_argcount != -1) { if (!(type->tt_argcount == 2 || (type->tt_argcount == 1 && (type->tt_flags & TTFLAG_VARARGS)))) { emsg(_(e_invalid_number_of_arguments)); return FAIL; } if (type->tt_flags & TTFLAG_VARARGS) // check the argument types at runtime t_func_exp.tt_argcount = -1; else { if (context->arg_types[0].type_curr->tt_type == VAR_STRING || context->arg_types[0].type_curr->tt_type == VAR_BLOB || context->arg_types[0].type_curr->tt_type == VAR_LIST) args[0] = &t_number; else if (context->arg_types[0].type_decl->tt_type == VAR_DICT) args[0] = &t_string; if (args[0] != NULL) args[1] = expected_member; } } if (!type_any_or_unknown(type->tt_member) || args[0] != NULL) { where_T where = WHERE_INIT; if (is_map) t_func_exp.tt_member = expected_member == NULL || type_any_or_unknown(type->tt_member) ? &t_any : expected_member; else t_func_exp.tt_member = &t_bool; if (args[0] == NULL) args[0] = &t_unknown; if (type->tt_argcount == -1) t_func_exp.tt_argcount = -1; where.wt_index = 2; return check_type(&t_func_exp, type, TRUE, where); } return OK; } /* * Check second argument of filter(): func must return a bool. */ static int arg_filter_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_STRING || type->tt_type == VAR_PARTIAL || type_any_or_unknown(type)) return OK; if (type->tt_type == VAR_FUNC) return check_map_filter_arg2(type, context, FALSE); semsg(_(e_string_or_function_required_for_argument_nr), 2); return FAIL; } /* * Check second argument of map(), the function. */ static int arg_map_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_STRING || type->tt_type == VAR_PARTIAL || type_any_or_unknown(type)) return OK; if (type->tt_type == VAR_FUNC) return check_map_filter_arg2(type, context, TRUE); semsg(_(e_string_or_function_required_for_argument_nr), 2); return FAIL; } /* * Check second argument of sort() and uniq(), the "how" argument. */ static int arg_sort_how(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_STRING || type->tt_type == VAR_PARTIAL || type_any_or_unknown(type)) return OK; if (type->tt_type == VAR_FUNC) { type_T *(args[2]); type_T t_func_exp = {VAR_FUNC, 2, 0, 0, &t_number, NULL, args}; if (context->arg_types[0].type_curr->tt_type == VAR_LIST) args[0] = context->arg_types[0].type_curr->tt_member; else args[0] = &t_unknown; if (!type_any_or_unknown(type->tt_member) || args[0] != &t_unknown) { where_T where = WHERE_INIT; args[1] = args[0]; if (type->tt_argcount == -1) t_func_exp.tt_argcount = -1; where.wt_index = 2; 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 "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 job. */ static int arg_job(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { return check_arg_type(&t_job, type, context); } /* * Check "type" is a channel or a job. */ static int arg_chan_or_job(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_CHANNEL || type->tt_type == VAR_JOB || type_any_or_unknown(type)) return OK; arg_type_mismatch(&t_channel, type, context->arg_idx + 1); return FAIL; } /* * Check "type" can be used as the type_decl of the previous argument. * Must not be used for the first argcheck_T entry. */ static int arg_same_as_prev(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { type_T *prev_type = context->arg_types[context->arg_idx - 1].type_decl; return check_arg_type(prev_type, type, context); } /* * Check "type" is the same basic type as the previous argument, checks list or * dict vs other type, but not member type. * Must not be used for the first argcheck_T entry. */ static int arg_same_struct_as_prev(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { type_T *prev_type = context->arg_types[context->arg_idx - 1].type_curr; if (prev_type->tt_type != context->arg_types[context->arg_idx].type_curr->tt_type) return check_arg_type(prev_type, type, context); return OK; } /* * Check "type" is an item of the list or blob of the previous arg. * Must not be used for the first argcheck_T entry. */ static int arg_item_of_prev(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { type_T *prev_type = context->arg_types[context->arg_idx - 1].type_curr; type_T *expected; if (prev_type->tt_type == VAR_LIST) expected = prev_type->tt_member; else if (prev_type->tt_type == VAR_BLOB) expected = &t_number; else // probably VAR_ANY, can't check return OK; return check_arg_type(expected, type, context); } /* * Check "type" is a string or a number or a list */ static int arg_str_or_nr_or_list(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_STRING || type->tt_type == VAR_NUMBER || type->tt_type == VAR_LIST || type_any_or_unknown(type)) return OK; arg_type_mismatch(&t_string, type, context->arg_idx + 1); return FAIL; } /* * Check "type" is a dict of 'any' or a string */ static int arg_dict_any_or_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_DICT || type->tt_type == VAR_STRING || type_any_or_unknown(type)) return OK; arg_type_mismatch(&t_string, type, context->arg_idx + 1); return FAIL; } /* * Check "type" which is the third argument of extend() (number or string or * any) */ static int arg_extend3(type_T *type, type_T *decl_type, argcontext_T *context) { type_T *first_type = context->arg_types[context->arg_idx - 2].type_curr; if (first_type->tt_type == VAR_LIST) return arg_number(type, decl_type, context); if (first_type->tt_type == VAR_DICT) return arg_string(type, decl_type, context); return OK; } /* * Check "type" which is the first argument of get() (blob or list or dict or * funcref) */ static int arg_get1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_BLOB || type->tt_type == VAR_LIST || type->tt_type == VAR_DICT || type->tt_type == VAR_FUNC || type->tt_type == VAR_PARTIAL || type_any_or_unknown(type)) return OK; arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); return FAIL; } /* * Check "type" which is the first argument of len() (number or string or * blob or list or dict) */ static int arg_len1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_STRING || type->tt_type == VAR_NUMBER || type->tt_type == VAR_BLOB || type->tt_type == VAR_LIST || type->tt_type == VAR_DICT || type_any_or_unknown(type)) return OK; arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); return FAIL; } /* * Check "type" which is the second argument of remove() (number or string or * any) */ static int arg_remove2(type_T *type, type_T *decl_type, argcontext_T *context) { type_T *first_type = context->arg_types[context->arg_idx - 1].type_curr; if (first_type->tt_type == VAR_LIST || first_type->tt_type == VAR_BLOB) return arg_number(type, decl_type, context); if (first_type->tt_type == VAR_DICT) return arg_string_or_nr(type, decl_type, context); return OK; } /* * Check "type" which is the first argument of repeat() (string or number or * list or any) */ static int arg_repeat1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_STRING || type->tt_type == VAR_NUMBER || type->tt_type == VAR_BLOB || type->tt_type == VAR_LIST || type_any_or_unknown(type)) return OK; arg_type_mismatch(&t_string, type, context->arg_idx + 1); return FAIL; } /* * Check "type" which is the first argument of slice() (list or blob or string * or any) */ static int arg_slice1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_LIST || type->tt_type == VAR_BLOB || type->tt_type == VAR_STRING || type_any_or_unknown(type)) return OK; arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); return FAIL; } /* * Check "type" which is the first argument of count() (string or list or dict * or any) */ static int arg_string_or_list_or_dict(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_STRING || type->tt_type == VAR_LIST || type->tt_type == VAR_DICT || type_any_or_unknown(type)) return OK; semsg(_(e_string_list_or_dict_required_for_argument_nr), context->arg_idx + 1); return FAIL; } /* * Check "type" which is the first argument of cursor() (number or string or * list or any) */ static int arg_cursor1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_NUMBER || type->tt_type == VAR_STRING || type->tt_type == VAR_LIST || type_any_or_unknown(type)) return OK; arg_type_mismatch(&t_number, type, context->arg_idx + 1); return FAIL; } /* * Lists of functions that check the argument types of a builtin function. */ static argcheck_T arg1_blob[] = {arg_blob}; static argcheck_T arg1_bool[] = {arg_bool}; static argcheck_T arg1_buffer[] = {arg_buffer}; static argcheck_T arg1_buffer_or_dict_any[] = {arg_buffer_or_dict_any}; static argcheck_T arg1_chan_or_job[] = {arg_chan_or_job}; static argcheck_T arg1_dict_any[] = {arg_dict_any}; static argcheck_T arg1_dict_or_string[] = {arg_dict_any_or_string}; static argcheck_T arg1_float_or_nr[] = {arg_float_or_nr}; static argcheck_T arg1_job[] = {arg_job}; static argcheck_T arg1_list_any[] = {arg_list_any}; static argcheck_T arg1_list_number[] = {arg_list_number}; static argcheck_T arg1_list_or_blob_mod[] = {arg_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[] = {NULL, arg_buffer}; static argcheck_T arg2_buffer_any[] = {arg_buffer, NULL}; 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, NULL}; 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, NULL}; 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_bool[] = {arg_string_or_list_any, arg_bool}; 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[] = {NULL, 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, NULL}; static argcheck_T arg3_buffer_string_dict[] = {arg_buffer, arg_string, arg_dict_any}; static argcheck_T arg3_dict_number_number[] = {arg_dict_any, arg_number, arg_number}; static argcheck_T arg3_list_string_dict[] = {arg_list_any, arg_string, arg_dict_any}; static argcheck_T arg3_lnum_number_bool[] = {arg_lnum, arg_number, arg_bool}; static argcheck_T arg3_number[] = {arg_number, arg_number, arg_number}; static argcheck_T arg3_number_any_dict[] = {arg_number, NULL, 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, NULL}; 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, NULL, arg_dict_any}; static argcheck_T arg3_string_any_string[] = {arg_string, NULL, 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_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, NULL}; static argcheck_T arg4_string_string_any_string[] = {arg_string, arg_string, NULL, arg_string}; static argcheck_T arg4_string_string_number_string[] = {arg_string, arg_string, arg_number, arg_string}; /* Function specific argument types (not covered by the above) */ static argcheck_T arg15_assert_fails[] = {arg_string_or_nr, arg_string_or_list_any, NULL, 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, NULL, 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, NULL, arg_bool, arg_number}; static argcheck_T arg13_cursor[] = {arg_cursor1, arg_number, arg_number}; static argcheck_T arg12_deepcopy[] = {NULL, 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, NULL}; 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_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, NULL}; 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 arg119_printf[] = {arg_string_or_nr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; static argcheck_T arg23_reduce[] = {arg_string_list_or_blob, NULL, NULL}; 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, NULL}; 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 == 2) { *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! */ 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 argcheck_T *f_argcheck; // list of functions to check argument types 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; // values for f_argtype; zero means it cannot be used as a method #define FEARG_1 1 // base is the first argument #define FEARG_2 2 // base is the second argument #define FEARG_3 3 // base is the third argument #define FEARG_4 4 // base is the fourth argument #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, 2, FEARG_1, arg2_string_number, ret_number, f_byteidx}, {"byteidxcomp", 2, 2, FEARG_1, arg2_string_number, 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, 3, FEARG_1, arg3_string_number_bool, ret_number, f_charidx}, {"chdir", 1, 1, FEARG_1, arg1_string, ret_string, f_chdir}, {"cindent", 1, 1, FEARG_1, arg1_lnum, ret_number, f_cindent}, {"clearmatches", 0, 1, FEARG_1, arg1_number, ret_void, f_clearmatches}, {"col", 1, 2, FEARG_1, arg2_string_or_list_number, ret_number, f_col}, {"complete", 2, 2, FEARG_2, arg2_number_list, ret_void, f_complete}, {"complete_add", 1, 1, FEARG_1, arg1_dict_or_string, ret_number, f_complete_add}, {"complete_check", 0, 0, 0, NULL, ret_number_bool, f_complete_check}, {"complete_info", 0, 1, FEARG_1, arg1_list_string, ret_dict_any, f_complete_info}, {"confirm", 1, 4, FEARG_1, arg4_string_string_number_string, ret_number, f_confirm}, {"copy", 1, 1, FEARG_1, NULL, ret_copy, f_copy}, {"cos", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, f_cos}, {"cosh", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, f_cosh}, {"count", 2, 4, FEARG_1, arg24_count, ret_number, f_count}, {"cscope_connection",0,3, 0, arg3_number_string_string, ret_number, f_cscope_connection}, {"cursor", 1, 3, FEARG_1, arg13_cursor, ret_number, f_cursor}, {"debugbreak", 1, 1, FEARG_1, arg1_number, ret_number, #ifdef MSWIN f_debugbreak #else NULL #endif }, {"deepcopy", 1, 2, FEARG_1, arg12_deepcopy, ret_copy, f_deepcopy}, {"delete", 1, 2, FEARG_1, arg2_string, ret_number_bool, f_delete}, {"deletebufline", 2, 3, FEARG_1, arg3_buffer_lnum_lnum, ret_number_bool, f_deletebufline}, {"did_filetype", 0, 0, 0, NULL, ret_number_bool, f_did_filetype}, {"diff_filler", 1, 1, FEARG_1, arg1_lnum, ret_number, f_diff_filler}, {"diff_hlID", 2, 2, FEARG_1, arg2_lnum_number, ret_number, f_diff_hlID}, {"digraph_get", 1, 1, FEARG_1, arg1_string, ret_string, f_digraph_get}, {"digraph_getlist",0, 1, FEARG_1, arg1_bool, ret_list_string_items, f_digraph_getlist}, {"digraph_set", 2, 2, FEARG_1, arg2_string, ret_bool, f_digraph_set}, {"digraph_setlist",1, 1, FEARG_1, arg1_list_string, ret_bool, f_digraph_setlist}, {"echoraw", 1, 1, FEARG_1, arg1_string, ret_void, f_echoraw}, {"empty", 1, 1, FEARG_1, NULL, ret_number_bool, f_empty}, {"environ", 0, 0, 0, NULL, ret_dict_string, f_environ}, {"escape", 2, 2, FEARG_1, arg2_string, ret_string, f_escape}, {"eval", 1, 1, FEARG_1, arg1_string, ret_any, f_eval}, {"eventhandler", 0, 0, 0, NULL, ret_number_bool, f_eventhandler}, {"executable", 1, 1, FEARG_1, arg1_string, ret_number, f_executable}, {"execute", 1, 2, FEARG_1, arg12_execute, ret_string, f_execute}, {"exepath", 1, 1, FEARG_1, arg1_string, ret_string, f_exepath}, {"exists", 1, 1, FEARG_1, arg1_string, ret_number_bool, f_exists}, {"exists_compiled", 1, 1, FEARG_1, arg1_string, ret_number_bool, f_exists_compiled}, {"exp", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, f_exp}, {"expand", 1, 3, FEARG_1, arg3_string_bool_bool, ret_any, f_expand}, {"expandcmd", 1, 2, FEARG_1, arg2_string_dict, ret_string, f_expandcmd}, {"extend", 2, 3, FEARG_1, arg23_extend, ret_extend, f_extend}, {"extendnew", 2, 3, FEARG_1, arg23_extendnew, ret_first_cont, f_extendnew}, {"feedkeys", 1, 2, FEARG_1, arg2_string, ret_void, f_feedkeys}, {"file_readable", 1, 1, FEARG_1, arg1_string, // obsolete ret_number_bool, f_filereadable}, {"filereadable", 1, 1, FEARG_1, arg1_string, ret_number_bool, f_filereadable}, {"filewritable", 1, 1, FEARG_1, arg1_string, ret_number, f_filewritable}, {"filter", 2, 2, FEARG_1, arg2_filter, ret_first_arg, f_filter}, {"finddir", 1, 3, FEARG_1, arg3_string_string_number, ret_finddir, f_finddir}, {"findfile", 1, 3, FEARG_1, arg3_string_string_number, ret_any, f_findfile}, {"flatten", 1, 2, FEARG_1, arg2_list_any_number, ret_list_any, f_flatten}, {"flattennew", 1, 2, FEARG_1, arg2_list_any_number, ret_list_any, f_flattennew}, {"float2nr", 1, 1, FEARG_1, arg1_float_or_nr, ret_number, f_float2nr}, {"floor", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, f_floor}, {"fmod", 2, 2, FEARG_1, arg2_float_or_nr, ret_float, f_fmod}, {"fnameescape", 1, 1, FEARG_1, arg1_string, ret_string, f_fnameescape}, {"fnamemodify", 2, 2, FEARG_1, arg2_string, ret_string, f_fnamemodify}, {"foldclosed", 1, 1, FEARG_1, arg1_lnum, ret_number, f_foldclosed}, {"foldclosedend", 1, 1, FEARG_1, arg1_lnum, ret_number, f_foldclosedend}, {"foldlevel", 1, 1, FEARG_1, arg1_lnum, ret_number, f_foldlevel}, {"foldtext", 0, 0, 0, NULL, ret_string, f_foldtext}, {"foldtextresult", 1, 1, FEARG_1, arg1_lnum, ret_string, f_foldtextresult}, {"foreground", 0, 0, 0, NULL, ret_void, f_foreground}, {"fullcommand", 1, 2, FEARG_1, arg2_string_bool, ret_string, f_fullcommand}, {"funcref", 1, 3, FEARG_1, arg3_any_list_dict, ret_func_unknown, f_funcref}, {"function", 1, 3, FEARG_1, arg3_any_list_dict, ret_func_unknown, f_function}, {"garbagecollect", 0, 1, 0, arg1_bool, ret_void, f_garbagecollect}, {"get", 2, 3, FEARG_1, arg23_get, ret_any, f_get}, {"getbufinfo", 0, 1, FEARG_1, arg1_buffer_or_dict_any, ret_list_dict_any, f_getbufinfo}, {"getbufline", 2, 3, FEARG_1, arg3_buffer_lnum_lnum, ret_list_string, f_getbufline}, {"getbufoneline", 2, 2, FEARG_1, arg2_buffer_lnum, ret_string, f_getbufoneline}, {"getbufvar", 2, 3, FEARG_1, arg3_buffer_string_any, ret_any, f_getbufvar}, {"getcellwidths", 0, 0, 0, NULL, ret_list_any, f_getcellwidths}, {"getchangelist", 0, 1, FEARG_1, arg1_buffer, ret_list_any, f_getchangelist}, {"getchar", 0, 1, 0, arg1_bool, ret_any, f_getchar}, {"getcharmod", 0, 0, 0, NULL, ret_number, f_getcharmod}, {"getcharpos", 1, 1, FEARG_1, arg1_string, ret_list_number, f_getcharpos}, {"getcharsearch", 0, 0, 0, NULL, ret_dict_any, f_getcharsearch}, {"getcharstr", 0, 1, 0, arg1_bool, ret_string, f_getcharstr}, {"getcmdcompltype", 0, 0, 0, NULL, ret_string, f_getcmdcompltype}, {"getcmdline", 0, 0, 0, NULL, ret_string, f_getcmdline}, {"getcmdpos", 0, 0, 0, NULL, ret_number, f_getcmdpos}, {"getcmdscreenpos", 0, 0, 0, NULL, ret_number, f_getcmdscreenpos}, {"getcmdtype", 0, 0, 0, NULL, ret_string, f_getcmdtype}, {"getcmdwintype", 0, 0, 0, NULL, ret_string, f_getcmdwintype}, {"getcompletion", 2, 3, FEARG_1, arg3_string_string_bool, ret_list_string, f_getcompletion}, {"getcurpos", 0, 1, FEARG_1, arg1_number, ret_list_number, f_getcurpos}, {"getcursorcharpos", 0, 1, FEARG_1, arg1_number, ret_list_number, f_getcursorcharpos}, {"getcwd", 0, 2, FEARG_1, arg2_number, ret_string, f_getcwd}, {"getenv", 1, 1, FEARG_1, arg1_string, ret_any, f_getenv}, {"getfontname", 0, 1, 0, arg1_string, ret_string, f_getfontname}, {"getfperm", 1, 1, FEARG_1, arg1_string, ret_string, f_getfperm}, {"getfsize", 1, 1, FEARG_1, arg1_string, ret_number, f_getfsize}, {"getftime", 1, 1, FEARG_1, arg1_string, ret_number, f_getftime}, {"getftype", 1, 1, FEARG_1, arg1_string, ret_string, f_getftype}, {"getimstatus", 0, 0, 0, NULL, ret_number_bool, f_getimstatus}, {"getjumplist", 0, 2, FEARG_1, arg2_number, ret_list_any, f_getjumplist}, {"getline", 1, 2, FEARG_1, arg2_lnum, ret_getline, f_getline}, {"getloclist", 1, 2, 0, arg2_number_dict_any, ret_list_or_dict_1, f_getloclist}, {"getmarklist", 0, 1, FEARG_1, arg1_buffer, ret_list_dict_any, f_getmarklist}, {"getmatches", 0, 1, 0, arg1_number, ret_list_dict_any, f_getmatches}, {"getmousepos", 0, 0, 0, NULL, ret_dict_number, f_getmousepos}, {"getmouseshape", 0, 0, 0, NULL, ret_string, f_getmouseshape}, {"getpid", 0, 0, 0, NULL, ret_number, f_getpid}, {"getpos", 1, 1, FEARG_1, arg1_string, ret_list_number, f_getpos}, {"getqflist", 0, 1, 0, arg1_dict_any, ret_list_or_dict_0, f_getqflist}, {"getreg", 0, 3, FEARG_1, arg3_string_bool_bool, ret_getreg, f_getreg}, {"getreginfo", 0, 1, FEARG_1, arg1_string, ret_dict_any, f_getreginfo}, {"getregtype", 0, 1, FEARG_1, arg1_string, ret_string, f_getregtype}, {"getscriptinfo", 0, 1, 0, arg1_dict_any, ret_list_dict_any, f_getscriptinfo}, {"gettabinfo", 0, 1, FEARG_1, arg1_number, ret_list_dict_any, f_gettabinfo}, {"gettabvar", 2, 3, FEARG_1, arg3_number_string_any, ret_any, f_gettabvar}, {"gettabwinvar", 3, 4, FEARG_1, arg4_number_number_string_any, ret_any, f_gettabwinvar}, {"gettagstack", 0, 1, FEARG_1, arg1_number, ret_dict_any, f_gettagstack}, {"gettext", 1, 1, FEARG_1, arg1_string, ret_string, f_gettext}, {"getwininfo", 0, 1, FEARG_1, arg1_number, ret_list_dict_any, f_getwininfo}, {"getwinpos", 0, 1, FEARG_1, arg1_number, ret_list_number, f_getwinpos}, {"getwinposx", 0, 0, 0, NULL, ret_number, f_getwinposx}, {"getwinposy", 0, 0, 0, NULL, ret_number, f_getwinposy}, {"getwinvar", 2, 3, FEARG_1, arg3_number_string_any, ret_any, f_getwinvar}, {"glob", 1, 4, FEARG_1, arg14_glob, ret_any, f_glob}, {"glob2regpat", 1, 1, FEARG_1, arg1_string, ret_string, f_glob2regpat}, {"globpath", 2, 5, FEARG_2, arg25_globpath, ret_any, f_globpath}, {"has", 1, 2, 0, arg2_string_bool, ret_number_bool, f_has}, {"has_key", 2, 2, FEARG_1, arg2_dict_any_string_or_nr, ret_number_bool, f_has_key}, {"haslocaldir", 0, 2, FEARG_1, arg2_number, ret_number, f_haslocaldir}, {"hasmapto", 1, 3, FEARG_1, arg3_string_string_bool, ret_number_bool, f_hasmapto}, {"highlightID", 1, 1, FEARG_1, arg1_string, // obsolete ret_number, f_hlID}, {"highlight_exists",1, 1, FEARG_1, arg1_string, // obsolete ret_number_bool, f_hlexists}, {"histadd", 2, 2, FEARG_2, arg2_string, ret_number_bool, f_histadd}, {"histdel", 1, 2, FEARG_1, arg2_string_string_or_number, ret_number_bool, f_histdel}, {"histget", 1, 2, FEARG_1, arg2_string_number, ret_string, f_histget}, {"histnr", 1, 1, FEARG_1, arg1_string, ret_number, f_histnr}, {"hlID", 1, 1, FEARG_1, arg1_string, ret_number, f_hlID}, {"hlexists", 1, 1, FEARG_1, arg1_string, ret_number_bool, f_hlexists}, {"hlget", 0, 2, FEARG_1, arg2_string_bool, ret_list_dict_any, f_hlget}, {"hlset", 1, 1, FEARG_1, arg1_list_any, ret_number_bool, f_hlset}, {"hostname", 0, 0, 0, NULL, ret_string, f_hostname}, {"iconv", 3, 3, FEARG_1, arg3_string, ret_string, f_iconv}, {"indent", 1, 1, FEARG_1, arg1_lnum, ret_number, f_indent}, {"index", 2, 4, FEARG_1, arg24_index, ret_number, f_index}, {"indexof", 2, 3, FEARG_1, arg23_index, ret_number, f_indexof}, {"input", 1, 3, FEARG_1, arg3_string, ret_string, f_input}, {"inputdialog", 1, 3, FEARG_1, arg3_string, ret_string, f_inputdialog}, {"inputlist", 1, 1, FEARG_1, arg1_list_string, ret_number, f_inputlist}, {"inputrestore", 0, 0, 0, NULL, ret_number_bool, f_inputrestore}, {"inputsave", 0, 0, 0, NULL, ret_number_bool, f_inputsave}, {"inputsecret", 1, 2, FEARG_1, arg2_string, ret_string, f_inputsecret}, {"insert", 2, 3, FEARG_1, arg23_insert, ret_first_arg, f_insert}, {"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}, {"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_string, 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}, {"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_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, 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}, {"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, 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, NULL, ret_number, f_type}, {"typename", 1, 1, FEARG_1, NULL, ret_string, f_typename}, {"undofile", 1, 1, FEARG_1, arg1_string, ret_string, f_undofile}, {"undotree", 0, 0, 0, NULL, ret_dict_any, f_undotree}, {"uniq", 1, 3, FEARG_1, arg13_sortuniq, ret_first_arg, f_uniq}, {"values", 1, 1, FEARG_1, arg1_dict_any, ret_list_member, f_values}, {"virtcol", 1, 2, FEARG_1, arg2_string_or_list_bool, 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}, }; /* * 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) { 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; ++i) if (argchecks[i] != NULL) { 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; 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; } 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; 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 == 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 (global_functions[fi].f_argtype == FEARG_2) { // 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_3) { // 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_4) { // 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; global_functions[fi].f_func(argv, rettv); return FCERR_NONE; } /* * Return TRUE for a non-zero Number and a non-empty String. */ int non_zero_arg(typval_T *argvars) { return ((argvars[0].v_type == VAR_NUMBER && argvars[0].vval.v_number != 0) || (argvars[0].v_type == VAR_BOOL && argvars[0].vval.v_number == VVAL_TRUE) || (argvars[0].v_type == VAR_STRING && argvars[0].vval.v_string != NULL && *argvars[0].vval.v_string != NUL)); } /* * "and(expr, expr)" function */ static void f_and(typval_T *argvars, typval_T *rettv) { if (in_vim9script() && (check_for_number_arg(argvars, 0) == FAIL || check_for_number_arg(argvars, 1) == FAIL)) return; rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) & tv_get_number_chk(&argvars[1], NULL); } /* * "balloon_show()" function */ #ifdef FEAT_BEVAL static void f_balloon_gettext(typval_T *argvars UNUSED, typval_T *rettv) { rettv->v_type = VAR_STRING; if (balloonEval == NULL) return; if (balloonEval->msg == NULL) rettv->vval.v_string = NULL; else rettv->vval.v_string = vim_strsave(balloonEval->msg); } static void f_balloon_show(typval_T *argvars, typval_T *rettv UNUSED) { if (balloonEval == NULL) return; if (in_vim9script() && check_for_string_or_list_arg(argvars, 0) == FAIL) return; if (argvars[0].v_type == VAR_LIST # ifdef FEAT_GUI && !gui.in_use # endif ) { list_T *l = argvars[0].vval.v_list; // empty list removes the balloon post_balloon(balloonEval, NULL, l == NULL || l->lv_len == 0 ? NULL : l); } else { char_u *mesg; if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) return; mesg = tv_get_string_chk(&argvars[0]); if (mesg != NULL) // empty string removes the balloon post_balloon(balloonEval, *mesg == NUL ? NULL : mesg, NULL); } } # if defined(FEAT_BEVAL_TERM) static void f_balloon_split(typval_T *argvars, typval_T *rettv UNUSED) { if (rettv_list_alloc(rettv) != OK) return; char_u *msg; if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) return; msg = tv_get_string_chk(&argvars[0]); if (msg != NULL) { pumitem_T *array; int size = split_message(msg, &array); // Skip the first and last item, they are always empty. for (int i = 1; i < size - 1; ++i) list_append_string(rettv->vval.v_list, array[i].pum_text, -1); while (size > 0) vim_free(array[--size].pum_text); vim_free(array); } } # endif #endif /* * Get the buffer from "arg" and give an error and return NULL if it is not * valid. */ buf_T * get_buf_arg(typval_T *arg) { buf_T *buf; ++emsg_off; buf = tv_get_buf(arg, FALSE); --emsg_off; if (buf == NULL) semsg(_(e_invalid_buffer_name_str), tv_get_string(arg)); return buf; } /* * "byte2line(byte)" function */ static void f_byte2line(typval_T *argvars UNUSED, typval_T *rettv) { #ifndef FEAT_BYTEOFF rettv->vval.v_number = -1; #else long boff = 0; if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL) return; boff = tv_get_number(&argvars[0]) - 1; // boff gets -1 on type error if (boff < 0) rettv->vval.v_number = -1; else rettv->vval.v_number = ml_find_line_or_offset(curbuf, (linenr_T)0, &boff); #endif } /* * "call(func, arglist [, dict])" function */ static void f_call(typval_T *argvars, typval_T *rettv) { char_u *func; partial_T *partial = NULL; dict_T *selfdict = NULL; char_u *dot; char_u *tofree = NULL; if (in_vim9script() && (check_for_string_or_func_arg(argvars, 0) == FAIL || check_for_list_arg(argvars, 1) == FAIL || check_for_opt_dict_arg(argvars, 2) == FAIL)) return; if (check_for_list_arg(argvars, 1) == FAIL) return; if (argvars[1].vval.v_list == NULL) return; if (argvars[0].v_type == VAR_FUNC) func = argvars[0].vval.v_string; else if (argvars[0].v_type == VAR_PARTIAL) { partial = argvars[0].vval.v_partial; func = partial_name(partial); } else func = tv_get_string(&argvars[0]); if (func == NULL || *func == NUL) return; // type error, empty name or null function dot = vim_strchr(func, '.'); if (dot != NULL) { imported_T *import = find_imported(func, dot - func, TRUE); if (import != NULL && SCRIPT_ID_VALID(import->imp_sid)) { scriptitem_T *si = SCRIPT_ITEM(import->imp_sid); if (si->sn_autoload_prefix != NULL) { // Turn "import.Func" into "scriptname#Func". tofree = concat_str(si->sn_autoload_prefix, dot + 1); if (tofree == NULL) return; func = tofree; } } } if (argvars[2].v_type != VAR_UNKNOWN) { if (check_for_dict_arg(argvars, 2) == FAIL) return; selfdict = argvars[2].vval.v_dict; } (void)func_call(func, &argvars[1], partial, selfdict, rettv); vim_free(tofree); } /* * "changenr()" function */ static void f_changenr(typval_T *argvars UNUSED, typval_T *rettv) { rettv->vval.v_number = curbuf->b_u_seq_cur; } /* * "char2nr(string)" function */ static void f_char2nr(typval_T *argvars, typval_T *rettv) { if (in_vim9script() && (check_for_string_arg(argvars, 0) == FAIL || check_for_opt_bool_arg(argvars, 1) == FAIL)) return; if (has_mbyte) { int utf8 = 0; if (argvars[1].v_type != VAR_UNKNOWN) utf8 = (int)tv_get_bool_chk(&argvars[1], NULL); if (utf8) rettv->vval.v_number = utf_ptr2char(tv_get_string(&argvars[0])); else rettv->vval.v_number = (*mb_ptr2char)(tv_get_string(&argvars[0])); } else rettv->vval.v_number = tv_get_string(&argvars[0])[0]; } /* * Get the current cursor column and store it in 'rettv'. If 'charcol' is TRUE, * returns the character index of the column. Otherwise, returns the byte index * of the column. */ static void get_col(typval_T *argvars, typval_T *rettv, int charcol) { colnr_T col = 0; pos_T *fp; switchwin_T switchwin; int winchanged = FALSE; if (check_for_string_or_list_arg(argvars, 0) == FAIL || check_for_opt_number_arg(argvars, 1) == FAIL) return; if (argvars[1].v_type != VAR_UNKNOWN) { tabpage_T *tp; win_T *wp; // use the window specified in the second argument wp = win_id2wp_tp((int)tv_get_number(&argvars[1]), &tp); if (wp == NULL || tp == NULL) return; if (switch_win_noblock(&switchwin, wp, tp, TRUE) != OK) return; check_cursor(); winchanged = TRUE; } int fnum = curbuf->b_fnum; fp = var2fpos(&argvars[0], FALSE, &fnum, charcol); if (fp != NULL && fnum == curbuf->b_fnum) { if (fp->col == MAXCOL) { // '> can be MAXCOL, get the length of the line then if (fp->lnum <= curbuf->b_ml.ml_line_count) col = (colnr_T)STRLEN(ml_get(fp->lnum)) + 1; else col = MAXCOL; } else { col = fp->col + 1; // col(".") when the cursor is on the NUL at the end of the line // because of "coladd" can be seen as an extra column. if (virtual_active() && fp == &curwin->w_cursor) { char_u *p = ml_get_cursor(); if (curwin->w_cursor.coladd >= (colnr_T)chartabsize(p, curwin->w_virtcol - curwin->w_cursor.coladd)) { int l; if (*p != NUL && p[(l = (*mb_ptr2len)(p))] == NUL) col += l; } } } } rettv->vval.v_number = col; if (winchanged) restore_win_noblock(&switchwin, TRUE); } /* * "charcol()" function */ static void f_charcol(typval_T *argvars, typval_T *rettv) { get_col(argvars, rettv, TRUE); } win_T * get_optional_window(typval_T *argvars, int idx) { win_T *win = curwin; if (argvars[idx].v_type == VAR_UNKNOWN) return curwin; win = find_win_by_nr_or_id(&argvars[idx]); if (win == NULL) { emsg(_(e_invalid_window_number)); return NULL; } return win; } /* * "col(string)" function */ static void f_col(typval_T *argvars, typval_T *rettv) { get_col(argvars, rettv, FALSE); } /* * "confirm(message, buttons[, default [, type]])" function */ static void f_confirm(typval_T *argvars UNUSED, typval_T *rettv UNUSED) { #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) char_u *message; char_u *buttons = NULL; char_u buf[NUMBUFLEN]; char_u buf2[NUMBUFLEN]; int def = 1; int type = VIM_GENERIC; char_u *typestr; int error = FALSE; if (in_vim9script() && (check_for_string_arg(argvars, 0) == FAIL || (check_for_opt_string_arg(argvars, 1) == FAIL || (argvars[1].v_type != VAR_UNKNOWN && (check_for_opt_number_arg(argvars, 2) == FAIL || (argvars[2].v_type != VAR_UNKNOWN && check_for_opt_string_arg(argvars, 3) == FAIL)))))) return; message = tv_get_string_chk(&argvars[0]); if (message == NULL) error = TRUE; if (argvars[1].v_type != VAR_UNKNOWN) { buttons = tv_get_string_buf_chk(&argvars[1], buf); if (buttons == NULL) error = TRUE; if (argvars[2].v_type != VAR_UNKNOWN) { def = (int)tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { typestr = tv_get_string_buf_chk(&argvars[3], buf2); if (typestr == NULL) error = TRUE; else { switch (TOUPPER_ASC(*typestr)) { case 'E': type = VIM_ERROR; break; case 'Q': type = VIM_QUESTION; break; case 'I': type = VIM_INFO; break; case 'W': type = VIM_WARNING; break; case 'G': type = VIM_GENERIC; break; } } } } } if (buttons == NULL || *buttons == NUL) buttons = (char_u *)_("&Ok"); if (!error) rettv->vval.v_number = do_dialog(type, NULL, message, buttons, def, NULL, FALSE); #endif } /* * "copy()" function */ static void f_copy(typval_T *argvars, typval_T *rettv) { item_copy(&argvars[0], rettv, FALSE, TRUE, 0); } /* * Set the cursor position. * If 'charcol' is TRUE, then use the column number as a character offset. * Otherwise use the column number as a byte offset. */ static void set_cursorpos(typval_T *argvars, typval_T *rettv, int charcol) { long lnum, col; long coladd = 0; int set_curswant = TRUE; if (in_vim9script() && (check_for_string_or_number_or_list_arg(argvars, 0) == FAIL || check_for_opt_number_arg(argvars, 1) == FAIL || (argvars[1].v_type != VAR_UNKNOWN && check_for_opt_number_arg(argvars, 2) == FAIL))) return; rettv->vval.v_number = -1; if (argvars[0].v_type == VAR_LIST) { pos_T pos; colnr_T curswant = -1; if (list2fpos(argvars, &pos, NULL, &curswant, charcol) == FAIL) { emsg(_(e_invalid_argument)); return; } lnum = pos.lnum; col = pos.col; coladd = pos.coladd; if (curswant >= 0) { curwin->w_curswant = curswant - 1; set_curswant = FALSE; } } else if ((argvars[0].v_type == VAR_NUMBER || argvars[0].v_type == VAR_STRING) && (argvars[1].v_type == VAR_NUMBER || argvars[1].v_type == VAR_STRING)) { lnum = tv_get_lnum(argvars); if (lnum < 0) semsg(_(e_invalid_argument_str), tv_get_string(&argvars[0])); else if (lnum == 0) lnum = curwin->w_cursor.lnum; col = (long)tv_get_number_chk(&argvars[1], NULL); if (charcol) col = buf_charidx_to_byteidx(curbuf, lnum, col) + 1; if (argvars[2].v_type != VAR_UNKNOWN) coladd = (long)tv_get_number_chk(&argvars[2], NULL); } else { emsg(_(e_invalid_argument)); return; } if (lnum < 0 || col < 0 || coladd < 0) return; // type error; errmsg already given if (lnum > 0) curwin->w_cursor.lnum = lnum; if (col > 0) curwin->w_cursor.col = col - 1; curwin->w_cursor.coladd = coladd; // Make sure the cursor is in a valid position. check_cursor(); // Correct cursor for multi-byte character. if (has_mbyte) mb_adjust_cursor(); curwin->w_set_curswant = set_curswant; rettv->vval.v_number = 0; } /* * "cursor(lnum, col)" function, or * "cursor(list)" * * Moves the cursor to the specified line and column. * Returns 0 when the position could be set, -1 otherwise. */ static void f_cursor(typval_T *argvars, typval_T *rettv) { set_cursorpos(argvars, rettv, FALSE); } #ifdef MSWIN /* * "debugbreak()" function */ static void f_debugbreak(typval_T *argvars, typval_T *rettv) { int pid; rettv->vval.v_number = FAIL; if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL) return; pid = (int)tv_get_number(&argvars[0]); if (pid == 0) { emsg(_(e_invalid_argument)); return; } HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid); if (hProcess == NULL) return; DebugBreakProcess(hProcess); CloseHandle(hProcess); rettv->vval.v_number = OK; } #endif /* * "deepcopy()" function */ static void f_deepcopy(typval_T *argvars, typval_T *rettv) { varnumber_T noref = 0; if (check_for_opt_bool_arg(argvars, 1) == FAIL) return; if (argvars[1].v_type != VAR_UNKNOWN) noref = tv_get_bool_chk(&argvars[1], NULL); item_copy(&argvars[0], rettv, TRUE, TRUE, noref == 0 ? get_copyID() : 0); } /* * "did_filetype()" function */ static void f_did_filetype(typval_T *argvars UNUSED, typval_T *rettv UNUSED) { rettv->vval.v_number = did_filetype; } /* * "echoraw({expr})" function */ static void f_echoraw(typval_T *argvars, typval_T *rettv UNUSED) { char_u *str; if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) return; str = tv_get_string_chk(&argvars[0]); if (str != NULL && *str != NUL) { out_str(str); out_flush(); } } /* * "empty({expr})" function */ static void f_empty(typval_T *argvars, typval_T *rettv) { int n = FALSE; switch (argvars[0].v_type) { case VAR_STRING: case VAR_FUNC: n = argvars[0].vval.v_string == NULL || *argvars[0].vval.v_string == NUL; break; case VAR_PARTIAL: n = FALSE; break; case VAR_NUMBER: n = argvars[0].vval.v_number == 0; break; case VAR_FLOAT: n = argvars[0].vval.v_float == 0.0; break; case VAR_LIST: n = argvars[0].vval.v_list == NULL || argvars[0].vval.v_list->lv_len == 0; break; case VAR_DICT: n = argvars[0].vval.v_dict == NULL || argvars[0].vval.v_dict->dv_hashtab.ht_used == 0; break; case VAR_BOOL: case VAR_SPECIAL: n = argvars[0].vval.v_number != VVAL_TRUE; break; case VAR_CLASS: n = argvars[0].vval.v_class != NULL; break; case VAR_OBJECT: n = argvars[0].vval.v_object != NULL; break; case VAR_BLOB: n = argvars[0].vval.v_blob == NULL || argvars[0].vval.v_blob->bv_ga.ga_len == 0; break; case VAR_JOB: #ifdef FEAT_JOB_CHANNEL n = argvars[0].vval.v_job == NULL || argvars[0].vval.v_job->jv_status != JOB_STARTED; break; #endif case VAR_CHANNEL: #ifdef FEAT_JOB_CHANNEL n = argvars[0].vval.v_channel == NULL || !channel_is_open(argvars[0].vval.v_channel); break; #endif case VAR_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 } /* * "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_refcount = 1; if (arg_pt != NULL && arg_pt->pt_func != NULL) { pt->pt_func = arg_pt->pt_func; func_ptr_ref(pt->pt_func); vim_free(name); } else if (is_funcref) { pt->pt_func = find_func(trans_name, is_global); func_ptr_ref(pt->pt_func); vim_free(name); } else { pt->pt_name = name; func_ref(name); } if (arg_pt != NULL) { pt->pt_outer_partial = arg_pt; ++arg_pt->pt_refcount; } } rettv->v_type = VAR_PARTIAL; rettv->vval.v_partial = pt; } else { // result is a VAR_FUNC rettv->v_type = VAR_FUNC; rettv->vval.v_string = name; func_ref(name); } } theend: vim_free(trans_name); } /* * "funcref()" function */ static void f_funcref(typval_T *argvars, typval_T *rettv) { common_function(argvars, rettv, TRUE); } /* * "function()" function */ static void f_function(typval_T *argvars, typval_T *rettv) { common_function(argvars, rettv, FALSE); } /* * "garbagecollect()" function */ static void f_garbagecollect(typval_T *argvars, typval_T *rettv UNUSED) { if (in_vim9script() && check_for_opt_bool_arg(argvars, 0) == FAIL) return; // This is postponed until we are back at the toplevel, because we may be // using Lists and Dicts internally. E.g.: ":echo [garbagecollect()]". want_garbage_collect = TRUE; if (argvars[0].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[0]) == 1) garbage_collect_at_exit = TRUE; } /* * "get()" function */ static void f_get(typval_T *argvars, typval_T *rettv) { listitem_T *li; list_T *l; dictitem_T *di; dict_T *d; typval_T *tv = NULL; int what_is_dict = FALSE; if (argvars[0].v_type == VAR_BLOB) { int error = FALSE; int idx = tv_get_number_chk(&argvars[1], &error); if (!error) { rettv->v_type = VAR_NUMBER; if (idx < 0) idx = blob_len(argvars[0].vval.v_blob) + idx; if (idx < 0 || idx >= blob_len(argvars[0].vval.v_blob)) rettv->vval.v_number = -1; else { rettv->vval.v_number = blob_get(argvars[0].vval.v_blob, idx); tv = rettv; } } } else if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) != NULL) { int error = FALSE; li = list_find(l, (long)tv_get_number_chk(&argvars[1], &error)); if (!error && li != NULL) tv = &li->li_tv; } } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) != NULL) { di = dict_find(d, tv_get_string(&argvars[1]), -1); if (di != NULL) tv = &di->di_tv; } } else if (argvars[0].v_type == VAR_PARTIAL || argvars[0].v_type == VAR_FUNC) { partial_T *pt; partial_T fref_pt; if (argvars[0].v_type == VAR_PARTIAL) pt = argvars[0].vval.v_partial; else { CLEAR_FIELD(fref_pt); fref_pt.pt_name = argvars[0].vval.v_string; pt = &fref_pt; } if (pt != NULL) { char_u *what = tv_get_string(&argvars[1]); if (STRCMP(what, "func") == 0 || STRCMP(what, "name") == 0) { char_u *name = partial_name(pt); rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING); if (name == NULL) rettv->vval.v_string = NULL; else { if (rettv->v_type == VAR_FUNC) func_ref(name); if (*what == 'n' && pt->pt_name == NULL && pt->pt_func != NULL) // use <SNR> instead of the byte code name = printable_func_name(pt->pt_func); rettv->vval.v_string = vim_strsave(name); } } else if (STRCMP(what, "dict") == 0) { what_is_dict = TRUE; if (pt->pt_dict != NULL) rettv_dict_set(rettv, pt->pt_dict); } else if (STRCMP(what, "args") == 0) { rettv->v_type = VAR_LIST; if (rettv_list_alloc(rettv) == OK) { int i; for (i = 0; i < pt->pt_argc; ++i) list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]); } } else semsg(_(e_invalid_argument_str), what); // When {what} == "dict" and pt->pt_dict == NULL, evaluate the // third argument if (!what_is_dict) return; } } else semsg(_(e_argument_of_str_must_be_list_dictionary_or_blob), "get()"); if (tv == NULL) { if (argvars[2].v_type != VAR_UNKNOWN) copy_tv(&argvars[2], rettv); } else copy_tv(tv, rettv); } /* * "getchangelist()" function */ static void f_getchangelist(typval_T *argvars, typval_T *rettv) { buf_T *buf; int i; list_T *l; dict_T *d; int changelistindex; if (rettv_list_alloc(rettv) == FAIL) return; if (in_vim9script() && check_for_opt_buffer_arg(argvars, 0) == FAIL) return; if (argvars[0].v_type == VAR_UNKNOWN) buf = curbuf; else buf = tv_get_buf_from_arg(&argvars[0]); if (buf == NULL) return; l = list_alloc(); if (l == NULL) return; if (list_append_list(rettv->vval.v_list, l) == FAIL) { vim_free(l); return; } /* * The current window change list index tracks only the position for the * current buffer. For other buffers use the stored index for the current * window, or, if that's not available, the change list length. */ if (buf == curwin->w_buffer) { changelistindex = curwin->w_changelistidx; } else { wininfo_T *wip; FOR_ALL_BUF_WININFO(buf, wip) if (wip->wi_win == curwin) break; changelistindex = wip != NULL ? wip->wi_changelistidx : buf->b_changelistlen; } list_append_number(rettv->vval.v_list, (varnumber_T)changelistindex); for (i = 0; i < buf->b_changelistlen; ++i) { if (buf->b_changelist[i].lnum == 0) continue; if ((d = dict_alloc()) == NULL) return; if (list_append_dict(l, d) == FAIL) return; dict_add_number(d, "lnum", (long)buf->b_changelist[i].lnum); dict_add_number(d, "col", (long)buf->b_changelist[i].col); dict_add_number(d, "coladd", (long)buf->b_changelist[i].coladd); } } static void getpos_both( typval_T *argvars, typval_T *rettv, int getcurpos, int charcol) { pos_T *fp = NULL; pos_T pos; win_T *wp = curwin; list_T *l; int fnum = -1; if (rettv_list_alloc(rettv) == OK) { l = rettv->vval.v_list; if (getcurpos) { if (argvars[0].v_type != VAR_UNKNOWN) { wp = find_win_by_nr_or_id(&argvars[0]); if (wp != NULL) fp = &wp->w_cursor; } else fp = &curwin->w_cursor; if (fp != NULL && charcol) { pos = *fp; pos.col = buf_byteidx_to_charidx(wp->w_buffer, pos.lnum, pos.col); fp = &pos; } } else fp = var2fpos(&argvars[0], TRUE, &fnum, charcol); if (fnum != -1) list_append_number(l, (varnumber_T)fnum); else list_append_number(l, (varnumber_T)0); list_append_number(l, (fp != NULL) ? (varnumber_T)fp->lnum : (varnumber_T)0); list_append_number(l, (fp != NULL) ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) : (varnumber_T)0); list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); if (getcurpos) { int save_set_curswant = curwin->w_set_curswant; colnr_T save_curswant = curwin->w_curswant; colnr_T save_virtcol = curwin->w_virtcol; if (wp == curwin) update_curswant(); list_append_number(l, wp == NULL ? 0 : wp->w_curswant == MAXCOL ? (varnumber_T)MAXCOL : (varnumber_T)wp->w_curswant + 1); // Do not change "curswant", as it is unexpected that a get // function has a side effect. if (wp == curwin && save_set_curswant) { curwin->w_set_curswant = save_set_curswant; curwin->w_curswant = save_curswant; curwin->w_virtcol = save_virtcol; curwin->w_valid &= ~VALID_VIRTCOL; } } } else rettv->vval.v_number = FALSE; } /* * "getcharpos()" function */ static void f_getcharpos(typval_T *argvars UNUSED, typval_T *rettv) { if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) return; getpos_both(argvars, rettv, FALSE, TRUE); } /* * "getcharsearch()" function */ static void f_getcharsearch(typval_T *argvars UNUSED, typval_T *rettv) { if (rettv_dict_alloc(rettv) != OK) return; dict_T *dict = rettv->vval.v_dict; dict_add_string(dict, "char", last_csearch()); dict_add_number(dict, "forward", last_csearch_forward()); dict_add_number(dict, "until", last_csearch_until()); } /* * "getenv()" function */ static void f_getenv(typval_T *argvars, typval_T *rettv) { int mustfree = FALSE; char_u *p; if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) return; p = vim_getenv(tv_get_string(&argvars[0]), &mustfree); if (p == NULL) { rettv->v_type = VAR_SPECIAL; rettv->vval.v_number = VVAL_NULL; return; } if (!mustfree) p = vim_strsave(p); rettv->vval.v_string = p; rettv->v_type = VAR_STRING; } /* * "getfontname()" function */ static void f_getfontname(typval_T *argvars UNUSED, typval_T *rettv) { rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL) return; #ifdef FEAT_GUI if (gui.in_use) { GuiFont font; char_u *name = NULL; if (argvars[0].v_type == VAR_UNKNOWN) { // Get the "Normal" font. Either the name saved by // hl_set_font_name() or from the font ID. font = gui.norm_font; name = hl_get_font_name(); } else { name = tv_get_string(&argvars[0]); if (STRCMP(name, "*") == 0) // don't use font dialog return; font = gui_mch_get_font(name, FALSE); if (font == NOFONT) return; // Invalid font name, return empty string. } rettv->vval.v_string = gui_mch_get_fontname(font, name); if (argvars[0].v_type != VAR_UNKNOWN) gui_mch_free_font(font); } #endif } /* * "getjumplist()" function */ static void f_getjumplist(typval_T *argvars, typval_T *rettv) { win_T *wp; int i; list_T *l; dict_T *d; if (rettv_list_alloc(rettv) == FAIL) return; if (in_vim9script() && (check_for_opt_number_arg(argvars, 0) == FAIL || (argvars[0].v_type != VAR_UNKNOWN && check_for_opt_number_arg(argvars, 1) == FAIL))) return; wp = find_tabwin(&argvars[0], &argvars[1], NULL); if (wp == NULL) return; cleanup_jumplist(wp, TRUE); l = list_alloc(); if (l == NULL) return; if (list_append_list(rettv->vval.v_list, l) == FAIL) { vim_free(l); return; } list_append_number(rettv->vval.v_list, (varnumber_T)wp->w_jumplistidx); for (i = 0; i < wp->w_jumplistlen; ++i) { if (wp->w_jumplist[i].fmark.mark.lnum == 0) continue; if ((d = dict_alloc()) == NULL) return; if (list_append_dict(l, d) == FAIL) return; dict_add_number(d, "lnum", (long)wp->w_jumplist[i].fmark.mark.lnum); dict_add_number(d, "col", (long)wp->w_jumplist[i].fmark.mark.col); dict_add_number(d, "coladd", (long)wp->w_jumplist[i].fmark.mark.coladd); dict_add_number(d, "bufnr", (long)wp->w_jumplist[i].fmark.fnum); if (wp->w_jumplist[i].fname != NULL) dict_add_string(d, "filename", wp->w_jumplist[i].fname); } } /* * "getpid()" function */ static void f_getpid(typval_T *argvars UNUSED, typval_T *rettv) { rettv->vval.v_number = mch_get_pid(); } /* * "getcurpos()" function */ static void f_getcurpos(typval_T *argvars, typval_T *rettv) { if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL) return; getpos_both(argvars, rettv, TRUE, FALSE); } static void f_getcursorcharpos(typval_T *argvars, typval_T *rettv) { if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL) return; getpos_both(argvars, rettv, TRUE, TRUE); } /* * "getpos(string)" function */ static void f_getpos(typval_T *argvars, typval_T *rettv) { if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) return; getpos_both(argvars, rettv, FALSE, FALSE); } /* * Common between getreg(), getreginfo() and getregtype(): get the register * name from the first argument. * Returns zero on error. */ static int getreg_get_regname(typval_T *argvars) { char_u *strregname; if (argvars[0].v_type != VAR_UNKNOWN) { strregname = tv_get_string_chk(&argvars[0]); if (strregname != NULL && in_vim9script() && STRLEN(strregname) > 1) { semsg(_(e_register_name_must_be_one_char_str), strregname); strregname = NULL; } if (strregname == NULL) // type error; errmsg already given return 0; } else // Default to v:register strregname = get_vim_var_str(VV_REG); return *strregname == 0 ? '"' : *strregname; } /* * "getreg()" function */ static void f_getreg(typval_T *argvars, typval_T *rettv) { int regname; int arg2 = FALSE; int return_list = FALSE; if (in_vim9script() && (check_for_opt_string_arg(argvars, 0) == FAIL || (argvars[0].v_type != VAR_UNKNOWN && (check_for_opt_bool_arg(argvars, 1) == FAIL || (argvars[1].v_type != VAR_UNKNOWN && check_for_opt_bool_arg(argvars, 2) == FAIL))))) return; regname = getreg_get_regname(argvars); if (regname == 0) return; if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_UNKNOWN) { int error = FALSE; arg2 = (int)tv_get_bool_chk(&argvars[1], &error); if (!error && argvars[2].v_type != VAR_UNKNOWN) return_list = (int)tv_get_bool_chk(&argvars[2], &error); if (error) return; } if (return_list) { rettv->v_type = VAR_LIST; rettv->vval.v_list = (list_T *)get_reg_contents(regname, (arg2 ? GREG_EXPR_SRC : 0) | GREG_LIST); if (rettv->vval.v_list == NULL) (void)rettv_list_alloc(rettv); else ++rettv->vval.v_list->lv_refcount; } else { rettv->v_type = VAR_STRING; rettv->vval.v_string = get_reg_contents(regname, arg2 ? GREG_EXPR_SRC : 0); } } /* * "getregtype()" function */ static void f_getregtype(typval_T *argvars, typval_T *rettv) { int regname; char_u buf[NUMBUFLEN + 2]; long reglen = 0; // on error return an empty string rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL) return; regname = getreg_get_regname(argvars); if (regname == 0) return; buf[0] = NUL; buf[1] = NUL; switch (get_reg_type(regname, ®len)) { case MLINE: buf[0] = 'V'; break; case MCHAR: buf[0] = 'v'; break; case MBLOCK: buf[0] = Ctrl_V; sprintf((char *)buf + 1, "%ld", reglen + 1); break; } rettv->vval.v_string = vim_strsave(buf); } /* * "gettagstack()" function */ static void f_gettagstack(typval_T *argvars, typval_T *rettv) { win_T *wp = curwin; // default is current window if (rettv_dict_alloc(rettv) == FAIL) return; if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL) return; if (argvars[0].v_type != VAR_UNKNOWN) { wp = find_win_by_nr_or_id(&argvars[0]); if (wp == NULL) return; } get_tagstack(wp, rettv->vval.v_dict); } /* * "gettext()" function */ static void f_gettext(typval_T *argvars, typval_T *rettv) { if (check_for_nonempty_string_arg(argvars, 0) == FAIL) return; rettv->v_type = VAR_STRING; rettv->vval.v_string = vim_strsave((char_u *)_(argvars[0].vval.v_string)); } // for VIM_VERSION_ defines #include "version.h" /* * "has()" function */ void f_has(typval_T *argvars, typval_T *rettv) { int i; char_u *name; int x = FALSE; int n = FALSE; typedef struct { char *name; short present; } has_item_T; static has_item_T has_list[] = { {"amiga", #ifdef AMIGA 1 #else 0 #endif }, {"arp", #if defined(AMIGA) && defined(FEAT_ARP) 1 #else 0 #endif }, {"haiku", #ifdef __HAIKU__ 1 #else 0 #endif }, {"bsd", #if defined(BSD) && !defined(MACOS_X) 1 #else 0 #endif }, {"hpux", #ifdef hpux 1 #else 0 #endif }, {"linux", #ifdef __linux__ 1 #else 0 #endif }, {"mac", // Mac OS X (and, once, Mac OS Classic) #ifdef MACOS_X 1 #else 0 #endif }, {"osx", // Mac OS X #ifdef MACOS_X 1 #else 0 #endif }, {"macunix", // Mac OS X, with the darwin feature #if defined(MACOS_X) && defined(MACOS_X_DARWIN) 1 #else 0 #endif }, {"osxdarwin", // synonym for macunix #if defined(MACOS_X) && defined(MACOS_X_DARWIN) 1 #else 0 #endif }, {"qnx", #ifdef __QNX__ 1 #else 0 #endif }, {"sun", #ifdef SUN_SYSTEM 1 #else 0 #endif }, {"unix", #ifdef UNIX 1 #else 0 #endif }, {"vms", #ifdef VMS 1 #else 0 #endif }, {"win32", #ifdef MSWIN 1 #else 0 #endif }, {"win32unix", #ifdef WIN32UNIX 1 #else 0 #endif }, {"win64", #ifdef _WIN64 1 #else 0 #endif }, {"ebcdic", 0 }, {"fname_case", #ifndef CASE_INSENSITIVE_FILENAME 1 #else 0 #endif }, {"acl", #ifdef HAVE_ACL 1 #else 0 #endif }, {"arabic", #ifdef FEAT_ARABIC 1 #else 0 #endif }, {"autocmd", 1}, {"autochdir", #ifdef FEAT_AUTOCHDIR 1 #else 0 #endif }, {"autoservername", #ifdef FEAT_AUTOSERVERNAME 1 #else 0 #endif }, {"balloon_eval", #ifdef FEAT_BEVAL_GUI 1 #else 0 #endif }, {"balloon_multiline", #ifdef FEAT_BEVAL_GUI 1 #else 0 #endif }, {"balloon_eval_term", #ifdef FEAT_BEVAL_TERM 1 #else 0 #endif }, {"builtin_terms", 1}, {"all_builtin_terms", 1}, {"browsefilter", #if defined(FEAT_BROWSE) && (defined(USE_FILE_CHOOSER) \ || defined(FEAT_GUI_MSWIN) \ || defined(FEAT_GUI_MOTIF)) 1 #else 0 #endif }, {"byte_offset", #ifdef FEAT_BYTEOFF 1 #else 0 #endif }, {"channel", #ifdef FEAT_JOB_CHANNEL 1 #else 0 #endif }, {"cindent", 1}, {"clientserver", #ifdef FEAT_CLIENTSERVER 1 #else 0 #endif }, {"clipboard", #ifdef FEAT_CLIPBOARD 1 #else 0 #endif }, {"cmdline_compl", 1}, {"cmdline_hist", 1}, {"cmdwin", 1}, {"comments", 1}, {"conceal", #ifdef FEAT_CONCEAL 1 #else 0 #endif }, {"cryptv", #ifdef FEAT_CRYPT 1 #else 0 #endif }, {"crypt-blowfish", #ifdef FEAT_CRYPT 1 #else 0 #endif }, {"crypt-blowfish2", #ifdef FEAT_CRYPT 1 #else 0 #endif }, {"cscope", #ifdef FEAT_CSCOPE 1 #else 0 #endif }, {"cursorbind", 1}, {"cursorshape", #ifdef CURSOR_SHAPE 1 #else 0 #endif }, {"debug", #ifdef DEBUG 1 #else 0 #endif }, {"dialog_con", #ifdef FEAT_CON_DIALOG 1 #else 0 #endif }, {"dialog_gui", #ifdef FEAT_GUI_DIALOG 1 #else 0 #endif }, {"diff", #ifdef FEAT_DIFF 1 #else 0 #endif }, {"digraphs", #ifdef FEAT_DIGRAPHS 1 #else 0 #endif }, {"directx", #ifdef FEAT_DIRECTX 1 #else 0 #endif }, {"dnd", #ifdef FEAT_DND 1 #else 0 #endif }, {"drop_file", #ifdef HAVE_DROP_FILE 1 #else 0 #endif }, {"emacs_tags", #ifdef FEAT_EMACS_TAGS 1 #else 0 #endif }, {"eval", 1}, // always present, of course! {"ex_extra", 1}, // graduated feature {"extra_search", #ifdef FEAT_SEARCH_EXTRA 1 #else 0 #endif }, {"file_in_path", 1}, {"filterpipe", #if defined(FEAT_FILTERPIPE) && !defined(VIMDLL) 1 #else 0 #endif }, {"find_in_path", #ifdef FEAT_FIND_ID 1 #else 0 #endif }, {"float", 1}, {"folding", #ifdef FEAT_FOLDING 1 #else 0 #endif }, {"footer", 0}, {"fork", #if !defined(USE_SYSTEM) && defined(UNIX) 1 #else 0 #endif }, {"gettext", #ifdef FEAT_GETTEXT 1 #else 0 #endif }, {"gui", #ifdef FEAT_GUI 1 #else 0 #endif }, {"gui_neXtaw", 0 }, {"gui_athena", 0 }, {"gui_gtk", #ifdef FEAT_GUI_GTK 1 #else 0 #endif }, {"gui_gtk2", #if defined(FEAT_GUI_GTK) && !defined(USE_GTK3) 1 #else 0 #endif }, {"gui_gtk3", #if defined(FEAT_GUI_GTK) && defined(USE_GTK3) 1 #else 0 #endif }, {"gui_gnome", #ifdef FEAT_GUI_GNOME 1 #else 0 #endif }, {"gui_haiku", #ifdef FEAT_GUI_HAIKU 1 #else 0 #endif }, {"gui_mac", 0}, {"gui_motif", #ifdef FEAT_GUI_MOTIF 1 #else 0 #endif }, {"gui_photon", #ifdef FEAT_GUI_PHOTON 1 #else 0 #endif }, {"gui_win32", #ifdef FEAT_GUI_MSWIN 1 #else 0 #endif }, {"iconv", #if defined(HAVE_ICONV_H) && defined(USE_ICONV) 1 #else 0 #endif }, {"insert_expand", 1}, {"ipv6", #ifdef FEAT_IPV6 1 #else 0 #endif }, {"job", #ifdef FEAT_JOB_CHANNEL 1 #else 0 #endif }, {"jumplist", 1}, {"keymap", #ifdef FEAT_KEYMAP 1 #else 0 #endif }, {"lambda", 1}, // always with FEAT_EVAL, since 7.4.2120 with closure {"langmap", #ifdef FEAT_LANGMAP 1 #else 0 #endif }, {"libcall", #ifdef FEAT_LIBCALL 1 #else 0 #endif }, {"linebreak", #ifdef FEAT_LINEBREAK 1 #else 0 #endif }, {"lispindent", 1}, {"listcmds", 1}, {"localmap", 1}, {"lua", #if defined(FEAT_LUA) && !defined(DYNAMIC_LUA) 1 #else 0 #endif }, {"menu", #ifdef FEAT_MENU 1 #else 0 #endif }, {"mksession", #ifdef FEAT_SESSION 1 #else 0 #endif }, {"modify_fname", 1}, {"mouse", 1}, {"mouseshape", #ifdef FEAT_MOUSESHAPE 1 #else 0 #endif }, {"mouse_dec", #if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_DEC) 1 #else 0 #endif }, {"mouse_gpm", #if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_GPM) && !defined(DYNAMIC_GPM) 1 #else 0 #endif }, {"mouse_jsbterm", #if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_JSB) 1 #else 0 #endif }, {"mouse_netterm", #if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_NET) 1 #else 0 #endif }, {"mouse_pterm", #if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_PTERM) 1 #else 0 #endif }, {"mouse_sgr", #if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_XTERM) 1 #else 0 #endif }, {"mouse_sysmouse", #if (defined(UNIX) || defined(VMS)) && defined(FEAT_SYSMOUSE) 1 #else 0 #endif }, {"mouse_urxvt", #if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_URXVT) 1 #else 0 #endif }, {"mouse_xterm", #if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_XTERM) 1 #else 0 #endif }, {"multi_byte", 1}, {"multi_byte_ime", #ifdef FEAT_MBYTE_IME 1 #else 0 #endif }, {"multi_lang", #ifdef FEAT_MULTI_LANG 1 #else 0 #endif }, {"mzscheme", #if defined(FEAT_MZSCHEME) && !defined(DYNAMIC_MZSCHEME) 1 #else 0 #endif }, {"nanotime", #ifdef ST_MTIM_NSEC 1 #else 0 #endif }, {"num64", 1}, {"ole", #ifdef FEAT_OLE 1 #else 0 #endif }, {"packages", #ifdef FEAT_EVAL 1 #else 0 #endif }, {"path_extra", 1}, {"perl", #if defined(FEAT_PERL) && !defined(DYNAMIC_PERL) 1 #else 0 #endif }, {"persistent_undo", #ifdef FEAT_PERSISTENT_UNDO 1 #else 0 #endif }, {"python_compiled", #if defined(FEAT_PYTHON) 1 #else 0 #endif }, {"python_dynamic", #if defined(FEAT_PYTHON) && defined(DYNAMIC_PYTHON) 1 #else 0 #endif }, {"python", #if defined(FEAT_PYTHON) && !defined(DYNAMIC_PYTHON) 1 #else 0 #endif }, {"pythonx", #if (defined(FEAT_PYTHON) && !defined(DYNAMIC_PYTHON)) \ || (defined(FEAT_PYTHON3) && !defined(DYNAMIC_PYTHON3)) 1 #else 0 #endif }, {"python3_compiled", #if defined(FEAT_PYTHON3) 1 #else 0 #endif }, {"python3_dynamic", #if defined(FEAT_PYTHON3) && defined(DYNAMIC_PYTHON3) 1 #else 0 #endif }, {"python3", #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 HAVE_TIMER_CREATE 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 }, {"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 }, {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 (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, 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); } /* * "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; end = get_lval(tv_get_string(&argvars[0]), NULL, &lv, FALSE, FALSE, GLV_NO_AUTOLOAD | GLV_READ_ONLY | GLV_NO_DECL, FNE_CHECK_START); 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_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); } } clear_lval(&lv); } /* * "keytrans()" function */ static void f_keytrans(typval_T *argvars, typval_T *rettv) { char_u *escaped; rettv->v_type = VAR_STRING; if (check_for_string_arg(argvars, 0) == FAIL || argvars[0].vval.v_string == NULL) return; // Need to escape K_SPECIAL and CSI for mb_unescape(). escaped = vim_strsave_escape_csi(argvars[0].vval.v_string); rettv->vval.v_string = str2special_save(escaped, TRUE, TRUE); vim_free(escaped); } /* * "last_buffer_nr()" function. */ static void f_last_buffer_nr(typval_T *argvars UNUSED, typval_T *rettv) { int n = 0; buf_T *buf; FOR_ALL_BUFFERS(buf) if (n < buf->b_fnum) n = buf->b_fnum; rettv->vval.v_number = n; } /* * "len()" function */ void f_len(typval_T *argvars, typval_T *rettv) { switch (argvars[0].v_type) { case VAR_STRING: case VAR_NUMBER: rettv->vval.v_number = (varnumber_T)STRLEN( tv_get_string(&argvars[0])); break; case VAR_BLOB: rettv->vval.v_number = blob_len(argvars[0].vval.v_blob); break; case VAR_LIST: rettv->vval.v_number = list_len(argvars[0].vval.v_list); break; case VAR_DICT: rettv->vval.v_number = dict_len(argvars[0].vval.v_dict); break; case VAR_UNKNOWN: case VAR_ANY: case VAR_VOID: case VAR_BOOL: case VAR_SPECIAL: case VAR_FLOAT: case VAR_FUNC: case VAR_PARTIAL: case VAR_JOB: case VAR_CHANNEL: case VAR_INSTR: case VAR_CLASS: case VAR_OBJECT: 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; } /* * "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); } /* * "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; 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 off; 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 { off = LineOffset[row] + col; if (enc_utf8 && ScreenLinesUC[off] != 0) c = ScreenLinesUC[off]; else c = ScreenLines[off]; } rettv->vval.v_number = c; } /* * "screenchars()" function */ static void f_screenchars(typval_T *argvars, typval_T *rettv) { int row; int col; int off; 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; off = LineOffset[row] + col; if (enc_utf8 && ScreenLinesUC[off] != 0) c = ScreenLinesUC[off]; else c = ScreenLines[off]; list_append_number(rettv->vval.v_list, (varnumber_T)c); if (enc_utf8) for (i = 0; i < Screen_mco && ScreenLinesC[i][off] != 0; ++i) list_append_number(rettv->vval.v_list, (varnumber_T)ScreenLinesC[i][off]); } /* * "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; int off; int c; int i; char_u buf[MB_MAXBYTES + 1]; int buflen = 0; 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; off = LineOffset[row] + col; if (enc_utf8 && ScreenLinesUC[off] != 0) c = ScreenLinesUC[off]; else c = ScreenLines[off]; buflen += mb_char2bytes(c, buf); if (enc_utf8 && ScreenLinesUC[off] != 0) for (i = 0; i < Screen_mco && ScreenLinesC[i][off] != 0; ++i) buflen += mb_char2bytes(ScreenLinesC[i][off], buf + buflen); buf[buflen] = NUL; 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; name = tv_get_string_buf(&argvars[0], namebuf); if (argvars[1].v_type == VAR_SPECIAL && argvars[1].vval.v_number == VVAL_NULL) vim_unsetenv_ext(name); else vim_setenv_ext(name, tv_get_string_buf(&argvars[1], valbuf)); } /* * "setfperm({fname}, {mode})" function */ static void f_setfperm(typval_T *argvars, typval_T *rettv) { char_u *fname; char_u modebuf[NUMBUFLEN]; char_u *mode_str; int i; int mask; int mode = 0; rettv->vval.v_number = 0; if (in_vim9script() && (check_for_string_arg(argvars, 0) == FAIL || check_for_string_arg(argvars, 1) == FAIL)) return; fname = tv_get_string_chk(&argvars[0]); if (fname == NULL) return; mode_str = tv_get_string_buf_chk(&argvars[1], modebuf); if (mode_str == NULL) return; if (STRLEN(mode_str) != 9) { semsg(_(e_invalid_argument_str), mode_str); return; } mask = 1; for (i = 8; i >= 0; --i) { if (mode_str[i] != '-') mode |= mask; mask = mask << 1; } rettv->vval.v_number = mch_setperm(fname, mode) == OK; } /* * "setpos()" function */ static void f_setpos(typval_T *argvars, typval_T *rettv) { set_position(argvars, rettv, FALSE); } /* * Translate a register type string to the yank type and block length */ static int get_yank_type(char_u **pp, char_u *yank_type, long *block_len) { char_u *stropt = *pp; switch (*stropt) { case 'v': case 'c': // character-wise selection *yank_type = MCHAR; break; case 'V': case 'l': // line-wise selection *yank_type = MLINE; break; case 'b': case Ctrl_V: // block-wise selection *yank_type = MBLOCK; if (VIM_ISDIGIT(stropt[1])) { ++stropt; *block_len = getdigits(&stropt) - 1; --stropt; } break; default: return FAIL; } *pp = stropt; return OK; } /* * "setreg()" function */ static void f_setreg(typval_T *argvars, typval_T *rettv) { int regname; char_u *strregname; char_u *stropt; char_u *strval; int append; char_u yank_type; long block_len; typval_T *regcontents; int pointreg; if (in_vim9script() && (check_for_string_arg(argvars, 0) == FAIL || check_for_opt_string_arg(argvars, 2) == FAIL)) return; pointreg = 0; regcontents = NULL; block_len = -1; yank_type = MAUTO; append = FALSE; strregname = tv_get_string_chk(argvars); rettv->vval.v_number = 1; // FAIL is default if (strregname == NULL) return; // type error; errmsg already given if (in_vim9script() && STRLEN(strregname) > 1) { semsg(_(e_register_name_must_be_one_char_str), strregname); return; } regname = *strregname; if (regname == 0 || regname == '@') regname = '"'; if (argvars[1].v_type == VAR_DICT) { dict_T *d = argvars[1].vval.v_dict; dictitem_T *di; if (d == NULL || d->dv_hashtab.ht_used == 0) { // Empty dict, clear the register (like setreg(0, [])) char_u *lstval[2] = {NULL, NULL}; write_reg_contents_lst(regname, lstval, 0, FALSE, MAUTO, -1); return; } di = dict_find(d, (char_u *)"regcontents", -1); if (di != NULL) regcontents = &di->di_tv; stropt = dict_get_string(d, "regtype", FALSE); if (stropt != NULL) { int ret = get_yank_type(&stropt, &yank_type, &block_len); if (ret == FAIL || *++stropt != NUL) { semsg(_(e_invalid_value_for_argument_str), "value"); return; } } if (regname == '"') { stropt = dict_get_string(d, "points_to", FALSE); if (stropt != NULL) { pointreg = *stropt; regname = pointreg; } } else if (dict_get_bool(d, "isunnamed", -1) > 0) pointreg = regname; } else regcontents = &argvars[1]; if (argvars[2].v_type != VAR_UNKNOWN) { if (yank_type != MAUTO) { semsg(_(e_too_many_arguments_for_function_str), "setreg"); return; } stropt = tv_get_string_chk(&argvars[2]); if (stropt == NULL) return; // type error for (; *stropt != NUL; ++stropt) switch (*stropt) { case 'a': case 'A': // append append = TRUE; break; default: get_yank_type(&stropt, &yank_type, &block_len); } } if (regcontents && regcontents->v_type == VAR_LIST) { char_u **lstval; char_u **allocval; char_u buf[NUMBUFLEN]; char_u **curval; char_u **curallocval; list_T *ll = regcontents->vval.v_list; listitem_T *li; int len; // If the list is NULL handle like an empty list. len = ll == NULL ? 0 : ll->lv_len; // First half: use for pointers to result lines; second half: use for // pointers to allocated copies. lstval = ALLOC_MULT(char_u *, (len + 1) * 2); if (lstval == NULL) return; curval = lstval; allocval = lstval + len + 2; curallocval = allocval; if (ll != NULL) { CHECK_LIST_MATERIALIZE(ll); FOR_ALL_LIST_ITEMS(ll, li) { strval = tv_get_string_buf_chk(&li->li_tv, buf); if (strval == NULL) goto free_lstval; if (strval == buf) { // Need to make a copy, next tv_get_string_buf_chk() will // overwrite the string. strval = vim_strsave(buf); if (strval == NULL) goto free_lstval; *curallocval++ = strval; } *curval++ = strval; } } *curval++ = NULL; write_reg_contents_lst(regname, lstval, -1, append, yank_type, block_len); free_lstval: while (curallocval > allocval) vim_free(*--curallocval); vim_free(lstval); } else if (regcontents) { strval = tv_get_string_chk(regcontents); if (strval == NULL) return; write_reg_contents_ex(regname, strval, -1, append, yank_type, block_len); } if (pointreg != 0) get_yank_register(pointreg, TRUE); rettv->vval.v_number = 0; } /* * "settagstack()" function */ static void f_settagstack(typval_T *argvars, typval_T *rettv) { win_T *wp; dict_T *d; int action = 'r'; rettv->vval.v_number = -1; if (in_vim9script() && (check_for_number_arg(argvars, 0) == FAIL || check_for_dict_arg(argvars, 1) == FAIL || check_for_opt_string_arg(argvars, 2) == FAIL)) return; // first argument: window number or id wp = find_win_by_nr_or_id(&argvars[0]); if (wp == NULL) return; // second argument: dict with items to set in the tag stack if (check_for_dict_arg(argvars, 1) == FAIL) return; d = argvars[1].vval.v_dict; if (d == NULL) return; // third argument: action - 'a' for append and 'r' for replace. // default is to replace the stack. if (argvars[2].v_type == VAR_UNKNOWN) action = 'r'; else if (check_for_string_arg(argvars, 2) == FAIL) return; else { char_u *actstr; actstr = tv_get_string_chk(&argvars[2]); if (actstr == NULL) return; if ((*actstr == 'r' || *actstr == 'a' || *actstr == 't') && actstr[1] == NUL) action = *actstr; else { semsg(_(e_invalid_action_str_2), actstr); return; } } if (set_tagstack(wp, d, action) == OK) rettv->vval.v_number = 0; } #ifdef FEAT_CRYPT /* * "sha256({string})" function */ static void f_sha256(typval_T *argvars, typval_T *rettv) { char_u *p; if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) return; p = tv_get_string(&argvars[0]); rettv->vval.v_string = vim_strsave( sha256_bytes(p, (int)STRLEN(p), NULL, 0)); rettv->v_type = VAR_STRING; } #endif // FEAT_CRYPT /* * "shellescape({string})" function */ static void f_shellescape(typval_T *argvars, typval_T *rettv) { int do_special; if (in_vim9script() && (check_for_string_arg(argvars, 0) == FAIL || check_for_opt_bool_arg(argvars, 1) == FAIL)) return; do_special = non_zero_arg(&argvars[1]); rettv->vval.v_string = vim_strsave_shellescape( tv_get_string(&argvars[0]), do_special, do_special); rettv->v_type = VAR_STRING; } /* * shiftwidth() function */ static void f_shiftwidth(typval_T *argvars UNUSED, typval_T *rettv) { rettv->vval.v_number = 0; if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL) return; if (argvars[0].v_type != VAR_UNKNOWN) { long col; col = (long)tv_get_number_chk(argvars, NULL); if (col < 0) return; // type error; errmsg already given #ifdef FEAT_VARTABS rettv->vval.v_number = get_sw_value_col(curbuf, col); return; #endif } rettv->vval.v_number = get_sw_value(curbuf); } /* * "soundfold({word})" function */ static void f_soundfold(typval_T *argvars, typval_T *rettv) { char_u *s; if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) return; rettv->v_type = VAR_STRING; s = tv_get_string(&argvars[0]); #ifdef FEAT_SPELL rettv->vval.v_string = eval_soundfold(s); #else rettv->vval.v_string = vim_strsave(s); #endif } /* * "spellbadword()" function */ static void f_spellbadword(typval_T *argvars UNUSED, typval_T *rettv) { char_u *word = (char_u *)""; hlf_T attr = HLF_COUNT; int len = 0; #ifdef FEAT_SPELL int wo_spell_save = curwin->w_p_spell; if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL) return; if (!curwin->w_p_spell) { parse_spelllang(curwin); curwin->w_p_spell = TRUE; } if (*curwin->w_s->b_p_spl == NUL) { emsg(_(e_spell_checking_is_not_possible)); curwin->w_p_spell = wo_spell_save; return; } #endif if (rettv_list_alloc(rettv) == FAIL) { #ifdef FEAT_SPELL curwin->w_p_spell = wo_spell_save; #endif return; } #ifdef FEAT_SPELL if (argvars[0].v_type == VAR_UNKNOWN) { // Find the start and length of the badly spelled word. len = spell_move_to(curwin, FORWARD, TRUE, TRUE, &attr); if (len != 0) { word = ml_get_cursor(); curwin->w_set_curswant = TRUE; } } else if (*curbuf->b_s.b_p_spl != NUL) { char_u *str = tv_get_string_chk(&argvars[0]); int capcol = -1; if (str != NULL) { // Check the argument for spelling. while (*str != NUL) { len = spell_check(curwin, str, &attr, &capcol, FALSE); if (attr != HLF_COUNT) { word = str; break; } str += len; capcol -= len; len = 0; } } } curwin->w_p_spell = wo_spell_save; #endif list_append_string(rettv->vval.v_list, word, len); list_append_string(rettv->vval.v_list, (char_u *)( attr == HLF_SPB ? "bad" : attr == HLF_SPR ? "rare" : attr == HLF_SPL ? "local" : attr == HLF_SPC ? "caps" : ""), -1); } /* * "spellsuggest()" function */ static void f_spellsuggest(typval_T *argvars UNUSED, typval_T *rettv) { #ifdef FEAT_SPELL char_u *str; int typeerr = FALSE; int maxcount; garray_T ga; int i; listitem_T *li; int need_capital = FALSE; int wo_spell_save = curwin->w_p_spell; if (in_vim9script() && (check_for_string_arg(argvars, 0) == FAIL || check_for_opt_number_arg(argvars, 1) == FAIL || (argvars[1].v_type != VAR_UNKNOWN && check_for_opt_bool_arg(argvars, 2) == FAIL))) return; if (!curwin->w_p_spell) { parse_spelllang(curwin); curwin->w_p_spell = TRUE; } if (*curwin->w_s->b_p_spl == NUL) { emsg(_(e_spell_checking_is_not_possible)); curwin->w_p_spell = wo_spell_save; return; } #endif if (rettv_list_alloc(rettv) == FAIL) { #ifdef FEAT_SPELL curwin->w_p_spell = wo_spell_save; #endif return; } #ifdef FEAT_SPELL str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { maxcount = (int)tv_get_number_chk(&argvars[1], &typeerr); if (maxcount <= 0) return; if (argvars[2].v_type != VAR_UNKNOWN) { need_capital = (int)tv_get_bool_chk(&argvars[2], &typeerr); if (typeerr) return; } } else maxcount = 25; spell_suggest_list(&ga, str, maxcount, need_capital, FALSE); for (i = 0; i < ga.ga_len; ++i) { str = ((char_u **)ga.ga_data)[i]; li = listitem_alloc(); if (li == NULL) vim_free(str); else { li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; li->li_tv.vval.v_string = str; list_append(rettv->vval.v_list, li); } } ga_clear(&ga); curwin->w_p_spell = wo_spell_save; #endif } static void f_split(typval_T *argvars, typval_T *rettv) { char_u *str; char_u *end; char_u *pat = NULL; regmatch_T regmatch; char_u patbuf[NUMBUFLEN]; char_u *save_cpo; int match; colnr_T col = 0; int keepempty = FALSE; int typeerr = FALSE; if (in_vim9script() && (check_for_string_arg(argvars, 0) == FAIL || check_for_opt_string_arg(argvars, 1) == FAIL || (argvars[1].v_type != VAR_UNKNOWN && check_for_opt_bool_arg(argvars, 2) == FAIL))) return; // Make 'cpoptions' empty, the 'l' flag should not be used here. save_cpo = p_cpo; p_cpo = empty_option; str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { pat = tv_get_string_buf_chk(&argvars[1], patbuf); if (pat == NULL) typeerr = TRUE; if (argvars[2].v_type != VAR_UNKNOWN) keepempty = (int)tv_get_bool_chk(&argvars[2], &typeerr); } if (pat == NULL || *pat == NUL) pat = (char_u *)"[\\x01- ]\\+"; if (rettv_list_alloc(rettv) == FAIL) goto theend; if (typeerr) goto theend; regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { regmatch.rm_ic = FALSE; while (*str != NUL || keepempty) { if (*str == NUL) match = FALSE; // empty item at the end else match = vim_regexec_nl(®match, str, col); if (match) end = regmatch.startp[0]; else end = str + STRLEN(str); if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0 && *str != NUL && match && end < regmatch.endp[0])) { if (list_append_string(rettv->vval.v_list, str, (int)(end - str)) == FAIL) break; } if (!match) break; // Advance to just after the match. if (regmatch.endp[0] > str) col = 0; else // Don't get stuck at the same match. col = (*mb_ptr2len)(regmatch.endp[0]); str = regmatch.endp[0]; } vim_regfree(regmatch.regprog); } theend: p_cpo = save_cpo; } /* * "submatch()" function */ static void f_submatch(typval_T *argvars, typval_T *rettv) { int error = FALSE; int no; int retList = 0; if (in_vim9script() && (check_for_number_arg(argvars, 0) == FAIL || check_for_opt_bool_arg(argvars, 1) == FAIL)) return; no = (int)tv_get_number_chk(&argvars[0], &error); if (error) return; if (no < 0 || no >= NSUBEXP) { semsg(_(e_invalid_submatch_number_nr), no); return; } if (argvars[1].v_type != VAR_UNKNOWN) retList = (int)tv_get_bool_chk(&argvars[1], &error); if (error) return; if (retList == 0) { rettv->v_type = VAR_STRING; rettv->vval.v_string = reg_submatch(no); } else { rettv->v_type = VAR_LIST; rettv->vval.v_list = reg_submatch_list(no); } } /* * "substitute()" function */ static void f_substitute(typval_T *argvars, typval_T *rettv) { char_u patbuf[NUMBUFLEN]; char_u subbuf[NUMBUFLEN]; char_u flagsbuf[NUMBUFLEN]; char_u *str; char_u *pat; char_u *sub = NULL; typval_T *expr = NULL; char_u *flg; if (in_vim9script() && (check_for_string_arg(argvars, 0) == FAIL || check_for_string_arg(argvars, 1) == FAIL || check_for_string_arg(argvars, 3) == FAIL)) return; str = tv_get_string_chk(&argvars[0]); pat = tv_get_string_buf_chk(&argvars[1], patbuf); flg = tv_get_string_buf_chk(&argvars[3], flagsbuf); if (argvars[2].v_type == VAR_FUNC || argvars[2].v_type == VAR_PARTIAL || argvars[2].v_type == VAR_INSTR || argvars[2].v_type == VAR_CLASS || argvars[2].v_type == VAR_OBJECT) expr = &argvars[2]; else sub = tv_get_string_buf_chk(&argvars[2], subbuf); rettv->v_type = VAR_STRING; if (str == NULL || pat == NULL || (sub == NULL && expr == NULL) || flg == NULL) rettv->vval.v_string = NULL; else rettv->vval.v_string = do_string_sub(str, pat, sub, expr, flg); } /* * "swapfilelist()" function */ static void f_swapfilelist(typval_T *argvars UNUSED, typval_T *rettv) { if (rettv_list_alloc(rettv) == FAIL) return; recover_names(NULL, FALSE, rettv->vval.v_list, 0, NULL); } /* * "swapinfo(swap_filename)" function */ static void f_swapinfo(typval_T *argvars, typval_T *rettv) { if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) return; if (rettv_dict_alloc(rettv) == OK) get_b0_dict(tv_get_string(argvars), rettv->vval.v_dict); } /* * "swapname(expr)" function */ static void f_swapname(typval_T *argvars, typval_T *rettv) { buf_T *buf; rettv->v_type = VAR_STRING; if (in_vim9script() && check_for_buffer_arg(argvars, 0) == FAIL) return; buf = tv_get_buf(&argvars[0], FALSE); if (buf == NULL || buf->b_ml.ml_mfp == NULL || buf->b_ml.ml_mfp->mf_fname == NULL) rettv->vval.v_string = NULL; else rettv->vval.v_string = vim_strsave(buf->b_ml.ml_mfp->mf_fname); } /* * "synID(lnum, col, trans)" function */ static void f_synID(typval_T *argvars UNUSED, typval_T *rettv) { int id = 0; #ifdef FEAT_SYN_HL linenr_T lnum; colnr_T col; int trans; int transerr = FALSE; if (in_vim9script() && (check_for_lnum_arg(argvars, 0) == FAIL || check_for_number_arg(argvars, 1) == FAIL || check_for_bool_arg(argvars, 2) == FAIL)) return; lnum = tv_get_lnum(argvars); // -1 on type error col = (linenr_T)tv_get_number(&argvars[1]) - 1; // -1 on type error trans = (int)tv_get_bool_chk(&argvars[2], &transerr); if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && col < (long)STRLEN(ml_get(lnum))) id = syn_get_id(curwin, lnum, col, trans, NULL, FALSE); #endif rettv->vval.v_number = id; } /* * "synIDattr(id, what [, mode])" function */ static void f_synIDattr(typval_T *argvars UNUSED, typval_T *rettv) { char_u *p = NULL; #ifdef FEAT_SYN_HL int id; char_u *what; char_u *mode; char_u modebuf[NUMBUFLEN]; int modec; if (in_vim9script() && (check_for_number_arg(argvars, 0) == FAIL || check_for_string_arg(argvars, 1) == FAIL || check_for_opt_string_arg(argvars, 2) == FAIL)) return; id = (int)tv_get_number(&argvars[0]); what = tv_get_string(&argvars[1]); if (argvars[2].v_type != VAR_UNKNOWN) { mode = tv_get_string_buf(&argvars[2], modebuf); modec = TOLOWER_ASC(mode[0]); if (modec != 't' && modec != 'c' && modec != 'g') modec = 0; // replace invalid with current } else { #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) if (USE_24BIT) modec = 'g'; else #endif if (t_colors > 1) modec = 'c'; else modec = 't'; } switch (TOLOWER_ASC(what[0])) { case 'b': if (TOLOWER_ASC(what[1]) == 'g') // bg[#] p = highlight_color(id, what, modec); else // bold p = highlight_has_attr(id, HL_BOLD, modec); break; case 'f': // fg[#] or font p = highlight_color(id, what, modec); break; case 'i': if (TOLOWER_ASC(what[1]) == 'n') // inverse p = highlight_has_attr(id, HL_INVERSE, modec); else // italic p = highlight_has_attr(id, HL_ITALIC, modec); break; case 'n': if (TOLOWER_ASC(what[1]) == 'o') // nocombine p = highlight_has_attr(id, HL_NOCOMBINE, modec); else // name p = get_highlight_name_ext(NULL, id - 1, FALSE); break; case 'r': // reverse p = highlight_has_attr(id, HL_INVERSE, modec); break; case 's': if (TOLOWER_ASC(what[1]) == 'p') // sp[#] p = highlight_color(id, what, modec); // strikeout else if (TOLOWER_ASC(what[1]) == 't' && TOLOWER_ASC(what[2]) == 'r') p = highlight_has_attr(id, HL_STRIKETHROUGH, modec); else // standout p = highlight_has_attr(id, HL_STANDOUT, modec); break; case 'u': if (STRLEN(what) >= 9) { if (TOLOWER_ASC(what[5]) == 'l') // underline p = highlight_has_attr(id, HL_UNDERLINE, modec); else if (TOLOWER_ASC(what[5]) != 'd') // undercurl p = highlight_has_attr(id, HL_UNDERCURL, modec); else if (TOLOWER_ASC(what[6]) != 'o') // underdashed p = highlight_has_attr(id, HL_UNDERDASHED, modec); else if (TOLOWER_ASC(what[7]) == 'u') // underdouble p = highlight_has_attr(id, HL_UNDERDOUBLE, modec); else // underdotted p = highlight_has_attr(id, HL_UNDERDOTTED, modec); } else // ul p = highlight_color(id, what, modec); break; } if (p != NULL) p = vim_strsave(p); #endif rettv->v_type = VAR_STRING; rettv->vval.v_string = p; } /* * "synIDtrans(id)" function */ static void f_synIDtrans(typval_T *argvars UNUSED, typval_T *rettv) { int id; #ifdef FEAT_SYN_HL if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL) return; id = (int)tv_get_number(&argvars[0]); if (id > 0) id = syn_get_final_id(id); else #endif id = 0; rettv->vval.v_number = id; } /* * "synconcealed(lnum, col)" function */ static void f_synconcealed(typval_T *argvars UNUSED, typval_T *rettv) { #if defined(FEAT_SYN_HL) && defined(FEAT_CONCEAL) linenr_T lnum; colnr_T col; int syntax_flags = 0; int cchar; int matchid = 0; char_u str[NUMBUFLEN]; #endif rettv_list_set(rettv, NULL); if (in_vim9script() && (check_for_lnum_arg(argvars, 0) == FAIL || check_for_number_arg(argvars, 1) == FAIL)) return; #if defined(FEAT_SYN_HL) && defined(FEAT_CONCEAL) lnum = tv_get_lnum(argvars); // -1 on type error col = (colnr_T)tv_get_number(&argvars[1]) - 1; // -1 on type error CLEAR_FIELD(str); if (rettv_list_alloc(rettv) == OK) { if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && col <= (long)STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) { (void)syn_get_id(curwin, lnum, col, FALSE, NULL, FALSE); syntax_flags = get_syntax_info(&matchid); // get the conceal character if ((syntax_flags & HL_CONCEAL) && curwin->w_p_cole < 3) { cchar = syn_get_sub_char(); if (cchar == NUL && curwin->w_p_cole == 1) cchar = (curwin->w_lcs_chars.conceal == NUL) ? ' ' : curwin->w_lcs_chars.conceal; if (cchar != NUL) { if (has_mbyte) (*mb_char2bytes)(cchar, str); else str[0] = cchar; } } } list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0); // -1 to auto-determine strlen list_append_string(rettv->vval.v_list, str, -1); list_append_number(rettv->vval.v_list, matchid); } #endif } /* * "synstack(lnum, col)" function */ static void f_synstack(typval_T *argvars UNUSED, typval_T *rettv) { #ifdef FEAT_SYN_HL linenr_T lnum; colnr_T col; int i; int id; #endif rettv_list_set(rettv, NULL); if (in_vim9script() && (check_for_lnum_arg(argvars, 0) == FAIL || check_for_number_arg(argvars, 1) == FAIL)) return; #ifdef FEAT_SYN_HL lnum = tv_get_lnum(argvars); // -1 on type error col = (colnr_T)tv_get_number(&argvars[1]) - 1; // -1 on type error if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && col <= (long)STRLEN(ml_get(lnum)) && rettv_list_alloc(rettv) == OK) { (void)syn_get_id(curwin, lnum, col, FALSE, NULL, TRUE); for (i = 0; ; ++i) { id = syn_get_stack_item(i); if (id < 0) break; if (list_append_number(rettv->vval.v_list, id) == FAIL) break; } } #endif } /* * "tabpagebuflist()" function */ static void f_tabpagebuflist(typval_T *argvars UNUSED, typval_T *rettv UNUSED) { tabpage_T *tp; win_T *wp = NULL; if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL) return; if (argvars[0].v_type == VAR_UNKNOWN) wp = firstwin; else { tp = find_tabpage((int)tv_get_number(&argvars[0])); if (tp != NULL) wp = (tp == curtab) ? firstwin : tp->tp_firstwin; } if (wp != NULL && rettv_list_alloc(rettv) == OK) { for (; wp != NULL; wp = wp->w_next) if (list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum) == FAIL) break; } } /* * "tagfiles()" function */ static void f_tagfiles(typval_T *argvars UNUSED, typval_T *rettv) { char_u *fname; tagname_T tn; int first; if (rettv_list_alloc(rettv) == FAIL) return; fname = alloc(MAXPATHL); if (fname == NULL) return; for (first = TRUE; ; first = FALSE) if (get_tagfname(&tn, first, fname) == FAIL || list_append_string(rettv->vval.v_list, fname, -1) == FAIL) break; tagname_free(&tn); vim_free(fname); } /* * "taglist()" function */ static void f_taglist(typval_T *argvars, typval_T *rettv) { char_u *fname = NULL; char_u *tag_pattern; if (in_vim9script() && (check_for_string_arg(argvars, 0) == FAIL || check_for_opt_string_arg(argvars, 1) == FAIL)) return; tag_pattern = tv_get_string(&argvars[0]); rettv->vval.v_number = FALSE; if (*tag_pattern == NUL) return; if (argvars[1].v_type != VAR_UNKNOWN) fname = tv_get_string(&argvars[1]); if (rettv_list_alloc(rettv) == OK) (void)get_tags(rettv->vval.v_list, tag_pattern, fname); } /* * "type(expr)" function */ static void f_type(typval_T *argvars, typval_T *rettv) { int n = -1; switch (argvars[0].v_type) { case VAR_NUMBER: n = VAR_TYPE_NUMBER; break; case VAR_STRING: n = VAR_TYPE_STRING; break; case VAR_PARTIAL: case VAR_FUNC: n = VAR_TYPE_FUNC; break; case VAR_LIST: n = VAR_TYPE_LIST; break; case VAR_DICT: n = VAR_TYPE_DICT; break; case VAR_FLOAT: n = VAR_TYPE_FLOAT; break; case VAR_BOOL: n = VAR_TYPE_BOOL; break; case VAR_SPECIAL: n = VAR_TYPE_NONE; break; case VAR_JOB: n = VAR_TYPE_JOB; break; case VAR_CHANNEL: n = VAR_TYPE_CHANNEL; break; case VAR_BLOB: n = VAR_TYPE_BLOB; break; case VAR_INSTR: n = VAR_TYPE_INSTR; break; case VAR_CLASS: n = VAR_TYPE_CLASS; break; case VAR_OBJECT: n = VAR_TYPE_OBJECT; break; case VAR_UNKNOWN: case VAR_ANY: case VAR_VOID: internal_error_no_abort("f_type(UNKNOWN)"); n = -1; break; } rettv->vval.v_number = n; } /* * "virtcol(string, bool)" function */ static void f_virtcol(typval_T *argvars, typval_T *rettv) { colnr_T vcol_start = 0; colnr_T vcol_end = 0; pos_T *fp; int fnum = curbuf->b_fnum; 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))) return; fp = var2fpos(&argvars[0], FALSE, &fnum, FALSE); if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count && fnum == curbuf->b_fnum) { // Limit the column to a valid value, getvvcol() doesn't check. if (fp->col < 0) fp->col = 0; else { len = (int)STRLEN(ml_get(fp->lnum)); if (fp->col > len) fp->col = len; } getvvcol(curwin, fp, &vcol_start, NULL, &vcol_end); ++vcol_start; ++vcol_end; } 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; } /* * "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