text: /* 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_hasmapto(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_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_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_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)
  {
      // TODO: would be useful to know if "actual" is a constant and pass it to
      // need_type() to get a compile time error if possible.
      return need_type(actual, expected,
  	    context->arg_idx - context->arg_count, context->arg_idx + 1,
  	    context->arg_cctx, FALSE, FALSE);
  }
  
  /*
   * 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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_FLOAT
  	    || type->tt_type == VAR_NUMBER)
  	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 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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_LIST
  	    || type->tt_type == VAR_BLOB)
  	return OK;
      arg_type_mismatch(&t_list_any, type, context->arg_idx + 1);
      return FAIL;
  }
  
  /*
   * 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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_STRING
  	    || type->tt_type == VAR_NUMBER)
  	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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_STRING
  	    || type->tt_type == VAR_NUMBER)
  	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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_STRING
  	    || type->tt_type == VAR_NUMBER
  	    || type->tt_type == VAR_DICT)
  	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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_STRING
  	    || type->tt_type == VAR_NUMBER)
  	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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_STRING)
  	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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_STRING
  	    || type->tt_type == VAR_LIST)
  	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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_STRING
  	    || type->tt_type == VAR_DICT)
  	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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_STRING
  	    || type->tt_type == VAR_BLOB)
  	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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_LIST
  	    || type->tt_type == VAR_DICT)
  	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.
   */
      static int
  arg_list_or_dict_or_blob(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
  {
      if (type->tt_type == VAR_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_LIST
  	    || type->tt_type == VAR_DICT
  	    || type->tt_type == VAR_BLOB)
  	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.
   */
      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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_LIST
  	    || type->tt_type == VAR_DICT
  	    || type->tt_type == VAR_BLOB
  	    || type->tt_type == VAR_STRING)
  	return OK;
      arg_type_mismatch(&t_list_any, type, context->arg_idx + 1);
      return FAIL;
  }
  
  /*
   * 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, 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->tt_member != &t_any && type->tt_member != &t_unknown)
  	    || args[0] != NULL)
      {
  	where_T where = WHERE_INIT;
  
  	if (is_map)
  	    t_func_exp.tt_member = expected_member == NULL
  				    || type->tt_member == &t_any
  				    || type->tt_member == &t_unknown
  				? &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 == &t_unknown
  	    || type == &t_any)
  	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 == &t_unknown
  	    || type == &t_any)
  	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 == &t_unknown
  	    || type == &t_any)
  	return OK;
  
      if (type->tt_type == VAR_FUNC)
      {
  	type_T *(args[2]);
  	type_T t_func_exp = {VAR_FUNC, 2, 0, 0, &t_number, 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->tt_member != &t_any && type->tt_member != &t_unknown)
  		|| 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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || 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)
  	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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_LIST
  	    || type->tt_type == VAR_BLOB
  	    || type->tt_type == VAR_STRING)
  	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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_CHANNEL
  	    || type->tt_type == VAR_JOB)
  	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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_STRING
  	    || type->tt_type == VAR_NUMBER
  	    || type->tt_type == VAR_LIST)
  	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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_DICT
  	    || type->tt_type == VAR_STRING)
  	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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || 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)
  	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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || 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)
  	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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_STRING
  	    || type->tt_type == VAR_NUMBER
  	    || type->tt_type == VAR_LIST)
  	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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_LIST
  	    || type->tt_type == VAR_BLOB
  	    || type->tt_type == VAR_STRING)
  	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_count1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
  {
      if (type->tt_type == VAR_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_STRING
  	    || type->tt_type == VAR_LIST
  	    || type->tt_type == VAR_DICT)
  	return OK;
  
      arg_type_mismatch(&t_string, type, 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_ANY
  	    || type->tt_type == VAR_UNKNOWN
  	    || type->tt_type == VAR_NUMBER
  	    || type->tt_type == VAR_STRING
  	    || type->tt_type == VAR_LIST)
  	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[] = {arg_list_or_blob};
  static argcheck_T arg1_list_or_dict[] = {arg_list_or_dict};
  static argcheck_T arg1_list_string[] = {arg_list_string};
  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_listblob_item[] = {arg_list_or_blob, 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_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_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_count1, 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, 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_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, arg_filter_func};
  static argcheck_T arg2_map[] = {arg_list_or_dict_or_blob_or_string, 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, 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, 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};
  
  
  /*
   * 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;
  }
  
  /*
   * 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
  
  #ifdef FEAT_FLOAT
  # define FLOAT_FUNC(name) name
  #else
  # define FLOAT_FUNC(name) NULL
  #endif
  #if defined(FEAT_FLOAT) && 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,	    FLOAT_FUNC(f_abs)},
      {"acos",		1, 1, FEARG_1,	    arg1_float_or_nr,
  			ret_float,	    FLOAT_FUNC(f_acos)},
      {"add",		2, 2, FEARG_1,	    arg2_listblob_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,	    FLOAT_FUNC(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,	    FLOAT_FUNC(f_atan)},
      {"atan2",		2, 2, FEARG_1,	    arg2_float_or_nr,
  			ret_float,	    FLOAT_FUNC(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,	    FLOAT_FUNC(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,	    JOB_FUNC(f_ch_log)},
      {"ch_logfile",	1, 2, FEARG_1,	    arg2_string,
  			ret_void,	    JOB_FUNC(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, 1, FEARG_1,	    arg1_string_or_list_any,
  			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, 1, FEARG_1,	    arg1_string_or_list_any,
  			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,	    FLOAT_FUNC(f_cos)},
      {"cosh",		1, 1, FEARG_1,	    arg1_float_or_nr,
  			ret_float,	    FLOAT_FUNC(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,	    FLOAT_FUNC(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,	    FLOAT_FUNC(f_float2nr)},
      {"floor",		1, 1, FEARG_1,	    arg1_float_or_nr,
  			ret_float,	    FLOAT_FUNC(f_floor)},
      {"fmod",		2, 2, FEARG_1,	    arg2_float_or_nr,
  			ret_float,	    FLOAT_FUNC(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, 1, FEARG_1,	    arg1_string,
  			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},
      {"getbufvar",	2, 3, FEARG_1,	    arg3_buffer_string_any,
  			ret_any,	    f_getbufvar},
      {"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},
      {"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},
      {"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},
      {"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_dict_any,
  			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},
      {"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,	    FLOAT_FUNC(f_log)},
      {"log10",		1, 1, FEARG_1,	    arg1_float_or_nr,
  			ret_float,	    FLOAT_FUNC(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_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,	    FLOAT_FUNC(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_void,	    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, 1, FEARG_1,	    arg1_string,
  			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,	    FLOAT_FUNC(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,
  			ret_first_arg,	    f_reverse},
      {"round",		1, 1, FEARG_1,	    arg1_float_or_nr,
  			ret_float,	    FLOAT_FUNC(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},
      {"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,	    FLOAT_FUNC(f_sin)},
      {"sinh",		1, 1, FEARG_1,	    arg1_float_or_nr,
  			ret_float,	    FLOAT_FUNC(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,	    FLOAT_FUNC(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,	    FLOAT_FUNC(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},
      {"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,	    FLOAT_FUNC(f_tan)},
      {"tanh",		1, 1, FEARG_1,	    arg1_float_or_nr,
  			ret_float,	    FLOAT_FUNC(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_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,	    FLOAT_FUNC(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_any,	    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))
      {
  	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;
      int		i;
  
      if (argchecks != NULL)
      {
  	argcontext_T context;
  
  	context.arg_count = argcount;
  	context.arg_types = types;
  	context.arg_cctx = cctx;
  	for (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)
  {
      type_T *ret;
  
      *decl_type = NULL;
      ret = global_functions[idx].f_retfunc(argcount, argtypes, decl_type);
      if (*decl_type == NULL)
  	*decl_type = ret;
      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)
  {
      int	    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;
  }
  
      int
  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().
   */
      int
  call_internal_method(
  	char_u	    *name,
  	int	    argcount,
  	typval_T    *argvars,
  	typval_T    *rettv,
  	typval_T    *basetv)
  {
      int		i;
      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 (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 (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 (i = 3; i < argcount; ++i)
  	    argv[i + 1] = argvars[i];
      }
      else
      {
  	// FEARG_1: base value goes first
  	argv[0] = *basetv;
  	for (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)
      {
  	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)
      {
  	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)
      {
  	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);
  	    int		i;
  
  	    // Skip the first and last item, they are always empty.
  	    for (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 (argvars[1].v_type != VAR_LIST)
      {
  	emsg(_(e_list_required));
  	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 (argvars[2].v_type != VAR_DICT)
  	{
  	    emsg(_(e_dictionary_required));
  	    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;
      int		fnum = curbuf->b_fnum;
  
      if (in_vim9script()
  	    && check_for_string_or_list_arg(argvars, 0) == FAIL)
  	return;
  
      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;
  }
  
  /*
   * "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)
      {
  	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	line, 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;
  	}
  	line = 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))
      {
  	line = tv_get_lnum(argvars);
  	if (line < 0)
  	    semsg(_(e_invalid_argument_str), tv_get_string(&argvars[0]));
  	col = (long)tv_get_number_chk(&argvars[1], NULL);
  	if (charcol)
  	    col = buf_charidx_to_byteidx(curbuf, line, 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 (line < 0 || col < 0 || coladd < 0)
  	return;		// type error; errmsg already given
      if (line > 0)
  	curwin->w_cursor.lnum = line;
      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));
      else
      {
  	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
  
  	if (hProcess != NULL)
  	{
  	    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;
      int		copyID;
  
      if (in_vim9script()
  	    && (check_for_opt_bool_arg(argvars, 1) == FAIL))
  	return;
  
      if (argvars[1].v_type != VAR_UNKNOWN)
  	noref = tv_get_bool_chk(&argvars[1], NULL);
      if (noref < 0 || noref > 1)
  	semsg(_(e_using_number_as_bool_nr), noref);
      else
      {
  	copyID = get_copyID();
  	item_copy(&argvars[0], rettv, TRUE, TRUE, noref == 0 ? 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:
  #ifdef FEAT_FLOAT
  	    n = argvars[0].vval.v_float == 0.0;
  	    break;
  #endif
  	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_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) == OK)
      {
  	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		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
  
      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;
      }
  
      // 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)
  	    {
  		int i;
  
  		ExpandOne(&xpc, s, NULL, options, WILD_ALL_KEEP);
  		for (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, (char_u *)"errmsg",
  								VVAL_FALSE))
  	emsgoff = FALSE;
  
      rettv->v_type = VAR_STRING;
      cmdstr = vim_strsave(tv_get_string(&argvars[0]));
  
      memset(&eap, 0, sizeof(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;
      char_u	*keys_esc;
  
      // 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)
      {
  	// Need to escape K_SPECIAL and CSI before putting the string in the
  	// typeahead buffer.
  	keys_esc = vim_strsave_escape_csi(keys);
  	if (keys_esc != NULL)
  	{
  	    if (lowlevel)
  	    {
  #ifdef USE_INPUT_BUF
  		int idx;
  		int len = (int)STRLEN(keys);
  
  		for (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
  	    {
  		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;
  
  		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
  }
  
      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 (argvars[dict_idx].v_type != VAR_DICT)
  		{
  		    emsg(_(e_expected_dict));
  		    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]);
  	    char_u *n;
  
  	    if (STRCMP(what, "func") == 0 || STRCMP(what, "name") == 0)
  	    {
  		rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING);
  		n = partial_name(pt);
  		if (n == NULL)
  		    rettv->vval.v_string = NULL;
  		else
  		{
  		    rettv->vval.v_string = vim_strsave(n);
  		    if (rettv->v_type == VAR_FUNC)
  			func_ref(rettv->vval.v_string);
  		}
  	    }
  	    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)
  	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)
      {
  	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)
  	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, &reglen))
      {
  	case MLINE: buf[0] = 'V'; break;
  	case MCHAR: buf[0] = 'v'; break;
  	case MBLOCK:
  		buf[0] = Ctrl_V;
  		sprintf((char *)buf + 1, "%ld", reglen + 1);
  		break;
      }
      rettv->vval.v_string = vim_strsave(buf);
  }
  
  /*
   * "gettagstack()" function
   */
      static void
  f_gettagstack(typval_T *argvars, typval_T *rettv)
  {
      win_T	*wp = curwin;			// default is current window
  
      if (rettv_dict_alloc(rettv) == FAIL)
  	return;
  
      if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
  	return;
  
      if (argvars[0].v_type != VAR_UNKNOWN)
      {
  	wp = find_win_by_nr_or_id(&argvars[0]);
  	if (wp == NULL)
  	    return;
      }
  
      get_tagstack(wp, rettv->vval.v_dict);
  }
  
  /*
   * "gettext()" function
   */
      static void
  f_gettext(typval_T *argvars, typval_T *rettv)
  {
      if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
  	return;
  
      if (argvars[0].v_type != VAR_STRING
  	    || argvars[0].vval.v_string == NULL
  	    || *argvars[0].vval.v_string == NUL)
      {
  	semsg(_(e_invalid_argument_str), tv_get_string(&argvars[0]));
      }
      else
      {
  	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",
  #if defined(SOME_BUILTIN_TCAPS) || defined(ALL_BUILTIN_TCAPS)
  		1
  #else
  		0
  #endif
  		},
  	{"all_builtin_terms",
  #if defined(ALL_BUILTIN_TCAPS)
  		1
  #else
  		0
  #endif
  		},
  	{"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",
  #ifdef FEAT_CMDWIN
  		1
  #else
  		0
  #endif
  		},
  	{"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",
  #ifdef FEAT_SEARCHPATH
  		1
  #else
  		0
  #endif
  		},
  	{"filterpipe",
  #if defined(FEAT_FILTERPIPE) && !defined(VIMDLL)
  		1
  #else
  		0
  #endif
  		},
  	{"find_in_path",
  #ifdef FEAT_FIND_ID
  		1
  #else
  		0
  #endif
  		},
  	{"float",
  #ifdef FEAT_FLOAT
  		1
  #else
  		0
  #endif
  		},
  	{"folding",
  #ifdef FEAT_FOLDING
  		1
  #else
  		0
  #endif
  		},
  	{"footer",
  #ifdef FEAT_FOOTER
  		1
  #else
  		0
  #endif
  		},
  	{"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",
  #ifdef FEAT_PATH_EXTRA
  		1
  #else
  		0
  #endif
  		},
  	{"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
  		},
  	{"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",
  #ifdef FEAT_CMDL_INFO
  		1
  #else
  		0
  #endif
  		},
  	{"cmdline_info",
  #ifdef FEAT_CMDL_INFO
  		1
  #else
  		0
  #endif
  		},
  	{"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",
  #ifdef FEAT_TEXTOBJ
  		1
  #else
  		0
  #endif
  		},
  	{"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",
  #ifdef FEAT_WILDIGN
  		1
  #else
  		0
  #endif
  		},
  	{"wildmenu",
  #ifdef FEAT_WILDMENU
  		1
  #else
  		0
  #endif
  		},
  	{"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
  		    && vim_isdigit(name[6])
  		    && vim_isdigit(name[8])
  		    && vim_isdigit(name[10]))
  	    {
  		int major = atoi((char *)name + 6);
  		int minor = atoi((char *)name + 8);
  
  		// 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((char *)name + 10))))));
  	    }
  	    else
  		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;
  }
  
  /*
   * "hasmapto()" function
   */
      static void
  f_hasmapto(typval_T *argvars, typval_T *rettv)
  {
      char_u	*name;
      char_u	*mode;
      char_u	buf[NUMBUFLEN];
      int		abbr = 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;
  
      name = tv_get_string(&argvars[0]);
      if (argvars[1].v_type == VAR_UNKNOWN)
  	mode = (char_u *)"nvo";
      else
      {
  	mode = tv_get_string_buf(&argvars[1], buf);
  	if (argvars[2].v_type != VAR_UNKNOWN)
  	    abbr = (int)tv_get_bool(&argvars[2]);
      }
  
      if (map_to_exists(name, mode, abbr))
  	rettv->vval.v_number = TRUE;
      else
  	rettv->vval.v_number = FALSE;
  }
  
  /*
   * "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)
      {
  	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;
  	    }
      }
  }
  
  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), 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);
  }
  
  /*
   * "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:
  	    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(&regmatch, str, startcol);
  
  	    if (match && --nth <= 0)
  		break;
  	    if (l == NULL && !match)
  		break;
  
  	    // Advance to just after the match.
  	    if (l != NULL)
  	    {
  		li = li->li_next;
  		++idx;
  	    }
  	    else
  	    {
  		startcol = (colnr_T)(regmatch.startp[0]
  				    + (*mb_ptr2len)(regmatch.startp[0]) - str);
  		if (startcol > (colnr_T)len
  				      || str + startcol <= regmatch.startp[0])
  		{
  		    match = FALSE;
  		    break;
  		}
  	    }
  	}
  
  	if (match)
  	{
  	    if (type == MATCH_POS)
  	    {
  		listitem_T *li1 = rettv->vval.v_list->lv_first;
  		listitem_T *li2 = li1->li_next;
  		listitem_T *li3 = li2->li_next;
  		listitem_T *li4 = li3->li_next;
  
  		vim_free(li1->li_tv.vval.v_string);
  		li1->li_tv.vval.v_string = vim_strnsave(regmatch.startp[0],
  					regmatch.endp[0] - regmatch.startp[0]);
  		li3->li_tv.vval.v_number =
  				      (varnumber_T)(regmatch.startp[0] - expr);
  		li4->li_tv.vval.v_number =
  					(varnumber_T)(regmatch.endp[0] - expr);
  		if (l != NULL)
  		    li2->li_tv.vval.v_number = (varnumber_T)idx;
  	    }
  	    else if (type == MATCH_LIST)
  	    {
  		int i;
  
  		// return list with matched string and submatches
  		for (i = 0; i < NSUBEXP; ++i)
  		{
  		    if (regmatch.endp[i] == NULL)
  		    {
  			if (list_append_string(rettv->vval.v_list,
  						     (char_u *)"", 0) == FAIL)
  			    break;
  		    }
  		    else if (list_append_string(rettv->vval.v_list,
  				regmatch.startp[i],
  				(int)(regmatch.endp[i] - regmatch.startp[i]))
  			    == FAIL)
  			break;
  		}
  	    }
  	    else if (type == MATCH_STR)
  	    {
  		// return matched string
  		if (l != NULL)
  		    copy_tv(&li->li_tv, rettv);
  		else
  		    rettv->vval.v_string = vim_strnsave(regmatch.startp[0],
  					regmatch.endp[0] - regmatch.startp[0]);
  	    }
  	    else if (l != NULL)
  		rettv->vval.v_number = idx;
  	    else
  	    {
  		if (type != MATCH_END)
  		    rettv->vval.v_number =
  				      (varnumber_T)(regmatch.startp[0] - str);
  		else
  		    rettv->vval.v_number =
  					(varnumber_T)(regmatch.endp[0] - str);
  		rettv->vval.v_number += (varnumber_T)(str - expr);
  	    }
  	}
  	vim_regfree(regmatch.regprog);
      }
  
  theend:
      if (type == MATCH_POS && l == NULL && rettv->vval.v_list != NULL)
  	// matchstrpos() without a list: drop the second item.
  	listitem_remove(rettv->vval.v_list,
  				       rettv->vval.v_list->lv_first->li_next);
      vim_free(tofree);
      p_cpo = save_cpo;
  }
  
  /*
   * "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 (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
  	    {
  		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)
  	// Reading /dev/urandom doesn't work, fall back to time().
  #endif
  	*x = vim_time();
  }
  
  #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));
      else if (stride > 0 ? end + 1 < start : end - 1 > start)
  	emsg(_(e_start_past_end));
      else
      {
  	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, &reglen))
      {
  	case MLINE: buf[0] = 'V'; break;
  	case MCHAR: buf[0] = 'v'; break;
  	case MBLOCK:
  		    vim_snprintf((char *)buf, sizeof(buf), "%c%ld", Ctrl_V,
  			    reglen + 1);
  		    break;
      }
      (void)dict_add_string(dict, (char *)"regtype", buf);
  
      buf[0] = get_register_name(get_unname_register());
      buf[1] = NUL;
      if (regname == '"')
  	(void)dict_add_string(dict, (char *)"points_to", buf);
      else
      {
  	dictitem_T	*item = dictitem_alloc((char_u *)"isunnamed");
  
  	if (item != NULL)
  	{
  	    item->di_tv.v_type = VAR_SPECIAL;
  	    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;
      int		n;
      int		slen;
      int		len;
      char_u	*r;
      int		i;
  
      if (in_vim9script()
  	    && (check_for_string_or_number_or_list_arg(argvars, 0) == FAIL
  		|| check_for_number_arg(argvars, 1) == FAIL))
  	return;
  
      n = (int)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
      {
  	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)
      {
  	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;
  	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;
  	}
      }
  
      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
      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)
      {
  	if (list2fpos(&argvars[1], &pos, &fnum, &curswant, charpos) == OK)
  	{
  	    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 (in_vim9script() && check_for_dict_arg(argvars, 0) == FAIL)
  	return;
  
      if (argvars[0].v_type != VAR_DICT)
      {
  	emsg(_(e_dictionary_required));
  	return;
      }
  
      if ((d = argvars[0].vval.v_dict) != NULL)
      {
  	csearch = dict_get_string(d, (char_u *)"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, (char_u *)"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, (char_u *)"points_to", FALSE);
  	    if (stropt != NULL)
  	    {
  		pointreg = *stropt;
  		regname = pointreg;
  	    }
  	}
  	else if (dict_get_bool(d, (char_u *)"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 (argvars[1].v_type != VAR_DICT)
      {
  	emsg(_(e_dictionary_required));
  	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 (argvars[2].v_type == VAR_STRING)
      {
  	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;
  	}
      }
      else
      {
  	emsg(_(e_string_required));
  	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)
      {
  	did_set_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)
      {
  	did_set_spelllang(curwin);
  	curwin->w_p_spell = TRUE;
      }
  
      if (*curwin->w_s->b_p_spl == NUL)
      {
  	emsg(_(e_spell_checking_is_not_possible));
  	curwin->w_p_spell = wo_spell_save;
  	return;
      }
  #endif
  
      if (rettv_list_alloc(rettv) == FAIL)
      {
  #ifdef FEAT_SPELL
  	curwin->w_p_spell = wo_spell_save;
  #endif
  	return;
      }
  
  #ifdef FEAT_SPELL
      str = tv_get_string(&argvars[0]);
      if (argvars[1].v_type != VAR_UNKNOWN)
      {
  	maxcount = (int)tv_get_number_chk(&argvars[1], &typeerr);
  	if (maxcount <= 0)
  	    return;
  	if (argvars[2].v_type != VAR_UNKNOWN)
  	{
  	    need_capital = (int)tv_get_bool_chk(&argvars[2], &typeerr);
  	    if (typeerr)
  		return;
  	}
      }
      else
  	maxcount = 25;
  
      spell_suggest_list(&ga, str, maxcount, need_capital, FALSE);
  
      for (i = 0; i < ga.ga_len; ++i)
      {
  	str = ((char_u **)ga.ga_data)[i];
  
  	li = listitem_alloc();
  	if (li == NULL)
  	    vim_free(str);
  	else
  	{
  	    li->li_tv.v_type = VAR_STRING;
  	    li->li_tv.v_lock = 0;
  	    li->li_tv.vval.v_string = str;
  	    list_append(rettv->vval.v_list, li);
  	}
      }
      ga_clear(&ga);
      curwin->w_p_spell = wo_spell_save;
  #endif
  }
  
      static void
  f_split(typval_T *argvars, typval_T *rettv)
  {
      char_u	*str;
      char_u	*end;
      char_u	*pat = NULL;
      regmatch_T	regmatch;
      char_u	patbuf[NUMBUFLEN];
      char_u	*save_cpo;
      int		match;
      colnr_T	col = 0;
      int		keepempty = FALSE;
      int		typeerr = FALSE;
  
      if (in_vim9script()
  	    && (check_for_string_arg(argvars, 0) == FAIL
  		|| check_for_opt_string_arg(argvars, 1) == FAIL
  		|| (argvars[1].v_type != VAR_UNKNOWN
  		    && check_for_opt_bool_arg(argvars, 2) == FAIL)))
  	return;
  
      // Make 'cpoptions' empty, the 'l' flag should not be used here.
      save_cpo = p_cpo;
      p_cpo = empty_option;
  
      str = tv_get_string(&argvars[0]);
      if (argvars[1].v_type != VAR_UNKNOWN)
      {
  	pat = tv_get_string_buf_chk(&argvars[1], patbuf);
  	if (pat == NULL)
  	    typeerr = TRUE;
  	if (argvars[2].v_type != VAR_UNKNOWN)
  	    keepempty = (int)tv_get_bool_chk(&argvars[2], &typeerr);
      }
      if (pat == NULL || *pat == NUL)
  	pat = (char_u *)"[\\x01- ]\\+";
  
      if (rettv_list_alloc(rettv) == FAIL)
  	goto theend;
      if (typeerr)
  	goto theend;
  
      regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
      if (regmatch.regprog != NULL)
      {
  	regmatch.rm_ic = FALSE;
  	while (*str != NUL || keepempty)
  	{
  	    if (*str == NUL)
  		match = FALSE;	// empty item at the end
  	    else
  		match = vim_regexec_nl(&regmatch, str, col);
  	    if (match)
  		end = regmatch.startp[0];
  	    else
  		end = str + STRLEN(str);
  	    if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0
  			   && *str != NUL && match && end < regmatch.endp[0]))
  	    {
  		if (list_append_string(rettv->vval.v_list, str,
  						    (int)(end - str)) == FAIL)
  		    break;
  	    }
  	    if (!match)
  		break;
  	    // Advance to just after the match.
  	    if (regmatch.endp[0] > str)
  		col = 0;
  	    else
  		// Don't get stuck at the same match.
  		col = (*mb_ptr2len)(regmatch.endp[0]);
  	    str = regmatch.endp[0];
  	}
  
  	vim_regfree(regmatch.regprog);
      }
  
  theend:
      p_cpo = save_cpo;
  }
  
  /*
   * "submatch()" function
   */
      static void
  f_submatch(typval_T *argvars, typval_T *rettv)
  {
      int		error = FALSE;
      int		no;
      int		retList = 0;
  
      if (in_vim9script()
  	    && (check_for_number_arg(argvars, 0) == FAIL
  		|| check_for_opt_bool_arg(argvars, 1) == FAIL))
  	return;
  
      no = (int)tv_get_number_chk(&argvars[0], &error);
      if (error)
  	return;
      if (no < 0 || no >= NSUBEXP)
      {
  	semsg(_(e_invalid_submatch_number_nr), no);
  	return;
      }
      if (argvars[1].v_type != VAR_UNKNOWN)
  	retList = (int)tv_get_bool_chk(&argvars[1], &error);
      if (error)
  	return;
  
      if (retList == 0)
      {
  	rettv->v_type = VAR_STRING;
  	rettv->vval.v_string = reg_submatch(no);
      }
      else
      {
  	rettv->v_type = VAR_LIST;
  	rettv->vval.v_list = reg_submatch_list(no);
      }
  }
  
  /*
   * "substitute()" function
   */
      static void
  f_substitute(typval_T *argvars, typval_T *rettv)
  {
      char_u	patbuf[NUMBUFLEN];
      char_u	subbuf[NUMBUFLEN];
      char_u	flagsbuf[NUMBUFLEN];
      char_u	*str;
      char_u	*pat;
      char_u	*sub = NULL;
      typval_T	*expr = NULL;
      char_u	*flg;
  
      if (in_vim9script()
  	    && (check_for_string_arg(argvars, 0) == FAIL
  		|| check_for_string_arg(argvars, 1) == FAIL
  		|| check_for_string_arg(argvars, 3) == FAIL))
  	return;
  
      str = tv_get_string_chk(&argvars[0]);
      pat = tv_get_string_buf_chk(&argvars[1], patbuf);
      flg = tv_get_string_buf_chk(&argvars[3], flagsbuf);
  
      if (argvars[2].v_type == VAR_FUNC
  	    || argvars[2].v_type == VAR_PARTIAL
  	    || argvars[2].v_type == VAR_INSTR)
  	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);
  }
  
  /*
   * "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':					// 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 (TOLOWER_ASC(what[1]) == 'l')	// ul
  		    p = highlight_color(id, what, modec);
  		else if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c')
  							// underline
  		    p = highlight_has_attr(id, HL_UNDERLINE, modec);
  		else
  							// undercurl
  		    p = highlight_has_attr(id, HL_UNDERCURL, 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_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)
  {
  #ifdef FEAT_WILDMENU
      if (wild_menu_showing || ((State & MODE_CMDLINE) && cmdline_pum_active()))
  	rettv->vval.v_number = 1;
  #endif
  }
  
  /*
   * "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
  