From a9b579f3d7463720a316e11e77a7a9fbb9267986 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 17 Jul 2016 18:29:19 +0200 Subject: [PATCH 1/9] patch 7.4.2058 Problem: eval.c is too big. Solution: Move user functions to userfunc.c --- Filelist | 2 + src/Makefile | 10 + src/eval.c | 3579 +--------------------------------------- src/globals.h | 2 + src/proto.h | 1 + src/proto/eval.pro | 47 +- src/proto/userfunc.pro | 52 + src/structs.h | 45 + src/userfunc.c | 3494 +++++++++++++++++++++++++++++++++++++++ src/version.c | 2 + src/vim.h | 25 + 11 files changed, 3728 insertions(+), 3531 deletions(-) create mode 100644 src/proto/userfunc.pro create mode 100644 src/userfunc.c diff --git a/Filelist b/Filelist index c395418cb0..dc9a2308b7 100644 --- a/Filelist +++ b/Filelist @@ -84,6 +84,7 @@ SRC_ALL = \ src/termlib.c \ src/ui.c \ src/undo.c \ + src/userfunc.c \ src/version.c \ src/version.h \ src/vim.h \ @@ -175,6 +176,7 @@ SRC_ALL = \ src/proto/termlib.pro \ src/proto/ui.pro \ src/proto/undo.pro \ + src/proto/userfunc.pro \ src/proto/version.pro \ src/proto/winclip.pro \ src/proto/window.pro \ diff --git a/src/Makefile b/src/Makefile index 8b15226669..7ab4d328f6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1531,6 +1531,7 @@ BASIC_SRC = \ term.c \ ui.c \ undo.c \ + userfunc.c \ version.c \ window.c \ $(OS_EXTRA_SRC) @@ -1631,6 +1632,7 @@ OBJ_COMMON = \ objects/term.o \ objects/ui.o \ objects/undo.o \ + objects/userfunc.o \ objects/version.o \ objects/window.o \ $(GUI_OBJ) \ @@ -1731,6 +1733,7 @@ PRO_AUTO = \ termlib.pro \ ui.pro \ undo.pro \ + userfunc.pro \ version.pro \ window.pro \ gui_beval.pro \ @@ -3066,6 +3069,9 @@ objects/ui.o: ui.c objects/undo.o: undo.c $(CCC) -o $@ undo.c +objects/userfunc.o: userfunc.c + $(CCC) -o $@ userfunc.c + objects/window.o: window.c $(CCC) -o $@ window.c @@ -3379,6 +3385,10 @@ objects/undo.o: undo.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \ ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ gui_beval.h proto/gui_beval.pro alloc.h ex_cmds.h proto.h globals.h \ farsi.h arabic.h +objects/userfunc.o: userfunc.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \ + ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ + gui_beval.h proto/gui_beval.pro alloc.h ex_cmds.h proto.h globals.h \ + farsi.h arabic.h objects/version.o: version.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \ ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ gui_beval.h proto/gui_beval.pro alloc.h ex_cmds.h proto.h globals.h \ diff --git a/src/eval.c b/src/eval.c index 3b4f2c460d..f930e47b62 100644 --- a/src/eval.c +++ b/src/eval.c @@ -30,54 +30,6 @@ #define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */ -#define DO_NOT_FREE_CNT 99999 /* refcount for dict or list that should not - be freed. */ - -/* - * Structure returned by get_lval() and used by set_var_lval(). - * For a plain name: - * "name" points to the variable name. - * "exp_name" is NULL. - * "tv" is NULL - * For a magic braces name: - * "name" points to the expanded variable name. - * "exp_name" is non-NULL, to be freed later. - * "tv" is NULL - * For an index in a list: - * "name" points to the (expanded) variable name. - * "exp_name" NULL or non-NULL, to be freed later. - * "tv" points to the (first) list item value - * "li" points to the (first) list item - * "range", "n1", "n2" and "empty2" indicate what items are used. - * For an existing Dict item: - * "name" points to the (expanded) variable name. - * "exp_name" NULL or non-NULL, to be freed later. - * "tv" points to the dict item value - * "newkey" is NULL - * For a non-existing Dict item: - * "name" points to the (expanded) variable name. - * "exp_name" NULL or non-NULL, to be freed later. - * "tv" points to the Dictionary typval_T - * "newkey" is the key for the new item. - */ -typedef struct lval_S -{ - char_u *ll_name; /* start of variable name (can be NULL) */ - char_u *ll_exp_name; /* NULL or expanded name in allocated memory. */ - typval_T *ll_tv; /* Typeval of item being used. If "newkey" - isn't NULL it's the Dict to which to add - the item. */ - listitem_T *ll_li; /* The list item or NULL. */ - list_T *ll_list; /* The list or NULL. */ - int ll_range; /* TRUE when a [i:j] range was used */ - long ll_n1; /* First index for list */ - long ll_n2; /* Second index for list range */ - int ll_empty2; /* Second index is empty: [i:] */ - dict_T *ll_dict; /* The Dictionary or NULL */ - dictitem_T *ll_di; /* The dictitem or NULL */ - char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */ -} lval_T; - static char *e_letunexp = N_("E18: Unexpected characters in :let"); static char *e_undefvar = N_("E121: Undefined variable: %s"); static char *e_missbrac = N_("E111: Missing ']'"); @@ -87,14 +39,8 @@ static char *e_listreq = N_("E714: List required"); #ifdef FEAT_QUICKFIX static char *e_stringreq = N_("E928: String required"); #endif -static char *e_toomanyarg = N_("E118: Too many arguments for function: %s"); -static char *e_dictkey = N_("E716: Key not present in Dictionary: %s"); -static char *e_funcexts = N_("E122: Function %s already exists, add ! to replace it"); -static char *e_funcdict = N_("E717: Dictionary entry already exists"); -static char *e_funcref = N_("E718: Funcref required"); static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); static char *e_letwrong = N_("E734: Wrong variable type for %s="); -static char *e_nofunc = N_("E130: Unknown function: %s"); static char *e_illvar = N_("E461: Illegal variable name: %s"); #ifdef FEAT_FLOAT static char *e_float_as_string = N_("E806: using Float as a String"); @@ -134,107 +80,9 @@ static garray_T ga_scripts = {0, 0, sizeof(scriptvar_T *), 4, NULL}; static int echo_attr = 0; /* attributes used for ":echo" */ -/* Values for trans_function_name() argument: */ -#define TFN_INT 1 /* internal function name OK */ -#define TFN_QUIET 2 /* no error messages */ -#define TFN_NO_AUTOLOAD 4 /* do not use script autoloading */ - -/* Values for get_lval() flags argument: */ -#define GLV_QUIET TFN_QUIET /* no error messages */ -#define GLV_NO_AUTOLOAD TFN_NO_AUTOLOAD /* do not use script autoloading */ - -/* - * Structure to hold info for a user function. - */ -typedef struct ufunc ufunc_T; - -struct ufunc -{ - int uf_varargs; /* variable nr of arguments */ - int uf_flags; - int uf_calls; /* nr of active calls */ - garray_T uf_args; /* arguments */ - garray_T uf_lines; /* function lines */ -#ifdef FEAT_PROFILE - int uf_profiling; /* TRUE when func is being profiled */ - /* profiling the function as a whole */ - int uf_tm_count; /* nr of calls */ - proftime_T uf_tm_total; /* time spent in function + children */ - proftime_T uf_tm_self; /* time spent in function itself */ - proftime_T uf_tm_children; /* time spent in children this call */ - /* profiling the function per line */ - int *uf_tml_count; /* nr of times line was executed */ - proftime_T *uf_tml_total; /* time spent in a line + children */ - proftime_T *uf_tml_self; /* time spent in a line itself */ - proftime_T uf_tml_start; /* start time for current line */ - proftime_T uf_tml_children; /* time spent in children for this line */ - proftime_T uf_tml_wait; /* start wait time for current line */ - int uf_tml_idx; /* index of line being timed; -1 if none */ - int uf_tml_execed; /* line being timed was executed */ -#endif - scid_T uf_script_ID; /* ID of script where function was defined, - used for s: variables */ - int uf_refcount; /* for numbered function: reference count */ - char_u uf_name[1]; /* name of function (actually longer); can - start with 123_ ( is K_SPECIAL - KS_EXTRA KE_SNR) */ -}; - -/* function flags */ -#define FC_ABORT 1 /* abort function on error */ -#define FC_RANGE 2 /* function accepts range */ -#define FC_DICT 4 /* Dict function, uses "self" */ - -/* - * All user-defined functions are found in this hashtable. - */ -static hashtab_T func_hashtab; - /* The names of packages that once were loaded are remembered. */ static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL}; -/* From user function to hashitem and back. */ -static ufunc_T dumuf; -#define UF2HIKEY(fp) ((fp)->uf_name) -#define HIKEY2UF(p) ((ufunc_T *)(p - (dumuf.uf_name - (char_u *)&dumuf))) -#define HI2UF(hi) HIKEY2UF((hi)->hi_key) - -#define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j] -#define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j] - -#define MAX_FUNC_ARGS 20 /* maximum number of function arguments */ -#define VAR_SHORT_LEN 20 /* short variable name length */ -#define FIXVAR_CNT 12 /* number of fixed variables */ - -/* structure to hold info for a function that is currently being executed. */ -typedef struct funccall_S funccall_T; - -struct funccall_S -{ - ufunc_T *func; /* function being called */ - int linenr; /* next line to be executed */ - int returned; /* ":return" used */ - struct /* fixed variables for arguments */ - { - dictitem_T var; /* variable (without room for name) */ - char_u room[VAR_SHORT_LEN]; /* room for the name */ - } fixvar[FIXVAR_CNT]; - dict_T l_vars; /* l: local function variables */ - dictitem_T l_vars_var; /* variable for l: scope */ - dict_T l_avars; /* a: argument variables */ - dictitem_T l_avars_var; /* variable for a: scope */ - list_T l_varlist; /* list for a:000 */ - listitem_T l_listitems[MAX_FUNC_ARGS]; /* listitems for a:000 */ - typval_T *rettv; /* return value */ - linenr_T breakpoint; /* next line with breakpoint or zero */ - int dbg_tick; /* debug_tick when breakpoint was set */ - int level; /* top nesting level of executed function */ -#ifdef FEAT_PROFILE - proftime_T prof_child; /* time spent in a child */ -#endif - funccall_T *caller; /* calling function or NULL */ -}; - /* * Info used by a ":for" loop. */ @@ -246,16 +94,6 @@ typedef struct list_T *fi_list; /* list being used */ } forinfo_T; -/* - * Struct used by trans_function_name() - */ -typedef struct -{ - dict_T *fd_dict; /* Dictionary used */ - char_u *fd_newkey; /* new key in "dict" in allocated memory */ - dictitem_T *fd_di; /* Dictionary item used */ -} funcdict_T; - /* * Array to hold the value of v: variables. @@ -373,7 +211,6 @@ static void restore_vimvar(int idx, typval_T *save_tv); static int ex_let_vars(char_u *arg, typval_T *tv, int copy, int semicolon, int var_count, char_u *nextchars); static char_u *skip_var_list(char_u *arg, int *var_count, int *semicolon); static char_u *skip_var_one(char_u *arg); -static void list_hashtable_vars(hashtab_T *ht, char_u *prefix, int empty, int *first); static void list_glob_vars(int *first); static void list_buf_vars(int *first); static void list_win_vars(int *first); @@ -382,12 +219,9 @@ static void list_tab_vars(int *first); #endif static void list_vim_vars(int *first); static void list_script_vars(int *first); -static void list_func_vars(int *first); static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first); static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, char_u *endchars, char_u *op); static int check_changedtick(char_u *arg); -static char_u *get_lval(char_u *name, typval_T *rettv, lval_T *lp, int unlet, int skip, int flags, int fne_flags); -static void clear_lval(lval_T *lp); static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, char_u *op); static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op); static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep); @@ -396,7 +230,6 @@ static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock); static void item_lock(typval_T *tv, int deep, int lock); static int tv_islocked(typval_T *tv); -static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate); static int eval2(char_u **arg, typval_T *rettv, int evaluate); static int eval3(char_u **arg, typval_T *rettv, int evaluate); static int eval4(char_u **arg, typval_T *rettv, int evaluate); @@ -409,14 +242,7 @@ static int get_option_tv(char_u **arg, typval_T *rettv, int evaluate); static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate); static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate); static int free_unref_items(int copyID); -static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, int *varargs, int skip); -static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate); -static char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate); -static int find_internal_func(char_u *name); -static char_u *deref_func_name(char_u *name, int *lenp, partial_T **partial, int no_autoload); -static int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict); -static void emsg_funcname(char *ermsg, char_u *name); static int non_zero_arg(typval_T *argvars); #ifdef FEAT_FLOAT @@ -818,17 +644,9 @@ static void f_xor(typval_T *argvars, typval_T *rettv); static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp); static pos_T *var2fpos(typval_T *varp, int dollar_lnum, int *fnum); static int get_env_len(char_u **arg); -static int get_id_len(char_u **arg); static int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose); -static char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end, int flags); -#define FNE_INCL_BR 1 /* find_name_end(): include [] in name */ -#define FNE_CHECK_START 2 /* find_name_end(): check name starts with - valid character */ static char_u * make_expanded_name(char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end); -static int eval_isnamec(int c); -static int eval_isnamec1(int c); static int get_var_tv(char_u *name, int len, typval_T *rettv, dictitem_T **dip, int verbose, int no_autoload); -static int handle_subscript(char_u **arg, typval_T *rettv, int evaluate, int verbose); static typval_T *alloc_string_tv(char_u *string); static void init_tv(typval_T *varp); #ifdef FEAT_FLOAT @@ -836,47 +654,14 @@ static float_T get_tv_float(typval_T *varp); #endif static linenr_T get_tv_lnum(typval_T *argvars); static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf); -static dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload); static dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload); static hashtab_T *find_var_ht(char_u *name, char_u **varname); -static funccall_T *get_funccal(void); -static void vars_clear_ext(hashtab_T *ht, int free_val); static void delete_var(hashtab_T *ht, hashitem_T *hi); static void list_one_var(dictitem_T *v, char_u *prefix, int *first); static void list_one_var_a(char_u *prefix, char_u *name, int type, char_u *string, int *first); static void set_var(char_u *name, typval_T *varp, int copy); static int var_check_fixed(int flags, char_u *name, int use_gettext); static char_u *find_option_end(char_u **arg, int *opt_flags); -static char_u *trans_function_name(char_u **pp, int skip, int flags, funcdict_T *fd, partial_T **partial); -static int eval_fname_script(char_u *p); -static int eval_fname_sid(char_u *p); -static void list_func_head(ufunc_T *fp, int indent); -static ufunc_T *find_func(char_u *name); -static int function_exists(char_u *name); -static int builtin_function(char_u *name, int len); -#ifdef FEAT_PROFILE -static void func_do_profile(ufunc_T *fp); -static void prof_sort_list(FILE *fd, ufunc_T **sorttab, int st_len, char *title, int prefer_self); -static void prof_func_line(FILE *fd, int count, proftime_T *total, proftime_T *self, int prefer_self); -static int -# ifdef __BORLANDC__ - _RTLENTRYF -# endif - prof_total_cmp(const void *s1, const void *s2); -static int -# ifdef __BORLANDC__ - _RTLENTRYF -# endif - prof_self_cmp(const void *s1, const void *s2); -#endif -static int script_autoload(char_u *name, int reload); -static char_u *autoload_name(char_u *name); -static void cat_func_name(char_u *buf, ufunc_T *fp); -static void func_free(ufunc_T *fp); -static void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, linenr_T firstline, linenr_T lastline, dict_T *selfdict); -static int can_free_funccal(funccall_T *fc, int copyID) ; -static void free_funccal(funccall_T *fc, int free_val); -static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr); static win_T *find_win_by_nr(typval_T *vp, tabpage_T *tp); static win_T *find_tabwin(typval_T *wvp, typval_T *tvp); static void getwinvar(typval_T *argvars, typval_T *rettv, int off); @@ -905,7 +690,7 @@ eval_init(void) init_var_dict(&vimvardict, &vimvars_var, VAR_SCOPE); vimvardict.dv_lock = VAR_FIXED; hash_init(&compat_hashtab); - hash_init(&func_hashtab); + func_init(); for (i = 0; i < VV_LEN; ++i) { @@ -1002,62 +787,9 @@ eval_clear(void) /* functions */ free_all_functions(); - hash_clear(&func_hashtab); } #endif -/* - * Return the name of the executed function. - */ - char_u * -func_name(void *cookie) -{ - return ((funccall_T *)cookie)->func->uf_name; -} - -/* - * Return the address holding the next breakpoint line for a funccall cookie. - */ - linenr_T * -func_breakpoint(void *cookie) -{ - return &((funccall_T *)cookie)->breakpoint; -} - -/* - * Return the address holding the debug tick for a funccall cookie. - */ - int * -func_dbg_tick(void *cookie) -{ - return &((funccall_T *)cookie)->dbg_tick; -} - -/* - * Return the nesting level for a funccall cookie. - */ - int -func_level(void *cookie) -{ - return ((funccall_T *)cookie)->level; -} - -/* pointer to funccal for currently active function */ -funccall_T *current_funccal = NULL; - -/* pointer to list of previously used funccal, still around because some - * item in it is still being used. */ -funccall_T *previous_funccal = NULL; - -/* - * Return TRUE when a function was ended by a ":return" command. - */ - int -current_func_returned(void) -{ - return current_funccal->returned; -} - /* * Set an internal variable to a string value. Creates the variable if it does @@ -1767,65 +1499,6 @@ call_func_retlist( } #endif -/* - * Save the current function call pointer, and set it to NULL. - * Used when executing autocommands and for ":source". - */ - void * -save_funccal(void) -{ - funccall_T *fc = current_funccal; - - current_funccal = NULL; - return (void *)fc; -} - - void -restore_funccal(void *vfc) -{ - funccall_T *fc = (funccall_T *)vfc; - - current_funccal = fc; -} - -#if defined(FEAT_PROFILE) || defined(PROTO) -/* - * Prepare profiling for entering a child or something else that is not - * counted for the script/function itself. - * Should always be called in pair with prof_child_exit(). - */ - void -prof_child_enter( - proftime_T *tm) /* place to store waittime */ -{ - funccall_T *fc = current_funccal; - - if (fc != NULL && fc->func->uf_profiling) - profile_start(&fc->prof_child); - script_prof_save(tm); -} - -/* - * Take care of time spent in a child. - * Should always be called after prof_child_enter(). - */ - void -prof_child_exit( - proftime_T *tm) /* where waittime was stored */ -{ - funccall_T *fc = current_funccal; - - if (fc != NULL && fc->func->uf_profiling) - { - profile_end(&fc->prof_child); - profile_sub_wait(tm, &fc->prof_child); /* don't count waiting time */ - profile_add(&fc->func->uf_tm_children, &fc->prof_child); - profile_add(&fc->func->uf_tml_children, &fc->prof_child); - } - script_prof_restore(tm); -} -#endif - #ifdef FEAT_FOLDING /* @@ -2130,7 +1803,7 @@ skip_var_one(char_u *arg) * List variables for hashtab "ht" with prefix "prefix". * If "empty" is TRUE also list NULL strings as empty strings. */ - static void + void list_hashtable_vars( hashtab_T *ht, char_u *prefix, @@ -2222,17 +1895,6 @@ list_script_vars(int *first) (char_u *)"s:", FALSE, first); } -/* - * List function variables, if there is a function. - */ - static void -list_func_vars(int *first) -{ - if (current_funccal != NULL) - list_hashtable_vars(¤t_funccal->l_vars.dv_hashtab, - (char_u *)"l:", FALSE, first); -} - /* * List variables in "arg". */ @@ -2567,7 +2229,7 @@ check_changedtick(char_u *arg) * When an evaluation error occurs "lp->ll_name" is NULL; * Returns NULL for a parsing error. Still need to free items in "lp"! */ - static char_u * + char_u * get_lval( char_u *name, typval_T *rettv, @@ -2912,7 +2574,7 @@ get_lval( /* * Clear lval "lp" that was filled by get_lval(). */ - static void + void clear_lval(lval_T *lp) { vim_free(lp->ll_exp_name); @@ -3404,139 +3066,6 @@ set_context_for_expression( #endif /* FEAT_CMDL_COMPL */ -/* - * ":1,25call func(arg1, arg2)" function call. - */ - void -ex_call(exarg_T *eap) -{ - char_u *arg = eap->arg; - char_u *startarg; - char_u *name; - char_u *tofree; - int len; - typval_T rettv; - linenr_T lnum; - int doesrange; - int failed = FALSE; - funcdict_T fudi; - partial_T *partial = NULL; - - if (eap->skip) - { - /* trans_function_name() doesn't work well when skipping, use eval0() - * instead to skip to any following command, e.g. for: - * :if 0 | call dict.foo().bar() | endif */ - ++emsg_skip; - if (eval0(eap->arg, &rettv, &eap->nextcmd, FALSE) != FAIL) - clear_tv(&rettv); - --emsg_skip; - return; - } - - tofree = trans_function_name(&arg, eap->skip, TFN_INT, &fudi, &partial); - if (fudi.fd_newkey != NULL) - { - /* Still need to give an error message for missing key. */ - EMSG2(_(e_dictkey), fudi.fd_newkey); - vim_free(fudi.fd_newkey); - } - if (tofree == NULL) - return; - - /* Increase refcount on dictionary, it could get deleted when evaluating - * the arguments. */ - if (fudi.fd_dict != NULL) - ++fudi.fd_dict->dv_refcount; - - /* If it is the name of a variable of type VAR_FUNC or VAR_PARTIAL use its - * contents. For VAR_PARTIAL get its partial, unless we already have one - * from trans_function_name(). */ - len = (int)STRLEN(tofree); - name = deref_func_name(tofree, &len, - partial != NULL ? NULL : &partial, FALSE); - - /* Skip white space to allow ":call func ()". Not good, but required for - * backward compatibility. */ - startarg = skipwhite(arg); - rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ - - if (*startarg != '(') - { - EMSG2(_("E107: Missing parentheses: %s"), eap->arg); - goto end; - } - - /* - * When skipping, evaluate the function once, to find the end of the - * arguments. - * When the function takes a range, this is discovered after the first - * call, and the loop is broken. - */ - if (eap->skip) - { - ++emsg_skip; - lnum = eap->line2; /* do it once, also with an invalid range */ - } - else - lnum = eap->line1; - for ( ; lnum <= eap->line2; ++lnum) - { - if (!eap->skip && eap->addr_count > 0) - { - curwin->w_cursor.lnum = lnum; - curwin->w_cursor.col = 0; -#ifdef FEAT_VIRTUALEDIT - curwin->w_cursor.coladd = 0; -#endif - } - arg = startarg; - if (get_func_tv(name, (int)STRLEN(name), &rettv, &arg, - eap->line1, eap->line2, &doesrange, - !eap->skip, partial, fudi.fd_dict) == FAIL) - { - failed = TRUE; - break; - } - - /* Handle a function returning a Funcref, Dictionary or List. */ - if (handle_subscript(&arg, &rettv, !eap->skip, TRUE) == FAIL) - { - failed = TRUE; - break; - } - - clear_tv(&rettv); - if (doesrange || eap->skip) - break; - - /* Stop when immediately aborting on error, or when an interrupt - * occurred or an exception was thrown but not caught. - * get_func_tv() returned OK, so that the check for trailing - * characters below is executed. */ - if (aborting()) - break; - } - if (eap->skip) - --emsg_skip; - - if (!failed) - { - /* Check for trailing illegal characters and a following command. */ - if (!ends_excmd(*arg)) - { - emsg_severe = TRUE; - EMSG(_(e_trailing)); - } - else - eap->nextcmd = check_nextcmd(arg); - } - -end: - dict_unref(fudi.fd_dict); - vim_free(tofree); -} - /* * ":unlet[!] var1 ... " command. */ @@ -3703,22 +3232,23 @@ do_unlet(char_u *name, int forceit) ht = find_var_ht(name, &varname); if (ht != NULL && *varname != NUL) { - if (ht == &globvarht) - d = &globvardict; - else if (current_funccal != NULL - && ht == ¤t_funccal->l_vars.dv_hashtab) - d = ¤t_funccal->l_vars; - else if (ht == &compat_hashtab) - d = &vimvardict; - else - { - di = find_var_in_ht(ht, *name, (char_u *)"", FALSE); - d = di == NULL ? NULL : di->di_tv.vval.v_dict; - } + d = get_current_funccal_dict(ht); if (d == NULL) { - EMSG2(_(e_intern2), "do_unlet()"); - return FAIL; + if (ht == &globvarht) + d = &globvardict; + else if (ht == &compat_hashtab) + d = &vimvardict; + else + { + di = find_var_in_ht(ht, *name, (char_u *)"", FALSE); + d = di == NULL ? NULL : di->di_tv.vval.v_dict; + } + if (d == NULL) + { + EMSG2(_(e_intern2), "do_unlet()"); + return FAIL; + } } hi = hash_find(ht, varname); if (!HASHITEM_EMPTY(hi)) @@ -4115,7 +3645,7 @@ typedef enum * Note: "rettv.v_lock" is not set. * Return OK or FAIL. */ - static int + int eval0( char_u *arg, typval_T *rettv, @@ -6088,9 +5618,6 @@ get_copyID(void) return current_copyID; } -/* Used by get_func_tv() */ -static garray_T funcargs = GA_EMPTY; - /* * Garbage collection for lists and dictionaries. * @@ -6124,9 +5651,7 @@ garbage_collect(int testing) buf_T *buf; win_T *wp; int i; - funccall_T *fc, **pfc; int did_free = FALSE; - int did_free_funccal = FALSE; #ifdef FEAT_WINDOWS tabpage_T *tp; #endif @@ -6151,13 +5676,7 @@ garbage_collect(int testing) /* Don't free variables in the previous_funccal list unless they are only * referenced through previous_funccal. This must be first, because if * the item is referenced elsewhere the funccal must not be freed. */ - for (fc = previous_funccal; fc != NULL; fc = fc->caller) - { - abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1, - NULL); - abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1, - NULL); - } + abort = abort || set_ref_in_previous_funccal(copyID); /* script-local variables */ for (i = 1; i <= ga_scripts.ga_len; ++i) @@ -6189,16 +5708,10 @@ garbage_collect(int testing) abort = abort || set_ref_in_ht(&globvarht, copyID, NULL); /* function-local variables */ - for (fc = current_funccal; fc != NULL; fc = fc->caller) - { - abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL); - abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL); - } + abort = abort || set_ref_in_call_stack(copyID); /* function call arguments, if v:testing is set. */ - for (i = 0; i < funcargs.ga_len; ++i) - abort = abort || set_ref_in_item(((typval_T **)funcargs.ga_data)[i], - copyID, NULL, NULL); + abort = abort || set_ref_in_func_args(copyID); /* v: vars */ abort = abort || set_ref_in_ht(&vimvarht, copyID, NULL); @@ -6236,24 +5749,9 @@ garbage_collect(int testing) /* * 3. Check if any funccal can be freed now. + * This may call us back recursively. */ - for (pfc = &previous_funccal; *pfc != NULL; ) - { - if (can_free_funccal(*pfc, copyID)) - { - fc = *pfc; - *pfc = fc->caller; - free_funccal(fc, TRUE); - did_free = TRUE; - did_free_funccal = TRUE; - } - else - pfc = &(*pfc)->caller; - } - if (did_free_funccal) - /* When a funccal was freed some more items might be garbage - * collected, so run again. */ - (void)garbage_collect(testing); + free_unref_funccal(copyID, testing); } else if (p_verbose > 0) { @@ -6567,202 +6065,6 @@ set_ref_in_item( return abort; } -/* Get function arguments. */ - static int -get_function_args( - char_u **argp, - char_u endchar, - garray_T *newargs, - int *varargs, - int skip) -{ - int mustend = FALSE; - char_u *arg = *argp; - char_u *p = arg; - int c; - int i; - - if (newargs != NULL) - ga_init2(newargs, (int)sizeof(char_u *), 3); - - if (varargs != NULL) - *varargs = FALSE; - - /* - * Isolate the arguments: "arg1, arg2, ...)" - */ - while (*p != endchar) - { - if (p[0] == '.' && p[1] == '.' && p[2] == '.') - { - if (varargs != NULL) - *varargs = TRUE; - p += 3; - mustend = TRUE; - } - else - { - arg = p; - while (ASCII_ISALNUM(*p) || *p == '_') - ++p; - if (arg == p || isdigit(*arg) - || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0) - || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0)) - { - if (!skip) - EMSG2(_("E125: Illegal argument: %s"), arg); - break; - } - if (newargs != NULL && ga_grow(newargs, 1) == FAIL) - return FAIL; - if (newargs != NULL) - { - c = *p; - *p = NUL; - arg = vim_strsave(arg); - if (arg == NULL) - goto err_ret; - - /* Check for duplicate argument name. */ - for (i = 0; i < newargs->ga_len; ++i) - if (STRCMP(((char_u **)(newargs->ga_data))[i], arg) == 0) - { - EMSG2(_("E853: Duplicate argument name: %s"), arg); - vim_free(arg); - goto err_ret; - } - ((char_u **)(newargs->ga_data))[newargs->ga_len] = arg; - newargs->ga_len++; - - *p = c; - } - if (*p == ',') - ++p; - else - mustend = TRUE; - } - p = skipwhite(p); - if (mustend && *p != endchar) - { - if (!skip) - EMSG2(_(e_invarg2), *argp); - break; - } - } - ++p; /* skip the ')' */ - - *argp = p; - return OK; - -err_ret: - if (newargs != NULL) - ga_clear_strings(newargs); - return FAIL; -} - -/* - * Parse a lambda expression and get a Funcref from "*arg". - * Return OK or FAIL. Returns NOTDONE for dict or {expr}. - */ - static int -get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) -{ - garray_T newargs; - garray_T newlines; - ufunc_T *fp = NULL; - int varargs; - int ret; - char_u name[20]; - char_u *start = skipwhite(*arg + 1); - char_u *s, *e; - static int lambda_no = 0; - - ga_init(&newargs); - ga_init(&newlines); - - /* First, check if this is a lambda expression. "->" must exist. */ - ret = get_function_args(&start, '-', NULL, NULL, TRUE); - if (ret == FAIL || *start != '>') - return NOTDONE; - - /* Parse the arguments again. */ - *arg = skipwhite(*arg + 1); - ret = get_function_args(arg, '-', &newargs, &varargs, FALSE); - if (ret == FAIL || **arg != '>') - goto errret; - - /* Get the start and the end of the expression. */ - *arg = skipwhite(*arg + 1); - s = *arg; - ret = skip_expr(arg); - if (ret == FAIL) - goto errret; - e = *arg; - *arg = skipwhite(*arg); - if (**arg != '}') - goto errret; - ++*arg; - - if (evaluate) - { - int len; - char_u *p; - - fp = (ufunc_T *)alloc((unsigned)(sizeof(ufunc_T) + 20)); - if (fp == NULL) - goto errret; - - sprintf((char*)name, "%d", ++lambda_no); - - ga_init2(&newlines, (int)sizeof(char_u *), 1); - if (ga_grow(&newlines, 1) == FAIL) - goto errret; - - /* Add "return " before the expression. - * TODO: Support multiple expressions. */ - len = 7 + e - s + 1; - p = (char_u *)alloc(len); - if (p == NULL) - goto errret; - ((char_u **)(newlines.ga_data))[newlines.ga_len++] = p; - STRCPY(p, "return "); - STRNCPY(p + 7, s, e - s); - p[7 + e - s] = NUL; - - fp->uf_refcount = 1; - STRCPY(fp->uf_name, name); - hash_add(&func_hashtab, UF2HIKEY(fp)); - fp->uf_args = newargs; - fp->uf_lines = newlines; - -#ifdef FEAT_PROFILE - fp->uf_tml_count = NULL; - fp->uf_tml_total = NULL; - fp->uf_tml_self = NULL; - fp->uf_profiling = FALSE; - if (prof_def_func()) - func_do_profile(fp); -#endif - fp->uf_varargs = TRUE; - fp->uf_flags = 0; - fp->uf_calls = 0; - fp->uf_script_ID = current_SID; - - rettv->vval.v_string = vim_strsave(name); - rettv->v_type = VAR_FUNC; - } - else - ga_clear_strings(&newargs); - - return OK; - -errret: - ga_clear_strings(&newargs); - ga_clear_strings(&newlines); - vim_free(fp); - return FAIL; -} - static char * get_var_special_name(int nr) { @@ -6973,7 +6275,7 @@ echo_string_core( * When "copyID" is not NULL replace recursive lists and dicts with "...". * May return NULL. */ - static char_u * + char_u * echo_string( typval_T *tv, char_u **tofree, @@ -7624,7 +6926,7 @@ sortFunctions(void) * Find internal function in table above. * Return index, or -1 if not found */ - static int + int find_internal_func( char_u *name) /* name of the function */ { @@ -7650,431 +6952,25 @@ find_internal_func( return -1; } -/* - * Check if "name" is a variable of type VAR_FUNC. If so, return the function - * name it contains, otherwise return "name". - * If "partialp" is not NULL, and "name" is of type VAR_PARTIAL also set - * "partialp". - */ - static char_u * -deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload) -{ - dictitem_T *v; - int cc; - - if (partialp != NULL) - *partialp = NULL; - - cc = name[*lenp]; - name[*lenp] = NUL; - v = find_var(name, NULL, no_autoload); - name[*lenp] = cc; - if (v != NULL && v->di_tv.v_type == VAR_FUNC) - { - if (v->di_tv.vval.v_string == NULL) - { - *lenp = 0; - return (char_u *)""; /* just in case */ - } - *lenp = (int)STRLEN(v->di_tv.vval.v_string); - return v->di_tv.vval.v_string; - } - - if (v != NULL && v->di_tv.v_type == VAR_PARTIAL) - { - partial_T *pt = v->di_tv.vval.v_partial; - - if (pt == NULL) - { - *lenp = 0; - return (char_u *)""; /* just in case */ - } - if (partialp != NULL) - *partialp = pt; - *lenp = (int)STRLEN(pt->pt_name); - return pt->pt_name; - } - - return name; -} - -/* - * Allocate a variable for the result of a function. - * Return OK or FAIL. - */ - static int -get_func_tv( - char_u *name, /* name of the function */ - int len, /* length of "name" */ - typval_T *rettv, - char_u **arg, /* argument, pointing to the '(' */ - linenr_T firstline, /* first line of range */ - linenr_T lastline, /* last line of range */ - int *doesrange, /* return: function handled range */ - int evaluate, - partial_T *partial, /* for extra arguments */ - dict_T *selfdict) /* Dictionary for "self" */ -{ - char_u *argp; - int ret = OK; - typval_T argvars[MAX_FUNC_ARGS + 1]; /* vars for arguments */ - int argcount = 0; /* number of arguments found */ - - /* - * Get the arguments. - */ - argp = *arg; - while (argcount < MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) - { - argp = skipwhite(argp + 1); /* skip the '(' or ',' */ - if (*argp == ')' || *argp == ',' || *argp == NUL) - break; - if (eval1(&argp, &argvars[argcount], evaluate) == FAIL) - { - ret = FAIL; - break; - } - ++argcount; - if (*argp != ',') - break; - } - if (*argp == ')') - ++argp; - else - ret = FAIL; - - if (ret == OK) - { - int i = 0; - - if (get_vim_var_nr(VV_TESTING)) - { - /* Prepare for calling test_garbagecollect_now(), need to know - * what variables are used on the call stack. */ - if (funcargs.ga_itemsize == 0) - ga_init2(&funcargs, (int)sizeof(typval_T *), 50); - for (i = 0; i < argcount; ++i) - if (ga_grow(&funcargs, 1) == OK) - ((typval_T **)funcargs.ga_data)[funcargs.ga_len++] = - &argvars[i]; - } - - ret = call_func(name, len, rettv, argcount, argvars, - firstline, lastline, doesrange, evaluate, partial, selfdict); - - funcargs.ga_len -= i; - } - else if (!aborting()) - { - if (argcount == MAX_FUNC_ARGS) - emsg_funcname(N_("E740: Too many arguments for function %s"), name); - else - emsg_funcname(N_("E116: Invalid arguments for function %s"), name); - } - - while (--argcount >= 0) - clear_tv(&argvars[argcount]); - - *arg = skipwhite(argp); - return ret; -} - -#define ERROR_UNKNOWN 0 -#define ERROR_TOOMANY 1 -#define ERROR_TOOFEW 2 -#define ERROR_SCRIPT 3 -#define ERROR_DICT 4 -#define ERROR_NONE 5 -#define ERROR_OTHER 6 -#define FLEN_FIXED 40 - -/* - * In a script change name() and s:name() to K_SNR 123_name(). - * Change 123_name() to K_SNR 123_name(). - * Use "fname_buf[FLEN_FIXED + 1]" when it fits, otherwise allocate memory - * (slow). - */ - static char_u * -fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error) -{ - int llen; - char_u *fname; - int i; - - llen = eval_fname_script(name); - if (llen > 0) - { - fname_buf[0] = K_SPECIAL; - fname_buf[1] = KS_EXTRA; - fname_buf[2] = (int)KE_SNR; - i = 3; - if (eval_fname_sid(name)) /* "" or "s:" */ - { - if (current_SID <= 0) - *error = ERROR_SCRIPT; - else - { - sprintf((char *)fname_buf + 3, "%ld_", (long)current_SID); - i = (int)STRLEN(fname_buf); - } - } - if (i + STRLEN(name + llen) < FLEN_FIXED) - { - STRCPY(fname_buf + i, name + llen); - fname = fname_buf; - } - else - { - fname = alloc((unsigned)(i + STRLEN(name + llen) + 1)); - if (fname == NULL) - *error = ERROR_OTHER; - else - { - *tofree = fname; - mch_memmove(fname, fname_buf, (size_t)i); - STRCPY(fname + i, name + llen); - } - } - } - else - fname = name; - return fname; -} - -/* - * Call a function with its resolved parameters - * Return FAIL when the function can't be called, OK otherwise. - * Also returns OK when an error was encountered while executing the function. - */ int -call_func( - char_u *funcname, /* name of the function */ - int len, /* length of "name" */ - typval_T *rettv, /* return value goes here */ - int argcount_in, /* number of "argvars" */ - typval_T *argvars_in, /* vars for arguments, must have "argcount" - PLUS ONE elements! */ - linenr_T firstline, /* first line of range */ - linenr_T lastline, /* last line of range */ - int *doesrange, /* return: function handled range */ - int evaluate, - partial_T *partial, /* optional, can be NULL */ - dict_T *selfdict_in) /* Dictionary for "self" */ +call_internal_func( + char_u *name, + int argcount, + typval_T *argvars, + typval_T *rettv) { - int ret = FAIL; - int error = ERROR_NONE; - int i; - ufunc_T *fp; - char_u fname_buf[FLEN_FIXED + 1]; - char_u *tofree = NULL; - char_u *fname; - char_u *name; - int argcount = argcount_in; - typval_T *argvars = argvars_in; - dict_T *selfdict = selfdict_in; - typval_T argv[MAX_FUNC_ARGS + 1]; /* used when "partial" is not NULL */ - int argv_clear = 0; + int i; - /* Make a copy of the name, if it comes from a funcref variable it could - * be changed or deleted in the called function. */ - name = vim_strnsave(funcname, len); - if (name == NULL) - return ret; - - fname = fname_trans_sid(name, fname_buf, &tofree, &error); - - *doesrange = FALSE; - - if (partial != NULL) - { - /* When the function has a partial with a dict and there is a dict - * argument, use the dict argument. That is backwards compatible. - * When the dict was bound explicitly use the one from the partial. */ - if (partial->pt_dict != NULL - && (selfdict_in == NULL || !partial->pt_auto)) - selfdict = partial->pt_dict; - if (error == ERROR_NONE && partial->pt_argc > 0) - { - for (argv_clear = 0; argv_clear < partial->pt_argc; ++argv_clear) - copy_tv(&partial->pt_argv[argv_clear], &argv[argv_clear]); - for (i = 0; i < argcount_in; ++i) - argv[i + argv_clear] = argvars_in[i]; - argvars = argv; - argcount = partial->pt_argc + argcount_in; - } - } - - - /* execute the function if no errors detected and executing */ - if (evaluate && error == ERROR_NONE) - { - char_u *rfname = fname; - - /* Ignore "g:" before a function name. */ - if (fname[0] == 'g' && fname[1] == ':') - rfname = fname + 2; - - rettv->v_type = VAR_NUMBER; /* default rettv is number zero */ - rettv->vval.v_number = 0; - error = ERROR_UNKNOWN; - - if (!builtin_function(rfname, -1)) - { - /* - * User defined function. - */ - fp = find_func(rfname); - -#ifdef FEAT_AUTOCMD - /* Trigger FuncUndefined event, may load the function. */ - if (fp == NULL - && apply_autocmds(EVENT_FUNCUNDEFINED, - rfname, rfname, TRUE, NULL) - && !aborting()) - { - /* executed an autocommand, search for the function again */ - fp = find_func(rfname); - } -#endif - /* Try loading a package. */ - if (fp == NULL && script_autoload(rfname, TRUE) && !aborting()) - { - /* loaded a package, search for the function again */ - fp = find_func(rfname); - } - - if (fp != NULL) - { - if (fp->uf_flags & FC_RANGE) - *doesrange = TRUE; - if (argcount < fp->uf_args.ga_len) - error = ERROR_TOOFEW; - else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len) - error = ERROR_TOOMANY; - else if ((fp->uf_flags & FC_DICT) && selfdict == NULL) - error = ERROR_DICT; - else - { - int did_save_redo = FALSE; - - /* - * Call the user function. - * Save and restore search patterns, script variables and - * redo buffer. - */ - save_search_patterns(); -#ifdef FEAT_INS_EXPAND - if (!ins_compl_active()) -#endif - { - saveRedobuff(); - did_save_redo = TRUE; - } - ++fp->uf_calls; - call_user_func(fp, argcount, argvars, rettv, - firstline, lastline, - (fp->uf_flags & FC_DICT) ? selfdict : NULL); - if (--fp->uf_calls <= 0 && (isdigit(*fp->uf_name) - || STRNCMP(fp->uf_name, "", 8) == 0) - && fp->uf_refcount <= 0) - /* Function was unreferenced while being used, free it - * now. */ - func_free(fp); - if (did_save_redo) - restoreRedobuff(); - restore_search_patterns(); - error = ERROR_NONE; - } - } - } - else - { - /* - * Find the function name in the table, call its implementation. - */ - i = find_internal_func(fname); - if (i >= 0) - { - if (argcount < functions[i].f_min_argc) - error = ERROR_TOOFEW; - else if (argcount > functions[i].f_max_argc) - error = ERROR_TOOMANY; - else - { - argvars[argcount].v_type = VAR_UNKNOWN; - functions[i].f_func(argvars, rettv); - error = ERROR_NONE; - } - } - } - /* - * The function call (or "FuncUndefined" autocommand sequence) might - * have been aborted by an error, an interrupt, or an explicitly thrown - * exception that has not been caught so far. This situation can be - * tested for by calling aborting(). For an error in an internal - * function or for the "E132" error in call_user_func(), however, the - * throw point at which the "force_abort" flag (temporarily reset by - * emsg()) is normally updated has not been reached yet. We need to - * update that flag first to make aborting() reliable. - */ - update_force_abort(); - } - if (error == ERROR_NONE) - ret = OK; - - /* - * Report an error unless the argument evaluation or function call has been - * cancelled due to an aborting error, an interrupt, or an exception. - */ - if (!aborting()) - { - switch (error) - { - case ERROR_UNKNOWN: - emsg_funcname(N_("E117: Unknown function: %s"), name); - break; - case ERROR_TOOMANY: - emsg_funcname(e_toomanyarg, name); - break; - case ERROR_TOOFEW: - emsg_funcname(N_("E119: Not enough arguments for function: %s"), - name); - break; - case ERROR_SCRIPT: - emsg_funcname(N_("E120: Using not in a script context: %s"), - name); - break; - case ERROR_DICT: - emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"), - name); - break; - } - } - - while (argv_clear > 0) - clear_tv(&argv[--argv_clear]); - vim_free(tofree); - vim_free(name); - - return ret; -} - -/* - * Give an error message with a function name. Handle things. - * "ermsg" is to be passed without translation, use N_() instead of _(). - */ - static void -emsg_funcname(char *ermsg, char_u *name) -{ - char_u *p; - - if (*name == K_SPECIAL) - p = concat_str((char_u *)"", name + 3); - else - p = name; - EMSG2(_(ermsg), p); - if (p != name) - vim_free(p); + i = find_internal_func(name); + if (i < 0) + return ERROR_UNKNOWN; + if (argcount < functions[i].f_min_argc) + return ERROR_TOOFEW; + if (argcount > functions[i].f_max_argc) + return ERROR_TOOMANY; + argvars[argcount].v_type = VAR_UNKNOWN; + functions[i].f_func(argvars, rettv); + return ERROR_NONE; } /* @@ -9019,46 +7915,6 @@ f_byteidxcomp(typval_T *argvars, typval_T *rettv) byteidx(argvars, rettv, TRUE); } - int -func_call( - char_u *name, - typval_T *args, - partial_T *partial, - dict_T *selfdict, - typval_T *rettv) -{ - listitem_T *item; - typval_T argv[MAX_FUNC_ARGS + 1]; - int argc = 0; - int dummy; - int r = 0; - - for (item = args->vval.v_list->lv_first; item != NULL; - item = item->li_next) - { - if (argc == MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) - { - EMSG(_("E699: Too many arguments")); - break; - } - /* Make a copy of each argument. This is needed to be able to set - * v_lock to VAR_FIXED in the copy without changing the original list. - */ - copy_tv(&item->li_tv, &argv[argc++]); - } - - if (item == NULL) - r = call_func(name, (int)STRLEN(name), rettv, argc, argv, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &dummy, TRUE, partial, selfdict); - - /* Free the arguments. */ - while (argc > 0) - clear_tv(&argv[--argc]); - - return r; -} - /* * "call(func, arglist [, dict])" function */ @@ -20638,7 +19494,7 @@ get_env_len(char_u **arg) * "arg" is advanced to the first non-white character after the name. * Return 0 if something is wrong. */ - static int + int get_id_len(char_u **arg) { char_u *p; @@ -20746,7 +19602,7 @@ get_name_len( * Return a pointer to just after the name. Equal to "arg" if there is no * valid name. */ - static char_u * + char_u * find_name_end( char_u *arg, char_u **expr_start, @@ -20900,7 +19756,7 @@ make_expanded_name( * Return TRUE if character "c" can be used in a variable or function name. * Does not include '{' or '}' for magic braces. */ - static int + int eval_isnamec(int c) { return (ASCII_ISALNUM(c) || c == '_' || c == ':' || c == AUTOLOAD_CHAR); @@ -20910,7 +19766,7 @@ eval_isnamec(int c) * Return TRUE if character "c" can be used as the first character in a * variable or function name (excluding '{' and '}'). */ - static int + int eval_isnamec1(int c) { return (ASCII_ISALPHA(c) || c == '_'); @@ -21238,7 +20094,7 @@ get_var_tv( * Also handle function call with Funcref variable: func(expr) * Can all be combined: dict.func(expr)[idx]['func'](expr) */ - static int + int handle_subscript( char_u **arg, typval_T *rettv, @@ -21327,66 +20183,7 @@ handle_subscript( || (rettv->v_type == VAR_PARTIAL && (rettv->vval.v_partial->pt_auto || rettv->vval.v_partial->pt_dict == NULL)))) - { - char_u *fname = rettv->v_type == VAR_FUNC ? rettv->vval.v_string - : rettv->vval.v_partial->pt_name; - char_u *tofree = NULL; - ufunc_T *fp; - char_u fname_buf[FLEN_FIXED + 1]; - int error; - - /* Translate "s:func" to the stored function name. */ - fname = fname_trans_sid(fname, fname_buf, &tofree, &error); - fp = find_func(fname); - vim_free(tofree); - - if (fp != NULL && (fp->uf_flags & FC_DICT)) - { - partial_T *pt = (partial_T *)alloc_clear(sizeof(partial_T)); - - if (pt != NULL) - { - pt->pt_refcount = 1; - pt->pt_dict = selfdict; - pt->pt_auto = TRUE; - selfdict = NULL; - if (rettv->v_type == VAR_FUNC) - { - /* Just a function: Take over the function name and use - * selfdict. */ - pt->pt_name = rettv->vval.v_string; - } - else - { - partial_T *ret_pt = rettv->vval.v_partial; - int i; - - /* Partial: copy the function name, use selfdict and copy - * args. Can't take over name or args, the partial might - * be referenced elsewhere. */ - pt->pt_name = vim_strsave(ret_pt->pt_name); - func_ref(pt->pt_name); - if (ret_pt->pt_argc > 0) - { - pt->pt_argv = (typval_T *)alloc( - sizeof(typval_T) * ret_pt->pt_argc); - if (pt->pt_argv == NULL) - /* out of memory: drop the arguments */ - pt->pt_argc = 0; - else - { - pt->pt_argc = ret_pt->pt_argc; - for (i = 0; i < pt->pt_argc; i++) - copy_tv(&ret_pt->pt_argv[i], &pt->pt_argv[i]); - } - } - partial_unref(ret_pt); - } - rettv->v_type = VAR_PARTIAL; - rettv->vval.v_partial = pt; - } - } - } + selfdict = make_partial(selfdict, rettv); dict_unref(selfdict); return ret; @@ -21809,7 +20606,7 @@ get_tv_string_buf_chk(typval_T *varp, char_u *buf) * When "htp" is not NULL we are writing to the variable, set "htp" to the * hashtab_T used. */ - static dictitem_T * + dictitem_T * find_var(char_u *name, hashtab_T **htp, int no_autoload) { char_u *varname; @@ -21849,10 +20646,8 @@ find_var_in_ht( #ifdef FEAT_WINDOWS case 't': return &curtab->tp_winvar; #endif - case 'l': return current_funccal == NULL - ? NULL : ¤t_funccal->l_vars_var; - case 'a': return current_funccal == NULL - ? NULL : ¤t_funccal->l_avars_var; + case 'l': return get_funccal_local_var(); + case 'a': return get_funccal_args_var(); } return NULL; } @@ -21887,6 +20682,7 @@ find_var_in_ht( find_var_ht(char_u *name, char_u **varname) { hashitem_T *hi; + hashtab_T *ht; if (name[0] == NUL) return NULL; @@ -21902,9 +20698,10 @@ find_var_ht(char_u *name, char_u **varname) if (!HASHITEM_EMPTY(hi)) return &compat_hashtab; - if (current_funccal == NULL) + ht = get_funccal_local_ht(); + if (ht == NULL) return &globvarht; /* global variable */ - return &get_funccal()->l_vars.dv_hashtab; /* l: variable */ + return ht; /* local variable */ } *varname = name + 2; if (*name == 'g') /* global variable */ @@ -21924,42 +20721,16 @@ find_var_ht(char_u *name, char_u **varname) #endif if (*name == 'v') /* v: variable */ return &vimvarht; - if (*name == 'a' && current_funccal != NULL) /* function argument */ - return &get_funccal()->l_avars.dv_hashtab; - if (*name == 'l' && current_funccal != NULL) /* local function variable */ - return &get_funccal()->l_vars.dv_hashtab; + if (*name == 'a') /* a: function argument */ + return get_funccal_args_ht(); + if (*name == 'l') /* l: local function variable */ + return get_funccal_local_ht(); if (*name == 's' /* script variable */ && current_SID > 0 && current_SID <= ga_scripts.ga_len) return &SCRIPT_VARS(current_SID); return NULL; } -/* - * Get function call environment based on bactrace debug level - */ - static funccall_T * -get_funccal(void) -{ - int i; - funccall_T *funccal; - funccall_T *temp_funccal; - - funccal = current_funccal; - if (debug_backtrace_level > 0) - { - for (i = 0; i < debug_backtrace_level; i++) - { - temp_funccal = funccal->caller; - if (temp_funccal) - funccal = temp_funccal; - else - /* backtrace level overflow. reset to max */ - debug_backtrace_level = i; - } - } - return funccal; -} - /* * Get the string value of a (global/local) variable. * Note: see get_tv_string() for how long the pointer remains valid. @@ -22056,7 +20827,7 @@ vars_clear(hashtab_T *ht) /* * Like vars_clear(), but only free the value if "free_val" is TRUE. */ - static void + void vars_clear_ext(hashtab_T *ht, int free_val) { int todo; @@ -22782,1196 +21553,33 @@ find_option_end(char_u **arg, int *opt_flags) } /* - * ":function" + * Return the autoload script name for a function or variable name. + * Returns NULL when out of memory. */ - void -ex_function(exarg_T *eap) -{ - char_u *theline; - int j; - int c; - int saved_did_emsg; - int saved_wait_return = need_wait_return; - char_u *name = NULL; - char_u *p; - char_u *arg; - char_u *line_arg = NULL; - garray_T newargs; - garray_T newlines; - int varargs = FALSE; - int flags = 0; - ufunc_T *fp; - int indent; - int nesting; - char_u *skip_until = NULL; - dictitem_T *v; - funcdict_T fudi; - static int func_nr = 0; /* number for nameless function */ - int paren; - hashtab_T *ht; - int todo; - hashitem_T *hi; - int sourcing_lnum_off; - - /* - * ":function" without argument: list functions. - */ - if (ends_excmd(*eap->arg)) - { - if (!eap->skip) - { - todo = (int)func_hashtab.ht_used; - for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) - { - if (!HASHITEM_EMPTY(hi)) - { - --todo; - fp = HI2UF(hi); - if (!isdigit(*fp->uf_name)) - list_func_head(fp, FALSE); - } - } - } - eap->nextcmd = check_nextcmd(eap->arg); - return; - } - - /* - * ":function /pat": list functions matching pattern. - */ - if (*eap->arg == '/') - { - p = skip_regexp(eap->arg + 1, '/', TRUE, NULL); - if (!eap->skip) - { - regmatch_T regmatch; - - c = *p; - *p = NUL; - regmatch.regprog = vim_regcomp(eap->arg + 1, RE_MAGIC); - *p = c; - if (regmatch.regprog != NULL) - { - regmatch.rm_ic = p_ic; - - todo = (int)func_hashtab.ht_used; - for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) - { - if (!HASHITEM_EMPTY(hi)) - { - --todo; - fp = HI2UF(hi); - if (!isdigit(*fp->uf_name) - && vim_regexec(®match, fp->uf_name, 0)) - list_func_head(fp, FALSE); - } - } - vim_regfree(regmatch.regprog); - } - } - if (*p == '/') - ++p; - eap->nextcmd = check_nextcmd(p); - return; - } - - /* - * Get the function name. There are these situations: - * func normal function name - * "name" == func, "fudi.fd_dict" == NULL - * dict.func new dictionary entry - * "name" == NULL, "fudi.fd_dict" set, - * "fudi.fd_di" == NULL, "fudi.fd_newkey" == func - * dict.func existing dict entry with a Funcref - * "name" == func, "fudi.fd_dict" set, - * "fudi.fd_di" set, "fudi.fd_newkey" == NULL - * dict.func existing dict entry that's not a Funcref - * "name" == NULL, "fudi.fd_dict" set, - * "fudi.fd_di" set, "fudi.fd_newkey" == NULL - * s:func script-local function name - * g:func global function name, same as "func" - */ - p = eap->arg; - name = trans_function_name(&p, eap->skip, 0, &fudi, NULL); - paren = (vim_strchr(p, '(') != NULL); - if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip) - { - /* - * Return on an invalid expression in braces, unless the expression - * evaluation has been cancelled due to an aborting error, an - * interrupt, or an exception. - */ - if (!aborting()) - { - if (!eap->skip && fudi.fd_newkey != NULL) - EMSG2(_(e_dictkey), fudi.fd_newkey); - vim_free(fudi.fd_newkey); - return; - } - else - eap->skip = TRUE; - } - - /* An error in a function call during evaluation of an expression in magic - * braces should not cause the function not to be defined. */ - saved_did_emsg = did_emsg; - did_emsg = FALSE; - - /* - * ":function func" with only function name: list function. - */ - if (!paren) - { - if (!ends_excmd(*skipwhite(p))) - { - EMSG(_(e_trailing)); - goto ret_free; - } - eap->nextcmd = check_nextcmd(p); - if (eap->nextcmd != NULL) - *p = NUL; - if (!eap->skip && !got_int) - { - fp = find_func(name); - if (fp != NULL) - { - list_func_head(fp, TRUE); - for (j = 0; j < fp->uf_lines.ga_len && !got_int; ++j) - { - if (FUNCLINE(fp, j) == NULL) - continue; - msg_putchar('\n'); - msg_outnum((long)(j + 1)); - if (j < 9) - msg_putchar(' '); - if (j < 99) - msg_putchar(' '); - msg_prt_line(FUNCLINE(fp, j), FALSE); - out_flush(); /* show a line at a time */ - ui_breakcheck(); - } - if (!got_int) - { - msg_putchar('\n'); - msg_puts((char_u *)" endfunction"); - } - } - else - emsg_funcname(N_("E123: Undefined function: %s"), name); - } - goto ret_free; - } - - /* - * ":function name(arg1, arg2)" Define function. - */ - p = skipwhite(p); - if (*p != '(') - { - if (!eap->skip) - { - EMSG2(_("E124: Missing '(': %s"), eap->arg); - goto ret_free; - } - /* attempt to continue by skipping some text */ - if (vim_strchr(p, '(') != NULL) - p = vim_strchr(p, '('); - } - p = skipwhite(p + 1); - - ga_init2(&newlines, (int)sizeof(char_u *), 3); - - if (!eap->skip) - { - /* Check the name of the function. Unless it's a dictionary function - * (that we are overwriting). */ - if (name != NULL) - arg = name; - else - arg = fudi.fd_newkey; - if (arg != NULL && (fudi.fd_di == NULL - || (fudi.fd_di->di_tv.v_type != VAR_FUNC - && fudi.fd_di->di_tv.v_type != VAR_PARTIAL))) - { - if (*arg == K_SPECIAL) - j = 3; - else - j = 0; - while (arg[j] != NUL && (j == 0 ? eval_isnamec1(arg[j]) - : eval_isnamec(arg[j]))) - ++j; - if (arg[j] != NUL) - emsg_funcname((char *)e_invarg2, arg); - } - /* Disallow using the g: dict. */ - if (fudi.fd_dict != NULL && fudi.fd_dict->dv_scope == VAR_DEF_SCOPE) - EMSG(_("E862: Cannot use g: here")); - } - - if (get_function_args(&p, ')', &newargs, &varargs, eap->skip) == FAIL) - goto errret_2; - - /* find extra arguments "range", "dict" and "abort" */ - for (;;) - { - p = skipwhite(p); - if (STRNCMP(p, "range", 5) == 0) - { - flags |= FC_RANGE; - p += 5; - } - else if (STRNCMP(p, "dict", 4) == 0) - { - flags |= FC_DICT; - p += 4; - } - else if (STRNCMP(p, "abort", 5) == 0) - { - flags |= FC_ABORT; - p += 5; - } - else - break; - } - - /* When there is a line break use what follows for the function body. - * Makes 'exe "func Test()\n...\nendfunc"' work. */ - if (*p == '\n') - line_arg = p + 1; - else if (*p != NUL && *p != '"' && !eap->skip && !did_emsg) - EMSG(_(e_trailing)); - - /* - * Read the body of the function, until ":endfunction" is found. - */ - if (KeyTyped) - { - /* Check if the function already exists, don't let the user type the - * whole function before telling him it doesn't work! For a script we - * need to skip the body to be able to find what follows. */ - if (!eap->skip && !eap->forceit) - { - if (fudi.fd_dict != NULL && fudi.fd_newkey == NULL) - EMSG(_(e_funcdict)); - else if (name != NULL && find_func(name) != NULL) - emsg_funcname(e_funcexts, name); - } - - if (!eap->skip && did_emsg) - goto erret; - - msg_putchar('\n'); /* don't overwrite the function name */ - cmdline_row = msg_row; - } - - indent = 2; - nesting = 0; - for (;;) - { - if (KeyTyped) - { - msg_scroll = TRUE; - saved_wait_return = FALSE; - } - need_wait_return = FALSE; - sourcing_lnum_off = sourcing_lnum; - - if (line_arg != NULL) - { - /* Use eap->arg, split up in parts by line breaks. */ - theline = line_arg; - p = vim_strchr(theline, '\n'); - if (p == NULL) - line_arg += STRLEN(line_arg); - else - { - *p = NUL; - line_arg = p + 1; - } - } - else if (eap->getline == NULL) - theline = getcmdline(':', 0L, indent); - else - theline = eap->getline(':', eap->cookie, indent); - if (KeyTyped) - lines_left = Rows - 1; - if (theline == NULL) - { - EMSG(_("E126: Missing :endfunction")); - goto erret; - } - - /* Detect line continuation: sourcing_lnum increased more than one. */ - if (sourcing_lnum > sourcing_lnum_off + 1) - sourcing_lnum_off = sourcing_lnum - sourcing_lnum_off - 1; - else - sourcing_lnum_off = 0; - - if (skip_until != NULL) - { - /* between ":append" and "." and between ":python < 2 && STRNCMP(p, "end", 3) == 0) - indent -= 2; - else if (STRNCMP(p, "if", 2) == 0 - || STRNCMP(p, "wh", 2) == 0 - || STRNCMP(p, "for", 3) == 0 - || STRNCMP(p, "try", 3) == 0) - indent += 2; - - /* Check for defining a function inside this function. */ - if (checkforcmd(&p, "function", 2)) - { - if (*p == '!') - p = skipwhite(p + 1); - p += eval_fname_script(p); - vim_free(trans_function_name(&p, TRUE, 0, NULL, NULL)); - if (*skipwhite(p) == '(') - { - ++nesting; - indent += 2; - } - } - - /* Check for ":append" or ":insert". */ - p = skip_range(p, NULL); - if ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p')) - || (p[0] == 'i' - && (!ASCII_ISALPHA(p[1]) || (p[1] == 'n' - && (!ASCII_ISALPHA(p[2]) || (p[2] == 's')))))) - skip_until = vim_strsave((char_u *)"."); - - /* Check for ":python < 0) - ((char_u **)(newlines.ga_data))[newlines.ga_len++] = NULL; - - /* Check for end of eap->arg. */ - if (line_arg != NULL && *line_arg == NUL) - line_arg = NULL; - } - - /* Don't define the function when skipping commands or when an error was - * detected. */ - if (eap->skip || did_emsg) - goto erret; - - /* - * If there are no errors, add the function - */ - if (fudi.fd_dict == NULL) - { - v = find_var(name, &ht, FALSE); - if (v != NULL && v->di_tv.v_type == VAR_FUNC) - { - emsg_funcname(N_("E707: Function name conflicts with variable: %s"), - name); - goto erret; - } - - fp = find_func(name); - if (fp != NULL) - { - if (!eap->forceit) - { - emsg_funcname(e_funcexts, name); - goto erret; - } - if (fp->uf_calls > 0) - { - emsg_funcname(N_("E127: Cannot redefine function %s: It is in use"), - name); - goto erret; - } - /* redefine existing function */ - ga_clear_strings(&(fp->uf_args)); - ga_clear_strings(&(fp->uf_lines)); - vim_free(name); - name = NULL; - } - } - else - { - char numbuf[20]; - - fp = NULL; - if (fudi.fd_newkey == NULL && !eap->forceit) - { - EMSG(_(e_funcdict)); - goto erret; - } - if (fudi.fd_di == NULL) - { - /* Can't add a function to a locked dictionary */ - if (tv_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE)) - goto erret; - } - /* Can't change an existing function if it is locked */ - else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE)) - goto erret; - - /* Give the function a sequential number. Can only be used with a - * Funcref! */ - vim_free(name); - sprintf(numbuf, "%d", ++func_nr); - name = vim_strsave((char_u *)numbuf); - if (name == NULL) - goto erret; - } - - if (fp == NULL) - { - if (fudi.fd_dict == NULL && vim_strchr(name, AUTOLOAD_CHAR) != NULL) - { - int slen, plen; - char_u *scriptname; - - /* Check that the autoload name matches the script name. */ - j = FAIL; - if (sourcing_name != NULL) - { - scriptname = autoload_name(name); - if (scriptname != NULL) - { - p = vim_strchr(scriptname, '/'); - plen = (int)STRLEN(p); - slen = (int)STRLEN(sourcing_name); - if (slen > plen && fnamecmp(p, - sourcing_name + slen - plen) == 0) - j = OK; - vim_free(scriptname); - } - } - if (j == FAIL) - { - EMSG2(_("E746: Function name does not match script file name: %s"), name); - goto erret; - } - } - - fp = (ufunc_T *)alloc((unsigned)(sizeof(ufunc_T) + STRLEN(name))); - if (fp == NULL) - goto erret; - - if (fudi.fd_dict != NULL) - { - if (fudi.fd_di == NULL) - { - /* add new dict entry */ - fudi.fd_di = dictitem_alloc(fudi.fd_newkey); - if (fudi.fd_di == NULL) - { - vim_free(fp); - goto erret; - } - if (dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) - { - vim_free(fudi.fd_di); - vim_free(fp); - goto erret; - } - } - else - /* overwrite existing dict entry */ - clear_tv(&fudi.fd_di->di_tv); - fudi.fd_di->di_tv.v_type = VAR_FUNC; - fudi.fd_di->di_tv.v_lock = 0; - fudi.fd_di->di_tv.vval.v_string = vim_strsave(name); - fp->uf_refcount = 1; - - /* behave like "dict" was used */ - flags |= FC_DICT; - } - - /* insert the new function in the function list */ - STRCPY(fp->uf_name, name); - if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) - { - vim_free(fp); - goto erret; - } - } - fp->uf_args = newargs; - fp->uf_lines = newlines; -#ifdef FEAT_PROFILE - fp->uf_tml_count = NULL; - fp->uf_tml_total = NULL; - fp->uf_tml_self = NULL; - fp->uf_profiling = FALSE; - if (prof_def_func()) - func_do_profile(fp); -#endif - fp->uf_varargs = varargs; - fp->uf_flags = flags; - fp->uf_calls = 0; - fp->uf_script_ID = current_SID; - goto ret_free; - -erret: - ga_clear_strings(&newargs); -errret_2: - ga_clear_strings(&newlines); -ret_free: - vim_free(skip_until); - vim_free(fudi.fd_newkey); - vim_free(name); - did_emsg |= saved_did_emsg; - need_wait_return |= saved_wait_return; -} - -/* - * Get a function name, translating "" and "". - * Also handles a Funcref in a List or Dictionary. - * Returns the function name in allocated memory, or NULL for failure. - * flags: - * TFN_INT: internal function name OK - * TFN_QUIET: be quiet - * TFN_NO_AUTOLOAD: do not use script autoloading - * Advances "pp" to just after the function name (if no error). - */ - static char_u * -trans_function_name( - char_u **pp, - int skip, /* only find the end, don't evaluate */ - int flags, - funcdict_T *fdp, /* return: info about dictionary used */ - partial_T **partial) /* return: partial of a FuncRef */ -{ - char_u *name = NULL; - char_u *start; - char_u *end; - int lead; - char_u sid_buf[20]; - int len; - lval_T lv; - - if (fdp != NULL) - vim_memset(fdp, 0, sizeof(funcdict_T)); - start = *pp; - - /* Check for hard coded : already translated function ID (from a user - * command). */ - if ((*pp)[0] == K_SPECIAL && (*pp)[1] == KS_EXTRA - && (*pp)[2] == (int)KE_SNR) - { - *pp += 3; - len = get_id_len(pp) + 3; - return vim_strnsave(start, len); - } - - /* A name starting with "" or "" is local to a script. But - * don't skip over "s:", get_lval() needs it for "s:dict.func". */ - lead = eval_fname_script(start); - if (lead > 2) - start += lead; - - /* Note that TFN_ flags use the same values as GLV_ flags. */ - end = get_lval(start, NULL, &lv, FALSE, skip, flags, - lead > 2 ? 0 : FNE_CHECK_START); - if (end == start) - { - if (!skip) - EMSG(_("E129: Function name required")); - goto theend; - } - if (end == NULL || (lv.ll_tv != NULL && (lead > 2 || lv.ll_range))) - { - /* - * Report an invalid expression in braces, unless the expression - * evaluation has been cancelled due to an aborting error, an - * interrupt, or an exception. - */ - if (!aborting()) - { - if (end != NULL) - EMSG2(_(e_invarg2), start); - } - else - *pp = find_name_end(start, NULL, NULL, FNE_INCL_BR); - goto theend; - } - - if (lv.ll_tv != NULL) - { - if (fdp != NULL) - { - fdp->fd_dict = lv.ll_dict; - fdp->fd_newkey = lv.ll_newkey; - lv.ll_newkey = NULL; - fdp->fd_di = lv.ll_di; - } - if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) - { - name = vim_strsave(lv.ll_tv->vval.v_string); - *pp = end; - } - else if (lv.ll_tv->v_type == VAR_PARTIAL - && lv.ll_tv->vval.v_partial != NULL) - { - name = vim_strsave(lv.ll_tv->vval.v_partial->pt_name); - *pp = end; - if (partial != NULL) - *partial = lv.ll_tv->vval.v_partial; - } - else - { - if (!skip && !(flags & TFN_QUIET) && (fdp == NULL - || lv.ll_dict == NULL || fdp->fd_newkey == NULL)) - EMSG(_(e_funcref)); - else - *pp = end; - name = NULL; - } - goto theend; - } - - if (lv.ll_name == NULL) - { - /* Error found, but continue after the function name. */ - *pp = end; - goto theend; - } - - /* Check if the name is a Funcref. If so, use the value. */ - if (lv.ll_exp_name != NULL) - { - len = (int)STRLEN(lv.ll_exp_name); - name = deref_func_name(lv.ll_exp_name, &len, partial, - flags & TFN_NO_AUTOLOAD); - if (name == lv.ll_exp_name) - name = NULL; - } - else - { - len = (int)(end - *pp); - name = deref_func_name(*pp, &len, partial, flags & TFN_NO_AUTOLOAD); - if (name == *pp) - name = NULL; - } - if (name != NULL) - { - name = vim_strsave(name); - *pp = end; - if (STRNCMP(name, "", 5) == 0) - { - /* Change "" to the byte sequence. */ - name[0] = K_SPECIAL; - name[1] = KS_EXTRA; - name[2] = (int)KE_SNR; - mch_memmove(name + 3, name + 5, STRLEN(name + 5) + 1); - } - goto theend; - } - - if (lv.ll_exp_name != NULL) - { - len = (int)STRLEN(lv.ll_exp_name); - if (lead <= 2 && lv.ll_name == lv.ll_exp_name - && STRNCMP(lv.ll_name, "s:", 2) == 0) - { - /* When there was "s:" already or the name expanded to get a - * leading "s:" then remove it. */ - lv.ll_name += 2; - len -= 2; - lead = 2; - } - } - else - { - /* skip over "s:" and "g:" */ - if (lead == 2 || (lv.ll_name[0] == 'g' && lv.ll_name[1] == ':')) - lv.ll_name += 2; - len = (int)(end - lv.ll_name); - } - - /* - * Copy the function name to allocated memory. - * Accept name() inside a script, translate into 123_name(). - * Accept 123_name() outside a script. - */ - if (skip) - lead = 0; /* do nothing */ - else if (lead > 0) - { - lead = 3; - if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name)) - || eval_fname_sid(*pp)) - { - /* It's "s:" or "" */ - if (current_SID <= 0) - { - EMSG(_(e_usingsid)); - goto theend; - } - sprintf((char *)sid_buf, "%ld_", (long)current_SID); - lead += (int)STRLEN(sid_buf); - } - } - else if (!(flags & TFN_INT) && builtin_function(lv.ll_name, len)) - { - EMSG2(_("E128: Function name must start with a capital or \"s:\": %s"), - start); - goto theend; - } - if (!skip && !(flags & TFN_QUIET)) - { - char_u *cp = vim_strchr(lv.ll_name, ':'); - - if (cp != NULL && cp < end) - { - EMSG2(_("E884: Function name cannot contain a colon: %s"), start); - goto theend; - } - } - - name = alloc((unsigned)(len + lead + 1)); - if (name != NULL) - { - if (lead > 0) - { - name[0] = K_SPECIAL; - name[1] = KS_EXTRA; - name[2] = (int)KE_SNR; - if (lead > 3) /* If it's "" */ - STRCPY(name + 3, sid_buf); - } - mch_memmove(name + lead, lv.ll_name, (size_t)len); - name[lead + len] = NUL; - } - *pp = end; - -theend: - clear_lval(&lv); - return name; -} - -/* - * Return 5 if "p" starts with "" or "" (ignoring case). - * Return 2 if "p" starts with "s:". - * Return 0 otherwise. - */ - static int -eval_fname_script(char_u *p) -{ - /* Use MB_STRICMP() because in Turkish comparing the "I" may not work with - * the standard library function. */ - if (p[0] == '<' && (MB_STRNICMP(p + 1, "SID>", 4) == 0 - || MB_STRNICMP(p + 1, "SNR>", 4) == 0)) - return 5; - if (p[0] == 's' && p[1] == ':') - return 2; - return 0; -} - -/* - * Return TRUE if "p" starts with "" or "s:". - * Only works if eval_fname_script() returned non-zero for "p"! - */ - static int -eval_fname_sid(char_u *p) -{ - return (*p == 's' || TOUPPER_ASC(p[2]) == 'I'); -} - -/* - * List the head of the function: "name(arg1, arg2)". - */ - static void -list_func_head(ufunc_T *fp, int indent) -{ - int j; - - msg_start(); - if (indent) - MSG_PUTS(" "); - MSG_PUTS("function "); - if (fp->uf_name[0] == K_SPECIAL) - { - MSG_PUTS_ATTR("", hl_attr(HLF_8)); - msg_puts(fp->uf_name + 3); - } - else - msg_puts(fp->uf_name); - msg_putchar('('); - for (j = 0; j < fp->uf_args.ga_len; ++j) - { - if (j) - MSG_PUTS(", "); - msg_puts(FUNCARG(fp, j)); - } - if (fp->uf_varargs) - { - if (j) - MSG_PUTS(", "); - MSG_PUTS("..."); - } - msg_putchar(')'); - if (fp->uf_flags & FC_ABORT) - MSG_PUTS(" abort"); - if (fp->uf_flags & FC_RANGE) - MSG_PUTS(" range"); - if (fp->uf_flags & FC_DICT) - MSG_PUTS(" dict"); - msg_clr_eos(); - if (p_verbose > 0) - last_set_msg(fp->uf_script_ID); -} - -/* - * Find a function by name, return pointer to it in ufuncs. - * Return NULL for unknown function. - */ - static ufunc_T * -find_func(char_u *name) -{ - hashitem_T *hi; - - hi = hash_find(&func_hashtab, name); - if (!HASHITEM_EMPTY(hi)) - return HI2UF(hi); - return NULL; -} - -#if defined(EXITFREE) || defined(PROTO) - void -free_all_functions(void) -{ - hashitem_T *hi; - - /* Need to start all over every time, because func_free() may change the - * hash table. */ - while (func_hashtab.ht_used > 0) - for (hi = func_hashtab.ht_array; ; ++hi) - if (!HASHITEM_EMPTY(hi)) - { - func_free(HI2UF(hi)); - break; - } -} -#endif - - int -translated_function_exists(char_u *name) -{ - if (builtin_function(name, -1)) - return find_internal_func(name) >= 0; - return find_func(name) != NULL; -} - -/* - * Return TRUE if a function "name" exists. - */ - static int -function_exists(char_u *name) -{ - char_u *nm = name; - char_u *p; - int n = FALSE; - - p = trans_function_name(&nm, FALSE, TFN_INT|TFN_QUIET|TFN_NO_AUTOLOAD, - NULL, NULL); - nm = skipwhite(nm); - - /* Only accept "funcname", "funcname ", "funcname (..." and - * "funcname(...", not "funcname!...". */ - if (p != NULL && (*nm == NUL || *nm == '(')) - n = translated_function_exists(p); - vim_free(p); - return n; -} - char_u * -get_expanded_name(char_u *name, int check) +autoload_name(char_u *name) { - char_u *nm = name; char_u *p; + char_u *scriptname; - p = trans_function_name(&nm, FALSE, TFN_INT|TFN_QUIET, NULL, NULL); - - if (p != NULL && *nm == NUL) - if (!check || translated_function_exists(p)) - return p; - - vim_free(p); - return NULL; -} - -/* - * Return TRUE if "name" looks like a builtin function name: starts with a - * lower case letter and doesn't contain AUTOLOAD_CHAR. - * "len" is the length of "name", or -1 for NUL terminated. - */ - static int -builtin_function(char_u *name, int len) -{ - char_u *p; - - if (!ASCII_ISLOWER(name[0])) + /* Get the script file name: replace '#' with '/', append ".vim". */ + scriptname = alloc((unsigned)(STRLEN(name) + 14)); + if (scriptname == NULL) return FALSE; - p = vim_strchr(name, AUTOLOAD_CHAR); - return p == NULL || (len > 0 && p > name + len); + STRCPY(scriptname, "autoload/"); + STRCAT(scriptname, name); + *vim_strrchr(scriptname, AUTOLOAD_CHAR) = NUL; + STRCAT(scriptname, ".vim"); + while ((p = vim_strchr(scriptname, AUTOLOAD_CHAR)) != NULL) + *p = '/'; + return scriptname; } -#if defined(FEAT_PROFILE) || defined(PROTO) -/* - * Start profiling function "fp". - */ - static void -func_do_profile(ufunc_T *fp) -{ - int len = fp->uf_lines.ga_len; - - if (len == 0) - len = 1; /* avoid getting error for allocating zero bytes */ - fp->uf_tm_count = 0; - profile_zero(&fp->uf_tm_self); - profile_zero(&fp->uf_tm_total); - if (fp->uf_tml_count == NULL) - fp->uf_tml_count = (int *)alloc_clear((unsigned) (sizeof(int) * len)); - if (fp->uf_tml_total == NULL) - fp->uf_tml_total = (proftime_T *)alloc_clear((unsigned) - (sizeof(proftime_T) * len)); - if (fp->uf_tml_self == NULL) - fp->uf_tml_self = (proftime_T *)alloc_clear((unsigned) - (sizeof(proftime_T) * len)); - fp->uf_tml_idx = -1; - if (fp->uf_tml_count == NULL || fp->uf_tml_total == NULL - || fp->uf_tml_self == NULL) - return; /* out of memory */ - - fp->uf_profiling = TRUE; -} - -/* - * Dump the profiling results for all functions in file "fd". - */ - void -func_dump_profile(FILE *fd) -{ - hashitem_T *hi; - int todo; - ufunc_T *fp; - int i; - ufunc_T **sorttab; - int st_len = 0; - - todo = (int)func_hashtab.ht_used; - if (todo == 0) - return; /* nothing to dump */ - - sorttab = (ufunc_T **)alloc((unsigned)(sizeof(ufunc_T *) * todo)); - - for (hi = func_hashtab.ht_array; todo > 0; ++hi) - { - if (!HASHITEM_EMPTY(hi)) - { - --todo; - fp = HI2UF(hi); - if (fp->uf_profiling) - { - if (sorttab != NULL) - sorttab[st_len++] = fp; - - if (fp->uf_name[0] == K_SPECIAL) - fprintf(fd, "FUNCTION %s()\n", fp->uf_name + 3); - else - fprintf(fd, "FUNCTION %s()\n", fp->uf_name); - if (fp->uf_tm_count == 1) - fprintf(fd, "Called 1 time\n"); - else - fprintf(fd, "Called %d times\n", fp->uf_tm_count); - fprintf(fd, "Total time: %s\n", profile_msg(&fp->uf_tm_total)); - fprintf(fd, " Self time: %s\n", profile_msg(&fp->uf_tm_self)); - fprintf(fd, "\n"); - fprintf(fd, "count total (s) self (s)\n"); - - for (i = 0; i < fp->uf_lines.ga_len; ++i) - { - if (FUNCLINE(fp, i) == NULL) - continue; - prof_func_line(fd, fp->uf_tml_count[i], - &fp->uf_tml_total[i], &fp->uf_tml_self[i], TRUE); - fprintf(fd, "%s\n", FUNCLINE(fp, i)); - } - fprintf(fd, "\n"); - } - } - } - - if (sorttab != NULL && st_len > 0) - { - qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *), - prof_total_cmp); - prof_sort_list(fd, sorttab, st_len, "TOTAL", FALSE); - qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *), - prof_self_cmp); - prof_sort_list(fd, sorttab, st_len, "SELF", TRUE); - } - - vim_free(sorttab); -} - - static void -prof_sort_list( - FILE *fd, - ufunc_T **sorttab, - int st_len, - char *title, - int prefer_self) /* when equal print only self time */ -{ - int i; - ufunc_T *fp; - - fprintf(fd, "FUNCTIONS SORTED ON %s TIME\n", title); - fprintf(fd, "count total (s) self (s) function\n"); - for (i = 0; i < 20 && i < st_len; ++i) - { - fp = sorttab[i]; - prof_func_line(fd, fp->uf_tm_count, &fp->uf_tm_total, &fp->uf_tm_self, - prefer_self); - if (fp->uf_name[0] == K_SPECIAL) - fprintf(fd, " %s()\n", fp->uf_name + 3); - else - fprintf(fd, " %s()\n", fp->uf_name); - } - fprintf(fd, "\n"); -} - -/* - * Print the count and times for one function or function line. - */ - static void -prof_func_line( - FILE *fd, - int count, - proftime_T *total, - proftime_T *self, - int prefer_self) /* when equal print only self time */ -{ - if (count > 0) - { - fprintf(fd, "%5d ", count); - if (prefer_self && profile_equal(total, self)) - fprintf(fd, " "); - else - fprintf(fd, "%s ", profile_msg(total)); - if (!prefer_self && profile_equal(total, self)) - fprintf(fd, " "); - else - fprintf(fd, "%s ", profile_msg(self)); - } - else - fprintf(fd, " "); -} - -/* - * Compare function for total time sorting. - */ - static int -#ifdef __BORLANDC__ -_RTLENTRYF -#endif -prof_total_cmp(const void *s1, const void *s2) -{ - ufunc_T *p1, *p2; - - p1 = *(ufunc_T **)s1; - p2 = *(ufunc_T **)s2; - return profile_cmp(&p1->uf_tm_total, &p2->uf_tm_total); -} - -/* - * Compare function for self time sorting. - */ - static int -#ifdef __BORLANDC__ -_RTLENTRYF -#endif -prof_self_cmp(const void *s1, const void *s2) -{ - ufunc_T *p1, *p2; - - p1 = *(ufunc_T **)s1; - p2 = *(ufunc_T **)s2; - return profile_cmp(&p1->uf_tm_self, &p2->uf_tm_self); -} - -#endif - /* * If "name" has a package name try autoloading the script for it. * Return TRUE if a package was loaded. */ - static int + int script_autoload( char_u *name, int reload) /* load script again when already loaded */ @@ -24013,1034 +21621,6 @@ script_autoload( return ret; } -/* - * Return the autoload script name for a function or variable name. - * Returns NULL when out of memory. - */ - static char_u * -autoload_name(char_u *name) -{ - char_u *p; - char_u *scriptname; - - /* Get the script file name: replace '#' with '/', append ".vim". */ - scriptname = alloc((unsigned)(STRLEN(name) + 14)); - if (scriptname == NULL) - return FALSE; - STRCPY(scriptname, "autoload/"); - STRCAT(scriptname, name); - *vim_strrchr(scriptname, AUTOLOAD_CHAR) = NUL; - STRCAT(scriptname, ".vim"); - while ((p = vim_strchr(scriptname, AUTOLOAD_CHAR)) != NULL) - *p = '/'; - return scriptname; -} - -#if defined(FEAT_CMDL_COMPL) || defined(PROTO) - -/* - * Function given to ExpandGeneric() to obtain the list of user defined - * function names. - */ - char_u * -get_user_func_name(expand_T *xp, int idx) -{ - static long_u done; - static hashitem_T *hi; - ufunc_T *fp; - - if (idx == 0) - { - done = 0; - hi = func_hashtab.ht_array; - } - if (done < func_hashtab.ht_used) - { - if (done++ > 0) - ++hi; - while (HASHITEM_EMPTY(hi)) - ++hi; - fp = HI2UF(hi); - - if (fp->uf_flags & FC_DICT) - return (char_u *)""; /* don't show dict functions */ - - if (STRLEN(fp->uf_name) + 4 >= IOSIZE) - return fp->uf_name; /* prevents overflow */ - - cat_func_name(IObuff, fp); - if (xp->xp_context != EXPAND_USER_FUNC) - { - STRCAT(IObuff, "("); - if (!fp->uf_varargs && fp->uf_args.ga_len == 0) - STRCAT(IObuff, ")"); - } - return IObuff; - } - return NULL; -} - -#endif /* FEAT_CMDL_COMPL */ - -/* - * Copy the function name of "fp" to buffer "buf". - * "buf" must be able to hold the function name plus three bytes. - * Takes care of script-local function names. - */ - static void -cat_func_name(char_u *buf, ufunc_T *fp) -{ - if (fp->uf_name[0] == K_SPECIAL) - { - STRCPY(buf, ""); - STRCAT(buf, fp->uf_name + 3); - } - else - STRCPY(buf, fp->uf_name); -} - -/* - * ":delfunction {name}" - */ - void -ex_delfunction(exarg_T *eap) -{ - ufunc_T *fp = NULL; - char_u *p; - char_u *name; - funcdict_T fudi; - - p = eap->arg; - name = trans_function_name(&p, eap->skip, 0, &fudi, NULL); - vim_free(fudi.fd_newkey); - if (name == NULL) - { - if (fudi.fd_dict != NULL && !eap->skip) - EMSG(_(e_funcref)); - return; - } - if (!ends_excmd(*skipwhite(p))) - { - vim_free(name); - EMSG(_(e_trailing)); - return; - } - eap->nextcmd = check_nextcmd(p); - if (eap->nextcmd != NULL) - *p = NUL; - - if (!eap->skip) - fp = find_func(name); - vim_free(name); - - if (!eap->skip) - { - if (fp == NULL) - { - EMSG2(_(e_nofunc), eap->arg); - return; - } - if (fp->uf_calls > 0) - { - EMSG2(_("E131: Cannot delete function %s: It is in use"), eap->arg); - return; - } - - if (fudi.fd_dict != NULL) - { - /* Delete the dict item that refers to the function, it will - * invoke func_unref() and possibly delete the function. */ - dictitem_remove(fudi.fd_dict, fudi.fd_di); - } - else - func_free(fp); - } -} - -/* - * Free a function and remove it from the list of functions. - */ - static void -func_free(ufunc_T *fp) -{ - hashitem_T *hi; - - /* clear this function */ - ga_clear_strings(&(fp->uf_args)); - ga_clear_strings(&(fp->uf_lines)); -#ifdef FEAT_PROFILE - vim_free(fp->uf_tml_count); - vim_free(fp->uf_tml_total); - vim_free(fp->uf_tml_self); -#endif - - /* remove the function from the function hashtable */ - hi = hash_find(&func_hashtab, UF2HIKEY(fp)); - if (HASHITEM_EMPTY(hi)) - EMSG2(_(e_intern2), "func_free()"); - else - hash_remove(&func_hashtab, hi); - - vim_free(fp); -} - -/* - * Unreference a Function: decrement the reference count and free it when it - * becomes zero. Only for numbered functions. - */ - void -func_unref(char_u *name) -{ - ufunc_T *fp; - - if (name == NULL) - return; - else if (isdigit(*name)) - { - fp = find_func(name); - if (fp == NULL) - { -#ifdef EXITFREE - if (!entered_free_all_mem) -#endif - EMSG2(_(e_intern2), "func_unref()"); - } - else if (--fp->uf_refcount <= 0) - { - /* Only delete it when it's not being used. Otherwise it's done - * when "uf_calls" becomes zero. */ - if (fp->uf_calls == 0) - func_free(fp); - } - } - else if (STRNCMP(name, "", 8) == 0) - { - /* fail silently, when lambda function isn't found. */ - fp = find_func(name); - if (fp != NULL && --fp->uf_refcount <= 0) - { - /* Only delete it when it's not being used. Otherwise it's done - * when "uf_calls" becomes zero. */ - if (fp->uf_calls == 0) - func_free(fp); - } - } -} - -/* - * Count a reference to a Function. - */ - void -func_ref(char_u *name) -{ - ufunc_T *fp; - - if (name == NULL) - return; - else if (isdigit(*name)) - { - fp = find_func(name); - if (fp == NULL) - EMSG2(_(e_intern2), "func_ref()"); - else - ++fp->uf_refcount; - } - else if (STRNCMP(name, "", 8) == 0) - { - /* fail silently, when lambda function isn't found. */ - fp = find_func(name); - if (fp != NULL) - ++fp->uf_refcount; - } -} - -/* - * Call a user function. - */ - static void -call_user_func( - ufunc_T *fp, /* pointer to function */ - int argcount, /* nr of args */ - typval_T *argvars, /* arguments */ - typval_T *rettv, /* return value */ - linenr_T firstline, /* first line of range */ - linenr_T lastline, /* last line of range */ - dict_T *selfdict) /* Dictionary for "self" */ -{ - char_u *save_sourcing_name; - linenr_T save_sourcing_lnum; - scid_T save_current_SID; - funccall_T *fc; - int save_did_emsg; - static int depth = 0; - dictitem_T *v; - int fixvar_idx = 0; /* index in fixvar[] */ - int i; - int ai; - int islambda = FALSE; - char_u numbuf[NUMBUFLEN]; - char_u *name; - size_t len; -#ifdef FEAT_PROFILE - proftime_T wait_start; - proftime_T call_start; -#endif - - /* If depth of calling is getting too high, don't execute the function */ - if (depth >= p_mfd) - { - EMSG(_("E132: Function call depth is higher than 'maxfuncdepth'")); - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = -1; - return; - } - ++depth; - - line_breakcheck(); /* check for CTRL-C hit */ - - fc = (funccall_T *)alloc(sizeof(funccall_T)); - fc->caller = current_funccal; - current_funccal = fc; - fc->func = fp; - fc->rettv = rettv; - rettv->vval.v_number = 0; - fc->linenr = 0; - fc->returned = FALSE; - fc->level = ex_nesting_level; - /* Check if this function has a breakpoint. */ - fc->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, (linenr_T)0); - fc->dbg_tick = debug_tick; - - if (STRNCMP(fp->uf_name, "", 8) == 0) - islambda = TRUE; - - /* - * Note about using fc->fixvar[]: This is an array of FIXVAR_CNT variables - * with names up to VAR_SHORT_LEN long. This avoids having to alloc/free - * each argument variable and saves a lot of time. - */ - /* - * Init l: variables. - */ - init_var_dict(&fc->l_vars, &fc->l_vars_var, VAR_DEF_SCOPE); - if (selfdict != NULL) - { - /* Set l:self to "selfdict". Use "name" to avoid a warning from - * some compiler that checks the destination size. */ - v = &fc->fixvar[fixvar_idx++].var; - name = v->di_key; - STRCPY(name, "self"); - v->di_flags = DI_FLAGS_RO + DI_FLAGS_FIX; - hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v)); - v->di_tv.v_type = VAR_DICT; - v->di_tv.v_lock = 0; - v->di_tv.vval.v_dict = selfdict; - ++selfdict->dv_refcount; - } - - /* - * Init a: variables. - * Set a:0 to "argcount". - * Set a:000 to a list with room for the "..." arguments. - */ - init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE); - add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "0", - (varnumber_T)(argcount - fp->uf_args.ga_len)); - /* Use "name" to avoid a warning from some compiler that checks the - * destination size. */ - v = &fc->fixvar[fixvar_idx++].var; - name = v->di_key; - STRCPY(name, "000"); - v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v)); - v->di_tv.v_type = VAR_LIST; - v->di_tv.v_lock = VAR_FIXED; - v->di_tv.vval.v_list = &fc->l_varlist; - vim_memset(&fc->l_varlist, 0, sizeof(list_T)); - fc->l_varlist.lv_refcount = DO_NOT_FREE_CNT; - fc->l_varlist.lv_lock = VAR_FIXED; - - /* - * Set a:firstline to "firstline" and a:lastline to "lastline". - * Set a:name to named arguments. - * Set a:N to the "..." arguments. - */ - add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "firstline", - (varnumber_T)firstline); - add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "lastline", - (varnumber_T)lastline); - for (i = 0; i < argcount; ++i) - { - int addlocal = FALSE; - dictitem_T *v2; - - ai = i - fp->uf_args.ga_len; - if (ai < 0) - { - /* named argument a:name */ - name = FUNCARG(fp, i); - if (islambda) - addlocal = TRUE; - } - else - { - /* "..." argument a:1, a:2, etc. */ - sprintf((char *)numbuf, "%d", ai + 1); - name = numbuf; - } - if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN) - { - v = &fc->fixvar[fixvar_idx++].var; - v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - - if (addlocal) - v2 = v; - } - else - { - v = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T) - + STRLEN(name))); - if (v == NULL) - break; - v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC; - - if (addlocal) - { - v2 = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T) - + STRLEN(name))); - if (v2 == NULL) - { - vim_free(v); - break; - } - v2->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC; - } - } - STRCPY(v->di_key, name); - hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v)); - - /* Note: the values are copied directly to avoid alloc/free. - * "argvars" must have VAR_FIXED for v_lock. */ - v->di_tv = argvars[i]; - v->di_tv.v_lock = VAR_FIXED; - - /* Named arguments can be accessed without the "a:" prefix in lambda - * expressions. Add to the l: dict. */ - if (addlocal) - { - STRCPY(v2->di_key, name); - copy_tv(&v->di_tv, &v2->di_tv); - v2->di_tv.v_lock = VAR_FIXED; - hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v2)); - } - - if (ai >= 0 && ai < MAX_FUNC_ARGS) - { - list_append(&fc->l_varlist, &fc->l_listitems[ai]); - fc->l_listitems[ai].li_tv = argvars[i]; - fc->l_listitems[ai].li_tv.v_lock = VAR_FIXED; - } - } - - /* Don't redraw while executing the function. */ - ++RedrawingDisabled; - save_sourcing_name = sourcing_name; - save_sourcing_lnum = sourcing_lnum; - sourcing_lnum = 1; - /* need space for function name + ("function " + 3) or "[number]" */ - len = (save_sourcing_name == NULL ? 0 : STRLEN(save_sourcing_name)) - + STRLEN(fp->uf_name) + 20; - sourcing_name = alloc((unsigned)len); - if (sourcing_name != NULL) - { - if (save_sourcing_name != NULL - && STRNCMP(save_sourcing_name, "function ", 9) == 0) - sprintf((char *)sourcing_name, "%s[%d]..", - save_sourcing_name, (int)save_sourcing_lnum); - else - STRCPY(sourcing_name, "function "); - cat_func_name(sourcing_name + STRLEN(sourcing_name), fp); - - if (p_verbose >= 12) - { - ++no_wait_return; - verbose_enter_scroll(); - - smsg((char_u *)_("calling %s"), sourcing_name); - if (p_verbose >= 14) - { - char_u buf[MSG_BUF_LEN]; - char_u numbuf2[NUMBUFLEN]; - char_u *tofree; - char_u *s; - - msg_puts((char_u *)"("); - for (i = 0; i < argcount; ++i) - { - if (i > 0) - msg_puts((char_u *)", "); - if (argvars[i].v_type == VAR_NUMBER) - msg_outnum((long)argvars[i].vval.v_number); - else - { - /* Do not want errors such as E724 here. */ - ++emsg_off; - s = tv2string(&argvars[i], &tofree, numbuf2, 0); - --emsg_off; - if (s != NULL) - { - if (vim_strsize(s) > MSG_BUF_CLEN) - { - trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN); - s = buf; - } - msg_puts(s); - vim_free(tofree); - } - } - } - msg_puts((char_u *)")"); - } - msg_puts((char_u *)"\n"); /* don't overwrite this either */ - - verbose_leave_scroll(); - --no_wait_return; - } - } -#ifdef FEAT_PROFILE - if (do_profiling == PROF_YES) - { - if (!fp->uf_profiling && has_profiling(FALSE, fp->uf_name, NULL)) - func_do_profile(fp); - if (fp->uf_profiling - || (fc->caller != NULL && fc->caller->func->uf_profiling)) - { - ++fp->uf_tm_count; - profile_start(&call_start); - profile_zero(&fp->uf_tm_children); - } - script_prof_save(&wait_start); - } -#endif - - save_current_SID = current_SID; - current_SID = fp->uf_script_ID; - save_did_emsg = did_emsg; - did_emsg = FALSE; - - /* call do_cmdline() to execute the lines */ - do_cmdline(NULL, get_func_line, (void *)fc, - DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT); - - --RedrawingDisabled; - - /* when the function was aborted because of an error, return -1 */ - if ((did_emsg && (fp->uf_flags & FC_ABORT)) || rettv->v_type == VAR_UNKNOWN) - { - clear_tv(rettv); - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = -1; - } - -#ifdef FEAT_PROFILE - if (do_profiling == PROF_YES && (fp->uf_profiling - || (fc->caller != NULL && fc->caller->func->uf_profiling))) - { - profile_end(&call_start); - profile_sub_wait(&wait_start, &call_start); - profile_add(&fp->uf_tm_total, &call_start); - profile_self(&fp->uf_tm_self, &call_start, &fp->uf_tm_children); - if (fc->caller != NULL && fc->caller->func->uf_profiling) - { - profile_add(&fc->caller->func->uf_tm_children, &call_start); - profile_add(&fc->caller->func->uf_tml_children, &call_start); - } - } -#endif - - /* when being verbose, mention the return value */ - if (p_verbose >= 12) - { - ++no_wait_return; - verbose_enter_scroll(); - - if (aborting()) - smsg((char_u *)_("%s aborted"), sourcing_name); - else if (fc->rettv->v_type == VAR_NUMBER) - smsg((char_u *)_("%s returning #%ld"), sourcing_name, - (long)fc->rettv->vval.v_number); - else - { - char_u buf[MSG_BUF_LEN]; - char_u numbuf2[NUMBUFLEN]; - char_u *tofree; - char_u *s; - - /* The value may be very long. Skip the middle part, so that we - * have some idea how it starts and ends. smsg() would always - * truncate it at the end. Don't want errors such as E724 here. */ - ++emsg_off; - s = tv2string(fc->rettv, &tofree, numbuf2, 0); - --emsg_off; - if (s != NULL) - { - if (vim_strsize(s) > MSG_BUF_CLEN) - { - trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN); - s = buf; - } - smsg((char_u *)_("%s returning %s"), sourcing_name, s); - vim_free(tofree); - } - } - msg_puts((char_u *)"\n"); /* don't overwrite this either */ - - verbose_leave_scroll(); - --no_wait_return; - } - - vim_free(sourcing_name); - sourcing_name = save_sourcing_name; - sourcing_lnum = save_sourcing_lnum; - current_SID = save_current_SID; -#ifdef FEAT_PROFILE - if (do_profiling == PROF_YES) - script_prof_restore(&wait_start); -#endif - - if (p_verbose >= 12 && sourcing_name != NULL) - { - ++no_wait_return; - verbose_enter_scroll(); - - smsg((char_u *)_("continuing in %s"), sourcing_name); - msg_puts((char_u *)"\n"); /* don't overwrite this either */ - - verbose_leave_scroll(); - --no_wait_return; - } - - did_emsg |= save_did_emsg; - current_funccal = fc->caller; - --depth; - - /* If the a:000 list and the l: and a: dicts are not referenced we can - * free the funccall_T and what's in it. */ - if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT - && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT - && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT) - { - free_funccal(fc, FALSE); - } - else - { - hashitem_T *hi; - listitem_T *li; - int todo; - - /* "fc" is still in use. This can happen when returning "a:000" or - * assigning "l:" to a global variable. - * Link "fc" in the list for garbage collection later. */ - fc->caller = previous_funccal; - previous_funccal = fc; - - /* Make a copy of the a: variables, since we didn't do that above. */ - todo = (int)fc->l_avars.dv_hashtab.ht_used; - for (hi = fc->l_avars.dv_hashtab.ht_array; todo > 0; ++hi) - { - if (!HASHITEM_EMPTY(hi)) - { - --todo; - v = HI2DI(hi); - copy_tv(&v->di_tv, &v->di_tv); - } - } - - /* Make a copy of the a:000 items, since we didn't do that above. */ - for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) - copy_tv(&li->li_tv, &li->li_tv); - } -} - -/* - * Return TRUE if items in "fc" do not have "copyID". That means they are not - * referenced from anywhere that is in use. - */ - static int -can_free_funccal(funccall_T *fc, int copyID) -{ - return (fc->l_varlist.lv_copyID != copyID - && fc->l_vars.dv_copyID != copyID - && fc->l_avars.dv_copyID != copyID); -} - -/* - * Free "fc" and what it contains. - */ - static void -free_funccal( - funccall_T *fc, - int free_val) /* a: vars were allocated */ -{ - listitem_T *li; - - /* The a: variables typevals may not have been allocated, only free the - * allocated variables. */ - vars_clear_ext(&fc->l_avars.dv_hashtab, free_val); - - /* free all l: variables */ - vars_clear(&fc->l_vars.dv_hashtab); - - /* Free the a:000 variables if they were allocated. */ - if (free_val) - for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) - clear_tv(&li->li_tv); - - vim_free(fc); -} - -/* - * Add a number variable "name" to dict "dp" with value "nr". - */ - static void -add_nr_var( - dict_T *dp, - dictitem_T *v, - char *name, - varnumber_T nr) -{ - STRCPY(v->di_key, name); - v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - hash_add(&dp->dv_hashtab, DI2HIKEY(v)); - v->di_tv.v_type = VAR_NUMBER; - v->di_tv.v_lock = VAR_FIXED; - v->di_tv.vval.v_number = nr; -} - -/* - * ":return [expr]" - */ - void -ex_return(exarg_T *eap) -{ - char_u *arg = eap->arg; - typval_T rettv; - int returning = FALSE; - - if (current_funccal == NULL) - { - EMSG(_("E133: :return not inside a function")); - return; - } - - if (eap->skip) - ++emsg_skip; - - eap->nextcmd = NULL; - if ((*arg != NUL && *arg != '|' && *arg != '\n') - && eval0(arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) - { - if (!eap->skip) - returning = do_return(eap, FALSE, TRUE, &rettv); - else - clear_tv(&rettv); - } - /* It's safer to return also on error. */ - else if (!eap->skip) - { - /* - * Return unless the expression evaluation has been cancelled due to an - * aborting error, an interrupt, or an exception. - */ - if (!aborting()) - returning = do_return(eap, FALSE, TRUE, NULL); - } - - /* When skipping or the return gets pending, advance to the next command - * in this line (!returning). Otherwise, ignore the rest of the line. - * Following lines will be ignored by get_func_line(). */ - if (returning) - eap->nextcmd = NULL; - else if (eap->nextcmd == NULL) /* no argument */ - eap->nextcmd = check_nextcmd(arg); - - if (eap->skip) - --emsg_skip; -} - -/* - * Return from a function. Possibly makes the return pending. Also called - * for a pending return at the ":endtry" or after returning from an extra - * do_cmdline(). "reanimate" is used in the latter case. "is_cmd" is set - * when called due to a ":return" command. "rettv" may point to a typval_T - * with the return rettv. Returns TRUE when the return can be carried out, - * FALSE when the return gets pending. - */ - int -do_return( - exarg_T *eap, - int reanimate, - int is_cmd, - void *rettv) -{ - int idx; - struct condstack *cstack = eap->cstack; - - if (reanimate) - /* Undo the return. */ - current_funccal->returned = FALSE; - - /* - * Cleanup (and inactivate) conditionals, but stop when a try conditional - * not in its finally clause (which then is to be executed next) is found. - * In this case, make the ":return" pending for execution at the ":endtry". - * Otherwise, return normally. - */ - idx = cleanup_conditionals(eap->cstack, 0, TRUE); - if (idx >= 0) - { - cstack->cs_pending[idx] = CSTP_RETURN; - - if (!is_cmd && !reanimate) - /* A pending return again gets pending. "rettv" points to an - * allocated variable with the rettv of the original ":return"'s - * argument if present or is NULL else. */ - cstack->cs_rettv[idx] = rettv; - else - { - /* When undoing a return in order to make it pending, get the stored - * return rettv. */ - if (reanimate) - rettv = current_funccal->rettv; - - if (rettv != NULL) - { - /* Store the value of the pending return. */ - if ((cstack->cs_rettv[idx] = alloc_tv()) != NULL) - *(typval_T *)cstack->cs_rettv[idx] = *(typval_T *)rettv; - else - EMSG(_(e_outofmem)); - } - else - cstack->cs_rettv[idx] = NULL; - - if (reanimate) - { - /* The pending return value could be overwritten by a ":return" - * without argument in a finally clause; reset the default - * return value. */ - current_funccal->rettv->v_type = VAR_NUMBER; - current_funccal->rettv->vval.v_number = 0; - } - } - report_make_pending(CSTP_RETURN, rettv); - } - else - { - current_funccal->returned = TRUE; - - /* If the return is carried out now, store the return value. For - * a return immediately after reanimation, the value is already - * there. */ - if (!reanimate && rettv != NULL) - { - clear_tv(current_funccal->rettv); - *current_funccal->rettv = *(typval_T *)rettv; - if (!is_cmd) - vim_free(rettv); - } - } - - return idx < 0; -} - -/* - * Free the variable with a pending return value. - */ - void -discard_pending_return(void *rettv) -{ - free_tv((typval_T *)rettv); -} - -/* - * Generate a return command for producing the value of "rettv". The result - * is an allocated string. Used by report_pending() for verbose messages. - */ - char_u * -get_return_cmd(void *rettv) -{ - char_u *s = NULL; - char_u *tofree = NULL; - char_u numbuf[NUMBUFLEN]; - - if (rettv != NULL) - s = echo_string((typval_T *)rettv, &tofree, numbuf, 0); - if (s == NULL) - s = (char_u *)""; - - STRCPY(IObuff, ":return "); - STRNCPY(IObuff + 8, s, IOSIZE - 8); - if (STRLEN(s) + 8 >= IOSIZE) - STRCPY(IObuff + IOSIZE - 4, "..."); - vim_free(tofree); - return vim_strsave(IObuff); -} - -/* - * Get next function line. - * Called by do_cmdline() to get the next line. - * Returns allocated string, or NULL for end of function. - */ - char_u * -get_func_line( - int c UNUSED, - void *cookie, - int indent UNUSED) -{ - funccall_T *fcp = (funccall_T *)cookie; - ufunc_T *fp = fcp->func; - char_u *retval; - garray_T *gap; /* growarray with function lines */ - - /* If breakpoints have been added/deleted need to check for it. */ - if (fcp->dbg_tick != debug_tick) - { - fcp->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, - sourcing_lnum); - fcp->dbg_tick = debug_tick; - } -#ifdef FEAT_PROFILE - if (do_profiling == PROF_YES) - func_line_end(cookie); -#endif - - gap = &fp->uf_lines; - if (((fp->uf_flags & FC_ABORT) && did_emsg && !aborted_in_try()) - || fcp->returned) - retval = NULL; - else - { - /* Skip NULL lines (continuation lines). */ - while (fcp->linenr < gap->ga_len - && ((char_u **)(gap->ga_data))[fcp->linenr] == NULL) - ++fcp->linenr; - if (fcp->linenr >= gap->ga_len) - retval = NULL; - else - { - retval = vim_strsave(((char_u **)(gap->ga_data))[fcp->linenr++]); - sourcing_lnum = fcp->linenr; -#ifdef FEAT_PROFILE - if (do_profiling == PROF_YES) - func_line_start(cookie); -#endif - } - } - - /* Did we encounter a breakpoint? */ - if (fcp->breakpoint != 0 && fcp->breakpoint <= sourcing_lnum) - { - dbg_breakpoint(fp->uf_name, sourcing_lnum); - /* Find next breakpoint. */ - fcp->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, - sourcing_lnum); - fcp->dbg_tick = debug_tick; - } - - return retval; -} - -#if defined(FEAT_PROFILE) || defined(PROTO) -/* - * Called when starting to read a function line. - * "sourcing_lnum" must be correct! - * When skipping lines it may not actually be executed, but we won't find out - * until later and we need to store the time now. - */ - void -func_line_start(void *cookie) -{ - funccall_T *fcp = (funccall_T *)cookie; - ufunc_T *fp = fcp->func; - - if (fp->uf_profiling && sourcing_lnum >= 1 - && sourcing_lnum <= fp->uf_lines.ga_len) - { - fp->uf_tml_idx = sourcing_lnum - 1; - /* Skip continuation lines. */ - while (fp->uf_tml_idx > 0 && FUNCLINE(fp, fp->uf_tml_idx) == NULL) - --fp->uf_tml_idx; - fp->uf_tml_execed = FALSE; - profile_start(&fp->uf_tml_start); - profile_zero(&fp->uf_tml_children); - profile_get_wait(&fp->uf_tml_wait); - } -} - -/* - * Called when actually executing a function line. - */ - void -func_line_exec(void *cookie) -{ - funccall_T *fcp = (funccall_T *)cookie; - ufunc_T *fp = fcp->func; - - if (fp->uf_profiling && fp->uf_tml_idx >= 0) - fp->uf_tml_execed = TRUE; -} - -/* - * Called when done with a function line. - */ - void -func_line_end(void *cookie) -{ - funccall_T *fcp = (funccall_T *)cookie; - ufunc_T *fp = fcp->func; - - if (fp->uf_profiling && fp->uf_tml_idx >= 0) - { - if (fp->uf_tml_execed) - { - ++fp->uf_tml_count[fp->uf_tml_idx]; - profile_end(&fp->uf_tml_start); - profile_sub_wait(&fp->uf_tml_wait, &fp->uf_tml_start); - profile_add(&fp->uf_tml_total[fp->uf_tml_idx], &fp->uf_tml_start); - profile_self(&fp->uf_tml_self[fp->uf_tml_idx], &fp->uf_tml_start, - &fp->uf_tml_children); - } - fp->uf_tml_idx = -1; - } -} -#endif - -/* - * Return TRUE if the currently active function should be ended, because a - * return was encountered or an error occurred. Used inside a ":while". - */ - int -func_has_ended(void *cookie) -{ - funccall_T *fcp = (funccall_T *)cookie; - - /* Ignore the "abort" flag if the abortion behavior has been changed due to - * an error inside a try conditional. */ - return (((fcp->func->uf_flags & FC_ABORT) && did_emsg && !aborted_in_try()) - || fcp->returned); -} - -/* - * return TRUE if cookie indicates a function which "abort"s on errors. - */ - int -func_has_abort( - void *cookie) -{ - return ((funccall_T *)cookie)->func->uf_flags & FC_ABORT; -} - #if defined(FEAT_VIMINFO) || defined(FEAT_SESSION) typedef enum { @@ -25078,7 +21658,7 @@ read_viminfo_varlist(vir_T *virp, int writing) char_u *tab; int type = VAR_NUMBER; typval_T tv; - funccall_T *save_funccal; + void *save_funccal; if (!writing && (find_viminfo_parameter('!') != NULL)) { @@ -25127,10 +21707,9 @@ read_viminfo_varlist(vir_T *virp, int writing) } /* when in a function use global variables */ - save_funccal = current_funccal; - current_funccal = NULL; + save_funccal = clear_current_funccal(); set_var(virp->vir_line + 1, &tv, FALSE); - current_funccal = save_funccal; + restore_current_funccal(save_funccal); if (tv.v_type == VAR_STRING) vim_free(tv.vval.v_string); diff --git a/src/globals.h b/src/globals.h index 770756cd91..07e3cf7b26 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1536,6 +1536,8 @@ EXTERN char_u e_readonlysbx[] INIT(= N_("E794: Cannot set variable in the sandbo EXTERN char_u e_emptykey[] INIT(= N_("E713: Cannot use empty key for Dictionary")); EXTERN char_u e_dictreq[] INIT(= N_("E715: Dictionary required")); EXTERN char_u e_listidx[] INIT(= N_("E684: list index out of range: %ld")); +EXTERN char_u e_toomanyarg[] INIT(= N_("E118: Too many arguments for function: %s")); +EXTERN char_u e_dictkey[] INIT(= N_("E716: Key not present in Dictionary: %s")); #endif #ifdef FEAT_QUICKFIX EXTERN char_u e_readerrf[] INIT(= N_("E47: Error while reading errorfile")); diff --git a/src/proto.h b/src/proto.h index ed2da6ef5c..b3683af424 100644 --- a/src/proto.h +++ b/src/proto.h @@ -165,6 +165,7 @@ void qsort(void *base, size_t elm_count, size_t elm_size, int (*cmp)(const void # endif # include "ui.pro" # include "undo.pro" +# include "userfunc.pro" # include "version.pro" # include "window.pro" diff --git a/src/proto/eval.pro b/src/proto/eval.pro index ee663ee7a9..d7315b058c 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -1,11 +1,6 @@ /* eval.c */ void eval_init(void); void eval_clear(void); -char_u *func_name(void *cookie); -linenr_T *func_breakpoint(void *cookie); -int *func_dbg_tick(void *cookie); -int func_level(void *cookie); -int current_func_returned(void); void set_internal_string_var(char_u *name, char_u *value); int var_redir_start(char_u *name, int append); void var_redir_str(char_u *value, int value_len); @@ -27,22 +22,21 @@ int call_vim_function(char_u *func, int argc, char_u **argv, int safe, int str_a varnumber_T call_func_retnr(char_u *func, int argc, char_u **argv, int safe); void *call_func_retstr(char_u *func, int argc, char_u **argv, int safe); void *call_func_retlist(char_u *func, int argc, char_u **argv, int safe); -void *save_funccal(void); -void restore_funccal(void *vfc); -void prof_child_enter(proftime_T *tm); -void prof_child_exit(proftime_T *tm); int eval_foldexpr(char_u *arg, int *cp); void ex_let(exarg_T *eap); +void list_hashtable_vars(hashtab_T *ht, char_u *prefix, int empty, int *first); +char_u *get_lval(char_u *name, typval_T *rettv, lval_T *lp, int unlet, int skip, int flags, int fne_flags); +void clear_lval(lval_T *lp); void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip); int next_for_item(void *fi_void, char_u *arg); void free_for_info(void *fi_void); void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx); -void ex_call(exarg_T *eap); void ex_unlet(exarg_T *eap); void ex_lockvar(exarg_T *eap); int do_unlet(char_u *name, int forceit); void del_menutrans_vars(void); char_u *get_user_var_name(expand_T *xp, int idx); +int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate); int eval1(char_u **arg, typval_T *rettv, int evaluate); void partial_unref(partial_T *pt); int tv_equal(typval_T *tv1, typval_T *tv2, int ic, int recursive); @@ -52,20 +46,25 @@ int set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack); int set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack); int set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack); char_u *echo_string_core(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID, int echo_style, int restore_copyID, int dict_val); +char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); char_u *string_quote(char_u *str, int function); int string2float(char_u *text, float_T *value); char_u *get_function_name(expand_T *xp, int idx); char_u *get_expr_name(expand_T *xp, int idx); -int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict_in); +int find_internal_func(char_u *name); +int call_internal_func(char_u *name, int argcount, typval_T *argvars, typval_T *rettv); buf_T *buflist_find_by_name(char_u *name, int curtab_only); -int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv); void execute_redir_str(char_u *value, int value_len); void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv); float_T vim_round(float_T f); long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, char_u *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit); char_u *get_callback(typval_T *arg, partial_T **pp); void free_callback(char_u *callback, partial_T *partial); +int get_id_len(char_u **arg); +char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end, int flags); +int eval_isnamec(int c); +int eval_isnamec1(int c); void set_vim_var_nr(int idx, varnumber_T val); varnumber_T get_vim_var_nr(int idx); char_u *get_vim_var_str(int idx); @@ -79,6 +78,7 @@ void set_reg_var(int c); char_u *v_exception(char_u *oldval); char_u *v_throwpoint(char_u *oldval); char_u *set_cmdarg(exarg_T *eap, char_u *oldarg); +int handle_subscript(char_u **arg, typval_T *rettv, int evaluate, int verbose); typval_T *alloc_tv(void); void free_tv(typval_T *varp); void clear_tv(typval_T *varp); @@ -88,11 +88,13 @@ char_u *get_tv_string(typval_T *varp); char_u *get_tv_string_buf(typval_T *varp, char_u *buf); char_u *get_tv_string_chk(typval_T *varp); char_u *get_tv_string_buf_chk(typval_T *varp, char_u *buf); +dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload); char_u *get_var_value(char_u *name); void new_script_vars(scid_T id); void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope); void unref_var_dict(dict_T *dict); void vars_clear(hashtab_T *ht); +void vars_clear_ext(hashtab_T *ht, int free_val); int var_check_ro(int flags, char_u *name, int use_gettext); int var_check_func_name(char_u *name, int new_var); int valid_varname(char_u *varname); @@ -102,25 +104,8 @@ int item_copy(typval_T *from, typval_T *to, int deep, int copyID); void ex_echo(exarg_T *eap); void ex_echohl(exarg_T *eap); void ex_execute(exarg_T *eap); -void ex_function(exarg_T *eap); -void free_all_functions(void); -int translated_function_exists(char_u *name); -char_u *get_expanded_name(char_u *name, int check); -void func_dump_profile(FILE *fd); -char_u *get_user_func_name(expand_T *xp, int idx); -void ex_delfunction(exarg_T *eap); -void func_unref(char_u *name); -void func_ref(char_u *name); -void ex_return(exarg_T *eap); -int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv); -void discard_pending_return(void *rettv); -char_u *get_return_cmd(void *rettv); -char_u *get_func_line(int c, void *cookie, int indent); -void func_line_start(void *cookie); -void func_line_exec(void *cookie); -void func_line_end(void *cookie); -int func_has_ended(void *cookie); -int func_has_abort(void *cookie); +char_u *autoload_name(char_u *name); +int script_autoload(char_u *name, int reload); int read_viminfo_varlist(vir_T *virp, int writing); void write_viminfo_varlist(FILE *fp); int store_session_globals(FILE *fd); diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro new file mode 100644 index 0000000000..b354197a32 --- /dev/null +++ b/src/proto/userfunc.pro @@ -0,0 +1,52 @@ +/* userfunc.c */ +void func_init(void); +int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate); +char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload); +int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict); +void free_all_functions(void); +int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv); +int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict_in); +void ex_function(exarg_T *eap); +int eval_fname_script(char_u *p); +int translated_function_exists(char_u *name); +int function_exists(char_u *name); +char_u *get_expanded_name(char_u *name, int check); +void func_dump_profile(FILE *fd); +void prof_child_enter(proftime_T *tm); +void prof_child_exit(proftime_T *tm); +char_u *get_user_func_name(expand_T *xp, int idx); +void ex_delfunction(exarg_T *eap); +void func_unref(char_u *name); +void func_ref(char_u *name); +void ex_return(exarg_T *eap); +void ex_call(exarg_T *eap); +int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv); +void discard_pending_return(void *rettv); +char_u *get_return_cmd(void *rettv); +char_u *get_func_line(int c, void *cookie, int indent); +void func_line_start(void *cookie); +void func_line_exec(void *cookie); +void func_line_end(void *cookie); +int func_has_ended(void *cookie); +int func_has_abort(void *cookie); +dict_T *make_partial(dict_T *selfdict_in, typval_T *rettv); +char_u *func_name(void *cookie); +linenr_T *func_breakpoint(void *cookie); +int *func_dbg_tick(void *cookie); +int func_level(void *cookie); +int current_func_returned(void); +void *save_funccal(void); +void restore_funccal(void *vfc); +int free_unref_funccal(int copyID, int testing); +hashtab_T *get_funccal_local_ht(void); +dictitem_T *get_funccal_local_var(void); +hashtab_T *get_funccal_args_ht(void); +dictitem_T *get_funccal_args_var(void); +void *clear_current_funccal(void); +void restore_current_funccal(void *f); +void list_func_vars(int *first); +dict_T *get_current_funccal_dict(hashtab_T *ht); +int set_ref_in_previous_funccal(int copyID); +int set_ref_in_call_stack(int copyID); +int set_ref_in_func_args(int copyID); +/* vim: set ft=c : */ diff --git a/src/structs.h b/src/structs.h index 740f7d04d5..00a8e035b4 100644 --- a/src/structs.h +++ b/src/structs.h @@ -3117,3 +3117,48 @@ typedef struct int diff_mode; /* start with 'diff' set */ #endif } mparm_T; + +/* + * Structure returned by get_lval() and used by set_var_lval(). + * For a plain name: + * "name" points to the variable name. + * "exp_name" is NULL. + * "tv" is NULL + * For a magic braces name: + * "name" points to the expanded variable name. + * "exp_name" is non-NULL, to be freed later. + * "tv" is NULL + * For an index in a list: + * "name" points to the (expanded) variable name. + * "exp_name" NULL or non-NULL, to be freed later. + * "tv" points to the (first) list item value + * "li" points to the (first) list item + * "range", "n1", "n2" and "empty2" indicate what items are used. + * For an existing Dict item: + * "name" points to the (expanded) variable name. + * "exp_name" NULL or non-NULL, to be freed later. + * "tv" points to the dict item value + * "newkey" is NULL + * For a non-existing Dict item: + * "name" points to the (expanded) variable name. + * "exp_name" NULL or non-NULL, to be freed later. + * "tv" points to the Dictionary typval_T + * "newkey" is the key for the new item. + */ +typedef struct lval_S +{ + char_u *ll_name; /* start of variable name (can be NULL) */ + char_u *ll_exp_name; /* NULL or expanded name in allocated memory. */ + typval_T *ll_tv; /* Typeval of item being used. If "newkey" + isn't NULL it's the Dict to which to add + the item. */ + listitem_T *ll_li; /* The list item or NULL. */ + list_T *ll_list; /* The list or NULL. */ + int ll_range; /* TRUE when a [i:j] range was used */ + long ll_n1; /* First index for list */ + long ll_n2; /* Second index for list range */ + int ll_empty2; /* Second index is empty: [i:] */ + dict_T *ll_dict; /* The Dictionary or NULL */ + dictitem_T *ll_di; /* The dictitem or NULL */ + char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */ +} lval_T; diff --git a/src/userfunc.c b/src/userfunc.c new file mode 100644 index 0000000000..67d9c6d5fa --- /dev/null +++ b/src/userfunc.c @@ -0,0 +1,3494 @@ +/* vi:set ts=8 sts=4 sw=4: + * + * 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. + */ + +/* + * eval.c: User defined function support + */ + +#include "vim.h" + +#if defined(FEAT_EVAL) || defined(PROTO) + +/* + * Structure to hold info for a user function. + */ +typedef struct ufunc ufunc_T; + +struct ufunc +{ + int uf_varargs; /* variable nr of arguments */ + int uf_flags; + int uf_calls; /* nr of active calls */ + garray_T uf_args; /* arguments */ + garray_T uf_lines; /* function lines */ +#ifdef FEAT_PROFILE + int uf_profiling; /* TRUE when func is being profiled */ + /* profiling the function as a whole */ + int uf_tm_count; /* nr of calls */ + proftime_T uf_tm_total; /* time spent in function + children */ + proftime_T uf_tm_self; /* time spent in function itself */ + proftime_T uf_tm_children; /* time spent in children this call */ + /* profiling the function per line */ + int *uf_tml_count; /* nr of times line was executed */ + proftime_T *uf_tml_total; /* time spent in a line + children */ + proftime_T *uf_tml_self; /* time spent in a line itself */ + proftime_T uf_tml_start; /* start time for current line */ + proftime_T uf_tml_children; /* time spent in children for this line */ + proftime_T uf_tml_wait; /* start wait time for current line */ + int uf_tml_idx; /* index of line being timed; -1 if none */ + int uf_tml_execed; /* line being timed was executed */ +#endif + scid_T uf_script_ID; /* ID of script where function was defined, + used for s: variables */ + int uf_refcount; /* for numbered function: reference count */ + char_u uf_name[1]; /* name of function (actually longer); can + start with 123_ ( is K_SPECIAL + KS_EXTRA KE_SNR) */ +}; + +/* function flags */ +#define FC_ABORT 1 /* abort function on error */ +#define FC_RANGE 2 /* function accepts range */ +#define FC_DICT 4 /* Dict function, uses "self" */ + +/* From user function to hashitem and back. */ +static ufunc_T dumuf; +#define UF2HIKEY(fp) ((fp)->uf_name) +#define HIKEY2UF(p) ((ufunc_T *)(p - (dumuf.uf_name - (char_u *)&dumuf))) +#define HI2UF(hi) HIKEY2UF((hi)->hi_key) + +#define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j] +#define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j] + +#define MAX_FUNC_ARGS 20 /* maximum number of function arguments */ +#define VAR_SHORT_LEN 20 /* short variable name length */ +#define FIXVAR_CNT 12 /* number of fixed variables */ + +/* structure to hold info for a function that is currently being executed. */ +typedef struct funccall_S funccall_T; + +struct funccall_S +{ + ufunc_T *func; /* function being called */ + int linenr; /* next line to be executed */ + int returned; /* ":return" used */ + struct /* fixed variables for arguments */ + { + dictitem_T var; /* variable (without room for name) */ + char_u room[VAR_SHORT_LEN]; /* room for the name */ + } fixvar[FIXVAR_CNT]; + dict_T l_vars; /* l: local function variables */ + dictitem_T l_vars_var; /* variable for l: scope */ + dict_T l_avars; /* a: argument variables */ + dictitem_T l_avars_var; /* variable for a: scope */ + list_T l_varlist; /* list for a:000 */ + listitem_T l_listitems[MAX_FUNC_ARGS]; /* listitems for a:000 */ + typval_T *rettv; /* return value */ + linenr_T breakpoint; /* next line with breakpoint or zero */ + int dbg_tick; /* debug_tick when breakpoint was set */ + int level; /* top nesting level of executed function */ +#ifdef FEAT_PROFILE + proftime_T prof_child; /* time spent in a child */ +#endif + funccall_T *caller; /* calling function or NULL */ +}; + +/* + * Struct used by trans_function_name() + */ +typedef struct +{ + dict_T *fd_dict; /* Dictionary used */ + char_u *fd_newkey; /* new key in "dict" in allocated memory */ + dictitem_T *fd_di; /* Dictionary item used */ +} funcdict_T; + +/* + * All user-defined functions are found in this hashtable. + */ +static hashtab_T func_hashtab; + +/* Used by get_func_tv() */ +static garray_T funcargs = GA_EMPTY; + +/* pointer to funccal for currently active function */ +funccall_T *current_funccal = NULL; + +/* pointer to list of previously used funccal, still around because some + * item in it is still being used. */ +funccall_T *previous_funccal = NULL; + +static char *e_funcexts = N_("E122: Function %s already exists, add ! to replace it"); +static char *e_funcdict = N_("E717: Dictionary entry already exists"); +static char *e_funcref = N_("E718: Funcref required"); +static char *e_nofunc = N_("E130: Unknown function: %s"); + +#ifdef FEAT_PROFILE +static void func_do_profile(ufunc_T *fp); +static void prof_sort_list(FILE *fd, ufunc_T **sorttab, int st_len, char *title, int prefer_self); +static void prof_func_line(FILE *fd, int count, proftime_T *total, proftime_T *self, int prefer_self); +static int +# ifdef __BORLANDC__ + _RTLENTRYF +# endif + prof_total_cmp(const void *s1, const void *s2); +static int +# ifdef __BORLANDC__ + _RTLENTRYF +# endif + prof_self_cmp(const void *s1, const void *s2); +#endif + + void +func_init() +{ + hash_init(&func_hashtab); +} + +/* Get function arguments. */ + static int +get_function_args( + char_u **argp, + char_u endchar, + garray_T *newargs, + int *varargs, + int skip) +{ + int mustend = FALSE; + char_u *arg = *argp; + char_u *p = arg; + int c; + int i; + + if (newargs != NULL) + ga_init2(newargs, (int)sizeof(char_u *), 3); + + if (varargs != NULL) + *varargs = FALSE; + + /* + * Isolate the arguments: "arg1, arg2, ...)" + */ + while (*p != endchar) + { + if (p[0] == '.' && p[1] == '.' && p[2] == '.') + { + if (varargs != NULL) + *varargs = TRUE; + p += 3; + mustend = TRUE; + } + else + { + arg = p; + while (ASCII_ISALNUM(*p) || *p == '_') + ++p; + if (arg == p || isdigit(*arg) + || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0) + || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0)) + { + if (!skip) + EMSG2(_("E125: Illegal argument: %s"), arg); + break; + } + if (newargs != NULL && ga_grow(newargs, 1) == FAIL) + return FAIL; + if (newargs != NULL) + { + c = *p; + *p = NUL; + arg = vim_strsave(arg); + if (arg == NULL) + goto err_ret; + + /* Check for duplicate argument name. */ + for (i = 0; i < newargs->ga_len; ++i) + if (STRCMP(((char_u **)(newargs->ga_data))[i], arg) == 0) + { + EMSG2(_("E853: Duplicate argument name: %s"), arg); + vim_free(arg); + goto err_ret; + } + ((char_u **)(newargs->ga_data))[newargs->ga_len] = arg; + newargs->ga_len++; + + *p = c; + } + if (*p == ',') + ++p; + else + mustend = TRUE; + } + p = skipwhite(p); + if (mustend && *p != endchar) + { + if (!skip) + EMSG2(_(e_invarg2), *argp); + break; + } + } + ++p; /* skip the ')' */ + + *argp = p; + return OK; + +err_ret: + if (newargs != NULL) + ga_clear_strings(newargs); + return FAIL; +} + +/* + * Parse a lambda expression and get a Funcref from "*arg". + * Return OK or FAIL. Returns NOTDONE for dict or {expr}. + */ + int +get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) +{ + garray_T newargs; + garray_T newlines; + ufunc_T *fp = NULL; + int varargs; + int ret; + char_u name[20]; + char_u *start = skipwhite(*arg + 1); + char_u *s, *e; + static int lambda_no = 0; + + ga_init(&newargs); + ga_init(&newlines); + + /* First, check if this is a lambda expression. "->" must exist. */ + ret = get_function_args(&start, '-', NULL, NULL, TRUE); + if (ret == FAIL || *start != '>') + return NOTDONE; + + /* Parse the arguments again. */ + *arg = skipwhite(*arg + 1); + ret = get_function_args(arg, '-', &newargs, &varargs, FALSE); + if (ret == FAIL || **arg != '>') + goto errret; + + /* Get the start and the end of the expression. */ + *arg = skipwhite(*arg + 1); + s = *arg; + ret = skip_expr(arg); + if (ret == FAIL) + goto errret; + e = *arg; + *arg = skipwhite(*arg); + if (**arg != '}') + goto errret; + ++*arg; + + if (evaluate) + { + int len; + char_u *p; + + fp = (ufunc_T *)alloc((unsigned)(sizeof(ufunc_T) + 20)); + if (fp == NULL) + goto errret; + + sprintf((char*)name, "%d", ++lambda_no); + + ga_init2(&newlines, (int)sizeof(char_u *), 1); + if (ga_grow(&newlines, 1) == FAIL) + goto errret; + + /* Add "return " before the expression. + * TODO: Support multiple expressions. */ + len = 7 + e - s + 1; + p = (char_u *)alloc(len); + if (p == NULL) + goto errret; + ((char_u **)(newlines.ga_data))[newlines.ga_len++] = p; + STRCPY(p, "return "); + STRNCPY(p + 7, s, e - s); + p[7 + e - s] = NUL; + + fp->uf_refcount = 1; + STRCPY(fp->uf_name, name); + hash_add(&func_hashtab, UF2HIKEY(fp)); + fp->uf_args = newargs; + fp->uf_lines = newlines; + +#ifdef FEAT_PROFILE + fp->uf_tml_count = NULL; + fp->uf_tml_total = NULL; + fp->uf_tml_self = NULL; + fp->uf_profiling = FALSE; + if (prof_def_func()) + func_do_profile(fp); +#endif + fp->uf_varargs = TRUE; + fp->uf_flags = 0; + fp->uf_calls = 0; + fp->uf_script_ID = current_SID; + + rettv->vval.v_string = vim_strsave(name); + rettv->v_type = VAR_FUNC; + } + else + ga_clear_strings(&newargs); + + return OK; + +errret: + ga_clear_strings(&newargs); + ga_clear_strings(&newlines); + vim_free(fp); + return FAIL; +} + +/* + * Check if "name" is a variable of type VAR_FUNC. If so, return the function + * name it contains, otherwise return "name". + * If "partialp" is not NULL, and "name" is of type VAR_PARTIAL also set + * "partialp". + */ + char_u * +deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload) +{ + dictitem_T *v; + int cc; + + if (partialp != NULL) + *partialp = NULL; + + cc = name[*lenp]; + name[*lenp] = NUL; + v = find_var(name, NULL, no_autoload); + name[*lenp] = cc; + if (v != NULL && v->di_tv.v_type == VAR_FUNC) + { + if (v->di_tv.vval.v_string == NULL) + { + *lenp = 0; + return (char_u *)""; /* just in case */ + } + *lenp = (int)STRLEN(v->di_tv.vval.v_string); + return v->di_tv.vval.v_string; + } + + if (v != NULL && v->di_tv.v_type == VAR_PARTIAL) + { + partial_T *pt = v->di_tv.vval.v_partial; + + if (pt == NULL) + { + *lenp = 0; + return (char_u *)""; /* just in case */ + } + if (partialp != NULL) + *partialp = pt; + *lenp = (int)STRLEN(pt->pt_name); + return pt->pt_name; + } + + return name; +} + +/* + * Give an error message with a function name. Handle things. + * "ermsg" is to be passed without translation, use N_() instead of _(). + */ + static void +emsg_funcname(char *ermsg, char_u *name) +{ + char_u *p; + + if (*name == K_SPECIAL) + p = concat_str((char_u *)"", name + 3); + else + p = name; + EMSG2(_(ermsg), p); + if (p != name) + vim_free(p); +} + +/* + * Allocate a variable for the result of a function. + * Return OK or FAIL. + */ + int +get_func_tv( + char_u *name, /* name of the function */ + int len, /* length of "name" */ + typval_T *rettv, + char_u **arg, /* argument, pointing to the '(' */ + linenr_T firstline, /* first line of range */ + linenr_T lastline, /* last line of range */ + int *doesrange, /* return: function handled range */ + int evaluate, + partial_T *partial, /* for extra arguments */ + dict_T *selfdict) /* Dictionary for "self" */ +{ + char_u *argp; + int ret = OK; + typval_T argvars[MAX_FUNC_ARGS + 1]; /* vars for arguments */ + int argcount = 0; /* number of arguments found */ + + /* + * Get the arguments. + */ + argp = *arg; + while (argcount < MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) + { + argp = skipwhite(argp + 1); /* skip the '(' or ',' */ + if (*argp == ')' || *argp == ',' || *argp == NUL) + break; + if (eval1(&argp, &argvars[argcount], evaluate) == FAIL) + { + ret = FAIL; + break; + } + ++argcount; + if (*argp != ',') + break; + } + if (*argp == ')') + ++argp; + else + ret = FAIL; + + if (ret == OK) + { + int i = 0; + + if (get_vim_var_nr(VV_TESTING)) + { + /* Prepare for calling test_garbagecollect_now(), need to know + * what variables are used on the call stack. */ + if (funcargs.ga_itemsize == 0) + ga_init2(&funcargs, (int)sizeof(typval_T *), 50); + for (i = 0; i < argcount; ++i) + if (ga_grow(&funcargs, 1) == OK) + ((typval_T **)funcargs.ga_data)[funcargs.ga_len++] = + &argvars[i]; + } + + ret = call_func(name, len, rettv, argcount, argvars, + firstline, lastline, doesrange, evaluate, partial, selfdict); + + funcargs.ga_len -= i; + } + else if (!aborting()) + { + if (argcount == MAX_FUNC_ARGS) + emsg_funcname(N_("E740: Too many arguments for function %s"), name); + else + emsg_funcname(N_("E116: Invalid arguments for function %s"), name); + } + + while (--argcount >= 0) + clear_tv(&argvars[argcount]); + + *arg = skipwhite(argp); + return ret; +} + +#define FLEN_FIXED 40 + +/* + * Return TRUE if "p" starts with "" or "s:". + * Only works if eval_fname_script() returned non-zero for "p"! + */ + static int +eval_fname_sid(char_u *p) +{ + return (*p == 's' || TOUPPER_ASC(p[2]) == 'I'); +} + +/* + * In a script change name() and s:name() to K_SNR 123_name(). + * Change 123_name() to K_SNR 123_name(). + * Use "fname_buf[FLEN_FIXED + 1]" when it fits, otherwise allocate memory + * (slow). + */ + static char_u * +fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error) +{ + int llen; + char_u *fname; + int i; + + llen = eval_fname_script(name); + if (llen > 0) + { + fname_buf[0] = K_SPECIAL; + fname_buf[1] = KS_EXTRA; + fname_buf[2] = (int)KE_SNR; + i = 3; + if (eval_fname_sid(name)) /* "" or "s:" */ + { + if (current_SID <= 0) + *error = ERROR_SCRIPT; + else + { + sprintf((char *)fname_buf + 3, "%ld_", (long)current_SID); + i = (int)STRLEN(fname_buf); + } + } + if (i + STRLEN(name + llen) < FLEN_FIXED) + { + STRCPY(fname_buf + i, name + llen); + fname = fname_buf; + } + else + { + fname = alloc((unsigned)(i + STRLEN(name + llen) + 1)); + if (fname == NULL) + *error = ERROR_OTHER; + else + { + *tofree = fname; + mch_memmove(fname, fname_buf, (size_t)i); + STRCPY(fname + i, name + llen); + } + } + } + else + fname = name; + return fname; +} + +/* + * Find a function by name, return pointer to it in ufuncs. + * Return NULL for unknown function. + */ + static ufunc_T * +find_func(char_u *name) +{ + hashitem_T *hi; + + hi = hash_find(&func_hashtab, name); + if (!HASHITEM_EMPTY(hi)) + return HI2UF(hi); + return NULL; +} + +/* + * Copy the function name of "fp" to buffer "buf". + * "buf" must be able to hold the function name plus three bytes. + * Takes care of script-local function names. + */ + static void +cat_func_name(char_u *buf, ufunc_T *fp) +{ + if (fp->uf_name[0] == K_SPECIAL) + { + STRCPY(buf, ""); + STRCAT(buf, fp->uf_name + 3); + } + else + STRCPY(buf, fp->uf_name); +} + +/* + * Add a number variable "name" to dict "dp" with value "nr". + */ + static void +add_nr_var( + dict_T *dp, + dictitem_T *v, + char *name, + varnumber_T nr) +{ + STRCPY(v->di_key, name); + v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; + hash_add(&dp->dv_hashtab, DI2HIKEY(v)); + v->di_tv.v_type = VAR_NUMBER; + v->di_tv.v_lock = VAR_FIXED; + v->di_tv.vval.v_number = nr; +} + +/* + * Free "fc" and what it contains. + */ + static void +free_funccal( + funccall_T *fc, + int free_val) /* a: vars were allocated */ +{ + listitem_T *li; + + /* The a: variables typevals may not have been allocated, only free the + * allocated variables. */ + vars_clear_ext(&fc->l_avars.dv_hashtab, free_val); + + /* free all l: variables */ + vars_clear(&fc->l_vars.dv_hashtab); + + /* Free the a:000 variables if they were allocated. */ + if (free_val) + for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) + clear_tv(&li->li_tv); + + vim_free(fc); +} + +/* + * Call a user function. + */ + static void +call_user_func( + ufunc_T *fp, /* pointer to function */ + int argcount, /* nr of args */ + typval_T *argvars, /* arguments */ + typval_T *rettv, /* return value */ + linenr_T firstline, /* first line of range */ + linenr_T lastline, /* last line of range */ + dict_T *selfdict) /* Dictionary for "self" */ +{ + char_u *save_sourcing_name; + linenr_T save_sourcing_lnum; + scid_T save_current_SID; + funccall_T *fc; + int save_did_emsg; + static int depth = 0; + dictitem_T *v; + int fixvar_idx = 0; /* index in fixvar[] */ + int i; + int ai; + int islambda = FALSE; + char_u numbuf[NUMBUFLEN]; + char_u *name; + size_t len; +#ifdef FEAT_PROFILE + proftime_T wait_start; + proftime_T call_start; +#endif + + /* If depth of calling is getting too high, don't execute the function */ + if (depth >= p_mfd) + { + EMSG(_("E132: Function call depth is higher than 'maxfuncdepth'")); + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = -1; + return; + } + ++depth; + + line_breakcheck(); /* check for CTRL-C hit */ + + fc = (funccall_T *)alloc(sizeof(funccall_T)); + fc->caller = current_funccal; + current_funccal = fc; + fc->func = fp; + fc->rettv = rettv; + rettv->vval.v_number = 0; + fc->linenr = 0; + fc->returned = FALSE; + fc->level = ex_nesting_level; + /* Check if this function has a breakpoint. */ + fc->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, (linenr_T)0); + fc->dbg_tick = debug_tick; + + if (STRNCMP(fp->uf_name, "", 8) == 0) + islambda = TRUE; + + /* + * Note about using fc->fixvar[]: This is an array of FIXVAR_CNT variables + * with names up to VAR_SHORT_LEN long. This avoids having to alloc/free + * each argument variable and saves a lot of time. + */ + /* + * Init l: variables. + */ + init_var_dict(&fc->l_vars, &fc->l_vars_var, VAR_DEF_SCOPE); + if (selfdict != NULL) + { + /* Set l:self to "selfdict". Use "name" to avoid a warning from + * some compiler that checks the destination size. */ + v = &fc->fixvar[fixvar_idx++].var; + name = v->di_key; + STRCPY(name, "self"); + v->di_flags = DI_FLAGS_RO + DI_FLAGS_FIX; + hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v)); + v->di_tv.v_type = VAR_DICT; + v->di_tv.v_lock = 0; + v->di_tv.vval.v_dict = selfdict; + ++selfdict->dv_refcount; + } + + /* + * Init a: variables. + * Set a:0 to "argcount". + * Set a:000 to a list with room for the "..." arguments. + */ + init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE); + add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "0", + (varnumber_T)(argcount - fp->uf_args.ga_len)); + /* Use "name" to avoid a warning from some compiler that checks the + * destination size. */ + v = &fc->fixvar[fixvar_idx++].var; + name = v->di_key; + STRCPY(name, "000"); + v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; + hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v)); + v->di_tv.v_type = VAR_LIST; + v->di_tv.v_lock = VAR_FIXED; + v->di_tv.vval.v_list = &fc->l_varlist; + vim_memset(&fc->l_varlist, 0, sizeof(list_T)); + fc->l_varlist.lv_refcount = DO_NOT_FREE_CNT; + fc->l_varlist.lv_lock = VAR_FIXED; + + /* + * Set a:firstline to "firstline" and a:lastline to "lastline". + * Set a:name to named arguments. + * Set a:N to the "..." arguments. + */ + add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "firstline", + (varnumber_T)firstline); + add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "lastline", + (varnumber_T)lastline); + for (i = 0; i < argcount; ++i) + { + int addlocal = FALSE; + dictitem_T *v2; + + ai = i - fp->uf_args.ga_len; + if (ai < 0) + { + /* named argument a:name */ + name = FUNCARG(fp, i); + if (islambda) + addlocal = TRUE; + } + else + { + /* "..." argument a:1, a:2, etc. */ + sprintf((char *)numbuf, "%d", ai + 1); + name = numbuf; + } + if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN) + { + v = &fc->fixvar[fixvar_idx++].var; + v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; + + if (addlocal) + v2 = v; + } + else + { + v = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T) + + STRLEN(name))); + if (v == NULL) + break; + v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC; + + if (addlocal) + { + v2 = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T) + + STRLEN(name))); + if (v2 == NULL) + { + vim_free(v); + break; + } + v2->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC; + } + } + STRCPY(v->di_key, name); + hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v)); + + /* Note: the values are copied directly to avoid alloc/free. + * "argvars" must have VAR_FIXED for v_lock. */ + v->di_tv = argvars[i]; + v->di_tv.v_lock = VAR_FIXED; + + /* Named arguments can be accessed without the "a:" prefix in lambda + * expressions. Add to the l: dict. */ + if (addlocal) + { + STRCPY(v2->di_key, name); + copy_tv(&v->di_tv, &v2->di_tv); + v2->di_tv.v_lock = VAR_FIXED; + hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v2)); + } + + if (ai >= 0 && ai < MAX_FUNC_ARGS) + { + list_append(&fc->l_varlist, &fc->l_listitems[ai]); + fc->l_listitems[ai].li_tv = argvars[i]; + fc->l_listitems[ai].li_tv.v_lock = VAR_FIXED; + } + } + + /* Don't redraw while executing the function. */ + ++RedrawingDisabled; + save_sourcing_name = sourcing_name; + save_sourcing_lnum = sourcing_lnum; + sourcing_lnum = 1; + /* need space for function name + ("function " + 3) or "[number]" */ + len = (save_sourcing_name == NULL ? 0 : STRLEN(save_sourcing_name)) + + STRLEN(fp->uf_name) + 20; + sourcing_name = alloc((unsigned)len); + if (sourcing_name != NULL) + { + if (save_sourcing_name != NULL + && STRNCMP(save_sourcing_name, "function ", 9) == 0) + sprintf((char *)sourcing_name, "%s[%d]..", + save_sourcing_name, (int)save_sourcing_lnum); + else + STRCPY(sourcing_name, "function "); + cat_func_name(sourcing_name + STRLEN(sourcing_name), fp); + + if (p_verbose >= 12) + { + ++no_wait_return; + verbose_enter_scroll(); + + smsg((char_u *)_("calling %s"), sourcing_name); + if (p_verbose >= 14) + { + char_u buf[MSG_BUF_LEN]; + char_u numbuf2[NUMBUFLEN]; + char_u *tofree; + char_u *s; + + msg_puts((char_u *)"("); + for (i = 0; i < argcount; ++i) + { + if (i > 0) + msg_puts((char_u *)", "); + if (argvars[i].v_type == VAR_NUMBER) + msg_outnum((long)argvars[i].vval.v_number); + else + { + /* Do not want errors such as E724 here. */ + ++emsg_off; + s = tv2string(&argvars[i], &tofree, numbuf2, 0); + --emsg_off; + if (s != NULL) + { + if (vim_strsize(s) > MSG_BUF_CLEN) + { + trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN); + s = buf; + } + msg_puts(s); + vim_free(tofree); + } + } + } + msg_puts((char_u *)")"); + } + msg_puts((char_u *)"\n"); /* don't overwrite this either */ + + verbose_leave_scroll(); + --no_wait_return; + } + } +#ifdef FEAT_PROFILE + if (do_profiling == PROF_YES) + { + if (!fp->uf_profiling && has_profiling(FALSE, fp->uf_name, NULL)) + func_do_profile(fp); + if (fp->uf_profiling + || (fc->caller != NULL && fc->caller->func->uf_profiling)) + { + ++fp->uf_tm_count; + profile_start(&call_start); + profile_zero(&fp->uf_tm_children); + } + script_prof_save(&wait_start); + } +#endif + + save_current_SID = current_SID; + current_SID = fp->uf_script_ID; + save_did_emsg = did_emsg; + did_emsg = FALSE; + + /* call do_cmdline() to execute the lines */ + do_cmdline(NULL, get_func_line, (void *)fc, + DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT); + + --RedrawingDisabled; + + /* when the function was aborted because of an error, return -1 */ + if ((did_emsg && (fp->uf_flags & FC_ABORT)) || rettv->v_type == VAR_UNKNOWN) + { + clear_tv(rettv); + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = -1; + } + +#ifdef FEAT_PROFILE + if (do_profiling == PROF_YES && (fp->uf_profiling + || (fc->caller != NULL && fc->caller->func->uf_profiling))) + { + profile_end(&call_start); + profile_sub_wait(&wait_start, &call_start); + profile_add(&fp->uf_tm_total, &call_start); + profile_self(&fp->uf_tm_self, &call_start, &fp->uf_tm_children); + if (fc->caller != NULL && fc->caller->func->uf_profiling) + { + profile_add(&fc->caller->func->uf_tm_children, &call_start); + profile_add(&fc->caller->func->uf_tml_children, &call_start); + } + } +#endif + + /* when being verbose, mention the return value */ + if (p_verbose >= 12) + { + ++no_wait_return; + verbose_enter_scroll(); + + if (aborting()) + smsg((char_u *)_("%s aborted"), sourcing_name); + else if (fc->rettv->v_type == VAR_NUMBER) + smsg((char_u *)_("%s returning #%ld"), sourcing_name, + (long)fc->rettv->vval.v_number); + else + { + char_u buf[MSG_BUF_LEN]; + char_u numbuf2[NUMBUFLEN]; + char_u *tofree; + char_u *s; + + /* The value may be very long. Skip the middle part, so that we + * have some idea how it starts and ends. smsg() would always + * truncate it at the end. Don't want errors such as E724 here. */ + ++emsg_off; + s = tv2string(fc->rettv, &tofree, numbuf2, 0); + --emsg_off; + if (s != NULL) + { + if (vim_strsize(s) > MSG_BUF_CLEN) + { + trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN); + s = buf; + } + smsg((char_u *)_("%s returning %s"), sourcing_name, s); + vim_free(tofree); + } + } + msg_puts((char_u *)"\n"); /* don't overwrite this either */ + + verbose_leave_scroll(); + --no_wait_return; + } + + vim_free(sourcing_name); + sourcing_name = save_sourcing_name; + sourcing_lnum = save_sourcing_lnum; + current_SID = save_current_SID; +#ifdef FEAT_PROFILE + if (do_profiling == PROF_YES) + script_prof_restore(&wait_start); +#endif + + if (p_verbose >= 12 && sourcing_name != NULL) + { + ++no_wait_return; + verbose_enter_scroll(); + + smsg((char_u *)_("continuing in %s"), sourcing_name); + msg_puts((char_u *)"\n"); /* don't overwrite this either */ + + verbose_leave_scroll(); + --no_wait_return; + } + + did_emsg |= save_did_emsg; + current_funccal = fc->caller; + --depth; + + /* If the a:000 list and the l: and a: dicts are not referenced we can + * free the funccall_T and what's in it. */ + if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT + && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT + && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT) + { + free_funccal(fc, FALSE); + } + else + { + hashitem_T *hi; + listitem_T *li; + int todo; + + /* "fc" is still in use. This can happen when returning "a:000" or + * assigning "l:" to a global variable. + * Link "fc" in the list for garbage collection later. */ + fc->caller = previous_funccal; + previous_funccal = fc; + + /* Make a copy of the a: variables, since we didn't do that above. */ + todo = (int)fc->l_avars.dv_hashtab.ht_used; + for (hi = fc->l_avars.dv_hashtab.ht_array; todo > 0; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + --todo; + v = HI2DI(hi); + copy_tv(&v->di_tv, &v->di_tv); + } + } + + /* Make a copy of the a:000 items, since we didn't do that above. */ + for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) + copy_tv(&li->li_tv, &li->li_tv); + } +} + +/* + * Free a function and remove it from the list of functions. + */ + static void +func_free(ufunc_T *fp) +{ + hashitem_T *hi; + + /* clear this function */ + ga_clear_strings(&(fp->uf_args)); + ga_clear_strings(&(fp->uf_lines)); +#ifdef FEAT_PROFILE + vim_free(fp->uf_tml_count); + vim_free(fp->uf_tml_total); + vim_free(fp->uf_tml_self); +#endif + + /* remove the function from the function hashtable */ + hi = hash_find(&func_hashtab, UF2HIKEY(fp)); + if (HASHITEM_EMPTY(hi)) + EMSG2(_(e_intern2), "func_free()"); + else + hash_remove(&func_hashtab, hi); + + vim_free(fp); +} + +#if defined(EXITFREE) || defined(PROTO) + void +free_all_functions(void) +{ + hashitem_T *hi; + + /* Need to start all over every time, because func_free() may change the + * hash table. */ + while (func_hashtab.ht_used > 0) + for (hi = func_hashtab.ht_array; ; ++hi) + if (!HASHITEM_EMPTY(hi)) + { + func_free(HI2UF(hi)); + break; + } + hash_clear(&func_hashtab); +} +#endif + +/* + * Return TRUE if "name" looks like a builtin function name: starts with a + * lower case letter and doesn't contain AUTOLOAD_CHAR. + * "len" is the length of "name", or -1 for NUL terminated. + */ + static int +builtin_function(char_u *name, int len) +{ + char_u *p; + + if (!ASCII_ISLOWER(name[0])) + return FALSE; + p = vim_strchr(name, AUTOLOAD_CHAR); + return p == NULL || (len > 0 && p > name + len); +} + + int +func_call( + char_u *name, + typval_T *args, + partial_T *partial, + dict_T *selfdict, + typval_T *rettv) +{ + listitem_T *item; + typval_T argv[MAX_FUNC_ARGS + 1]; + int argc = 0; + int dummy; + int r = 0; + + for (item = args->vval.v_list->lv_first; item != NULL; + item = item->li_next) + { + if (argc == MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) + { + EMSG(_("E699: Too many arguments")); + break; + } + /* Make a copy of each argument. This is needed to be able to set + * v_lock to VAR_FIXED in the copy without changing the original list. + */ + copy_tv(&item->li_tv, &argv[argc++]); + } + + if (item == NULL) + r = call_func(name, (int)STRLEN(name), rettv, argc, argv, + curwin->w_cursor.lnum, curwin->w_cursor.lnum, + &dummy, TRUE, partial, selfdict); + + /* Free the arguments. */ + while (argc > 0) + clear_tv(&argv[--argc]); + + return r; +} + +/* + * Call a function with its resolved parameters + * Return FAIL when the function can't be called, OK otherwise. + * Also returns OK when an error was encountered while executing the function. + */ + int +call_func( + char_u *funcname, /* name of the function */ + int len, /* length of "name" */ + typval_T *rettv, /* return value goes here */ + int argcount_in, /* number of "argvars" */ + typval_T *argvars_in, /* vars for arguments, must have "argcount" + PLUS ONE elements! */ + linenr_T firstline, /* first line of range */ + linenr_T lastline, /* last line of range */ + int *doesrange, /* return: function handled range */ + int evaluate, + partial_T *partial, /* optional, can be NULL */ + dict_T *selfdict_in) /* Dictionary for "self" */ +{ + int ret = FAIL; + int error = ERROR_NONE; + int i; + ufunc_T *fp; + char_u fname_buf[FLEN_FIXED + 1]; + char_u *tofree = NULL; + char_u *fname; + char_u *name; + int argcount = argcount_in; + typval_T *argvars = argvars_in; + dict_T *selfdict = selfdict_in; + typval_T argv[MAX_FUNC_ARGS + 1]; /* used when "partial" is not NULL */ + int argv_clear = 0; + + /* Make a copy of the name, if it comes from a funcref variable it could + * be changed or deleted in the called function. */ + name = vim_strnsave(funcname, len); + if (name == NULL) + return ret; + + fname = fname_trans_sid(name, fname_buf, &tofree, &error); + + *doesrange = FALSE; + + if (partial != NULL) + { + /* When the function has a partial with a dict and there is a dict + * argument, use the dict argument. That is backwards compatible. + * When the dict was bound explicitly use the one from the partial. */ + if (partial->pt_dict != NULL + && (selfdict_in == NULL || !partial->pt_auto)) + selfdict = partial->pt_dict; + if (error == ERROR_NONE && partial->pt_argc > 0) + { + for (argv_clear = 0; argv_clear < partial->pt_argc; ++argv_clear) + copy_tv(&partial->pt_argv[argv_clear], &argv[argv_clear]); + for (i = 0; i < argcount_in; ++i) + argv[i + argv_clear] = argvars_in[i]; + argvars = argv; + argcount = partial->pt_argc + argcount_in; + } + } + + + /* execute the function if no errors detected and executing */ + if (evaluate && error == ERROR_NONE) + { + char_u *rfname = fname; + + /* Ignore "g:" before a function name. */ + if (fname[0] == 'g' && fname[1] == ':') + rfname = fname + 2; + + rettv->v_type = VAR_NUMBER; /* default rettv is number zero */ + rettv->vval.v_number = 0; + error = ERROR_UNKNOWN; + + if (!builtin_function(rfname, -1)) + { + /* + * User defined function. + */ + fp = find_func(rfname); + +#ifdef FEAT_AUTOCMD + /* Trigger FuncUndefined event, may load the function. */ + if (fp == NULL + && apply_autocmds(EVENT_FUNCUNDEFINED, + rfname, rfname, TRUE, NULL) + && !aborting()) + { + /* executed an autocommand, search for the function again */ + fp = find_func(rfname); + } +#endif + /* Try loading a package. */ + if (fp == NULL && script_autoload(rfname, TRUE) && !aborting()) + { + /* loaded a package, search for the function again */ + fp = find_func(rfname); + } + + if (fp != NULL) + { + if (fp->uf_flags & FC_RANGE) + *doesrange = TRUE; + if (argcount < fp->uf_args.ga_len) + error = ERROR_TOOFEW; + else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len) + error = ERROR_TOOMANY; + else if ((fp->uf_flags & FC_DICT) && selfdict == NULL) + error = ERROR_DICT; + else + { + int did_save_redo = FALSE; + + /* + * Call the user function. + * Save and restore search patterns, script variables and + * redo buffer. + */ + save_search_patterns(); +#ifdef FEAT_INS_EXPAND + if (!ins_compl_active()) +#endif + { + saveRedobuff(); + did_save_redo = TRUE; + } + ++fp->uf_calls; + call_user_func(fp, argcount, argvars, rettv, + firstline, lastline, + (fp->uf_flags & FC_DICT) ? selfdict : NULL); + if (--fp->uf_calls <= 0 && (isdigit(*fp->uf_name) + || STRNCMP(fp->uf_name, "", 8) == 0) + && fp->uf_refcount <= 0) + /* Function was unreferenced while being used, free it + * now. */ + func_free(fp); + if (did_save_redo) + restoreRedobuff(); + restore_search_patterns(); + error = ERROR_NONE; + } + } + } + else + { + /* + * Find the function name in the table, call its implementation. + */ + error = call_internal_func(fname, argcount, argvars, rettv); + } + /* + * The function call (or "FuncUndefined" autocommand sequence) might + * have been aborted by an error, an interrupt, or an explicitly thrown + * exception that has not been caught so far. This situation can be + * tested for by calling aborting(). For an error in an internal + * function or for the "E132" error in call_user_func(), however, the + * throw point at which the "force_abort" flag (temporarily reset by + * emsg()) is normally updated has not been reached yet. We need to + * update that flag first to make aborting() reliable. + */ + update_force_abort(); + } + if (error == ERROR_NONE) + ret = OK; + + /* + * Report an error unless the argument evaluation or function call has been + * cancelled due to an aborting error, an interrupt, or an exception. + */ + if (!aborting()) + { + switch (error) + { + case ERROR_UNKNOWN: + emsg_funcname(N_("E117: Unknown function: %s"), name); + break; + case ERROR_TOOMANY: + emsg_funcname((char *)e_toomanyarg, name); + break; + case ERROR_TOOFEW: + emsg_funcname(N_("E119: Not enough arguments for function: %s"), + name); + break; + case ERROR_SCRIPT: + emsg_funcname(N_("E120: Using not in a script context: %s"), + name); + break; + case ERROR_DICT: + emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"), + name); + break; + } + } + + while (argv_clear > 0) + clear_tv(&argv[--argv_clear]); + vim_free(tofree); + vim_free(name); + + return ret; +} + +/* + * List the head of the function: "name(arg1, arg2)". + */ + static void +list_func_head(ufunc_T *fp, int indent) +{ + int j; + + msg_start(); + if (indent) + MSG_PUTS(" "); + MSG_PUTS("function "); + if (fp->uf_name[0] == K_SPECIAL) + { + MSG_PUTS_ATTR("", hl_attr(HLF_8)); + msg_puts(fp->uf_name + 3); + } + else + msg_puts(fp->uf_name); + msg_putchar('('); + for (j = 0; j < fp->uf_args.ga_len; ++j) + { + if (j) + MSG_PUTS(", "); + msg_puts(FUNCARG(fp, j)); + } + if (fp->uf_varargs) + { + if (j) + MSG_PUTS(", "); + MSG_PUTS("..."); + } + msg_putchar(')'); + if (fp->uf_flags & FC_ABORT) + MSG_PUTS(" abort"); + if (fp->uf_flags & FC_RANGE) + MSG_PUTS(" range"); + if (fp->uf_flags & FC_DICT) + MSG_PUTS(" dict"); + msg_clr_eos(); + if (p_verbose > 0) + last_set_msg(fp->uf_script_ID); +} + +/* + * Get a function name, translating "" and "". + * Also handles a Funcref in a List or Dictionary. + * Returns the function name in allocated memory, or NULL for failure. + * flags: + * TFN_INT: internal function name OK + * TFN_QUIET: be quiet + * TFN_NO_AUTOLOAD: do not use script autoloading + * Advances "pp" to just after the function name (if no error). + */ + static char_u * +trans_function_name( + char_u **pp, + int skip, /* only find the end, don't evaluate */ + int flags, + funcdict_T *fdp, /* return: info about dictionary used */ + partial_T **partial) /* return: partial of a FuncRef */ +{ + char_u *name = NULL; + char_u *start; + char_u *end; + int lead; + char_u sid_buf[20]; + int len; + lval_T lv; + + if (fdp != NULL) + vim_memset(fdp, 0, sizeof(funcdict_T)); + start = *pp; + + /* Check for hard coded : already translated function ID (from a user + * command). */ + if ((*pp)[0] == K_SPECIAL && (*pp)[1] == KS_EXTRA + && (*pp)[2] == (int)KE_SNR) + { + *pp += 3; + len = get_id_len(pp) + 3; + return vim_strnsave(start, len); + } + + /* A name starting with "" or "" is local to a script. But + * don't skip over "s:", get_lval() needs it for "s:dict.func". */ + lead = eval_fname_script(start); + if (lead > 2) + start += lead; + + /* Note that TFN_ flags use the same values as GLV_ flags. */ + end = get_lval(start, NULL, &lv, FALSE, skip, flags, + lead > 2 ? 0 : FNE_CHECK_START); + if (end == start) + { + if (!skip) + EMSG(_("E129: Function name required")); + goto theend; + } + if (end == NULL || (lv.ll_tv != NULL && (lead > 2 || lv.ll_range))) + { + /* + * Report an invalid expression in braces, unless the expression + * evaluation has been cancelled due to an aborting error, an + * interrupt, or an exception. + */ + if (!aborting()) + { + if (end != NULL) + EMSG2(_(e_invarg2), start); + } + else + *pp = find_name_end(start, NULL, NULL, FNE_INCL_BR); + goto theend; + } + + if (lv.ll_tv != NULL) + { + if (fdp != NULL) + { + fdp->fd_dict = lv.ll_dict; + fdp->fd_newkey = lv.ll_newkey; + lv.ll_newkey = NULL; + fdp->fd_di = lv.ll_di; + } + if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) + { + name = vim_strsave(lv.ll_tv->vval.v_string); + *pp = end; + } + else if (lv.ll_tv->v_type == VAR_PARTIAL + && lv.ll_tv->vval.v_partial != NULL) + { + name = vim_strsave(lv.ll_tv->vval.v_partial->pt_name); + *pp = end; + if (partial != NULL) + *partial = lv.ll_tv->vval.v_partial; + } + else + { + if (!skip && !(flags & TFN_QUIET) && (fdp == NULL + || lv.ll_dict == NULL || fdp->fd_newkey == NULL)) + EMSG(_(e_funcref)); + else + *pp = end; + name = NULL; + } + goto theend; + } + + if (lv.ll_name == NULL) + { + /* Error found, but continue after the function name. */ + *pp = end; + goto theend; + } + + /* Check if the name is a Funcref. If so, use the value. */ + if (lv.ll_exp_name != NULL) + { + len = (int)STRLEN(lv.ll_exp_name); + name = deref_func_name(lv.ll_exp_name, &len, partial, + flags & TFN_NO_AUTOLOAD); + if (name == lv.ll_exp_name) + name = NULL; + } + else + { + len = (int)(end - *pp); + name = deref_func_name(*pp, &len, partial, flags & TFN_NO_AUTOLOAD); + if (name == *pp) + name = NULL; + } + if (name != NULL) + { + name = vim_strsave(name); + *pp = end; + if (STRNCMP(name, "", 5) == 0) + { + /* Change "" to the byte sequence. */ + name[0] = K_SPECIAL; + name[1] = KS_EXTRA; + name[2] = (int)KE_SNR; + mch_memmove(name + 3, name + 5, STRLEN(name + 5) + 1); + } + goto theend; + } + + if (lv.ll_exp_name != NULL) + { + len = (int)STRLEN(lv.ll_exp_name); + if (lead <= 2 && lv.ll_name == lv.ll_exp_name + && STRNCMP(lv.ll_name, "s:", 2) == 0) + { + /* When there was "s:" already or the name expanded to get a + * leading "s:" then remove it. */ + lv.ll_name += 2; + len -= 2; + lead = 2; + } + } + else + { + /* skip over "s:" and "g:" */ + if (lead == 2 || (lv.ll_name[0] == 'g' && lv.ll_name[1] == ':')) + lv.ll_name += 2; + len = (int)(end - lv.ll_name); + } + + /* + * Copy the function name to allocated memory. + * Accept name() inside a script, translate into 123_name(). + * Accept 123_name() outside a script. + */ + if (skip) + lead = 0; /* do nothing */ + else if (lead > 0) + { + lead = 3; + if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name)) + || eval_fname_sid(*pp)) + { + /* It's "s:" or "" */ + if (current_SID <= 0) + { + EMSG(_(e_usingsid)); + goto theend; + } + sprintf((char *)sid_buf, "%ld_", (long)current_SID); + lead += (int)STRLEN(sid_buf); + } + } + else if (!(flags & TFN_INT) && builtin_function(lv.ll_name, len)) + { + EMSG2(_("E128: Function name must start with a capital or \"s:\": %s"), + start); + goto theend; + } + if (!skip && !(flags & TFN_QUIET)) + { + char_u *cp = vim_strchr(lv.ll_name, ':'); + + if (cp != NULL && cp < end) + { + EMSG2(_("E884: Function name cannot contain a colon: %s"), start); + goto theend; + } + } + + name = alloc((unsigned)(len + lead + 1)); + if (name != NULL) + { + if (lead > 0) + { + name[0] = K_SPECIAL; + name[1] = KS_EXTRA; + name[2] = (int)KE_SNR; + if (lead > 3) /* If it's "" */ + STRCPY(name + 3, sid_buf); + } + mch_memmove(name + lead, lv.ll_name, (size_t)len); + name[lead + len] = NUL; + } + *pp = end; + +theend: + clear_lval(&lv); + return name; +} + +/* + * ":function" + */ + void +ex_function(exarg_T *eap) +{ + char_u *theline; + int j; + int c; + int saved_did_emsg; + int saved_wait_return = need_wait_return; + char_u *name = NULL; + char_u *p; + char_u *arg; + char_u *line_arg = NULL; + garray_T newargs; + garray_T newlines; + int varargs = FALSE; + int flags = 0; + ufunc_T *fp; + int indent; + int nesting; + char_u *skip_until = NULL; + dictitem_T *v; + funcdict_T fudi; + static int func_nr = 0; /* number for nameless function */ + int paren; + hashtab_T *ht; + int todo; + hashitem_T *hi; + int sourcing_lnum_off; + + /* + * ":function" without argument: list functions. + */ + if (ends_excmd(*eap->arg)) + { + if (!eap->skip) + { + todo = (int)func_hashtab.ht_used; + for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + --todo; + fp = HI2UF(hi); + if (!isdigit(*fp->uf_name)) + list_func_head(fp, FALSE); + } + } + } + eap->nextcmd = check_nextcmd(eap->arg); + return; + } + + /* + * ":function /pat": list functions matching pattern. + */ + if (*eap->arg == '/') + { + p = skip_regexp(eap->arg + 1, '/', TRUE, NULL); + if (!eap->skip) + { + regmatch_T regmatch; + + c = *p; + *p = NUL; + regmatch.regprog = vim_regcomp(eap->arg + 1, RE_MAGIC); + *p = c; + if (regmatch.regprog != NULL) + { + regmatch.rm_ic = p_ic; + + todo = (int)func_hashtab.ht_used; + for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + --todo; + fp = HI2UF(hi); + if (!isdigit(*fp->uf_name) + && vim_regexec(®match, fp->uf_name, 0)) + list_func_head(fp, FALSE); + } + } + vim_regfree(regmatch.regprog); + } + } + if (*p == '/') + ++p; + eap->nextcmd = check_nextcmd(p); + return; + } + + /* + * Get the function name. There are these situations: + * func normal function name + * "name" == func, "fudi.fd_dict" == NULL + * dict.func new dictionary entry + * "name" == NULL, "fudi.fd_dict" set, + * "fudi.fd_di" == NULL, "fudi.fd_newkey" == func + * dict.func existing dict entry with a Funcref + * "name" == func, "fudi.fd_dict" set, + * "fudi.fd_di" set, "fudi.fd_newkey" == NULL + * dict.func existing dict entry that's not a Funcref + * "name" == NULL, "fudi.fd_dict" set, + * "fudi.fd_di" set, "fudi.fd_newkey" == NULL + * s:func script-local function name + * g:func global function name, same as "func" + */ + p = eap->arg; + name = trans_function_name(&p, eap->skip, 0, &fudi, NULL); + paren = (vim_strchr(p, '(') != NULL); + if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip) + { + /* + * Return on an invalid expression in braces, unless the expression + * evaluation has been cancelled due to an aborting error, an + * interrupt, or an exception. + */ + if (!aborting()) + { + if (!eap->skip && fudi.fd_newkey != NULL) + EMSG2(_(e_dictkey), fudi.fd_newkey); + vim_free(fudi.fd_newkey); + return; + } + else + eap->skip = TRUE; + } + + /* An error in a function call during evaluation of an expression in magic + * braces should not cause the function not to be defined. */ + saved_did_emsg = did_emsg; + did_emsg = FALSE; + + /* + * ":function func" with only function name: list function. + */ + if (!paren) + { + if (!ends_excmd(*skipwhite(p))) + { + EMSG(_(e_trailing)); + goto ret_free; + } + eap->nextcmd = check_nextcmd(p); + if (eap->nextcmd != NULL) + *p = NUL; + if (!eap->skip && !got_int) + { + fp = find_func(name); + if (fp != NULL) + { + list_func_head(fp, TRUE); + for (j = 0; j < fp->uf_lines.ga_len && !got_int; ++j) + { + if (FUNCLINE(fp, j) == NULL) + continue; + msg_putchar('\n'); + msg_outnum((long)(j + 1)); + if (j < 9) + msg_putchar(' '); + if (j < 99) + msg_putchar(' '); + msg_prt_line(FUNCLINE(fp, j), FALSE); + out_flush(); /* show a line at a time */ + ui_breakcheck(); + } + if (!got_int) + { + msg_putchar('\n'); + msg_puts((char_u *)" endfunction"); + } + } + else + emsg_funcname(N_("E123: Undefined function: %s"), name); + } + goto ret_free; + } + + /* + * ":function name(arg1, arg2)" Define function. + */ + p = skipwhite(p); + if (*p != '(') + { + if (!eap->skip) + { + EMSG2(_("E124: Missing '(': %s"), eap->arg); + goto ret_free; + } + /* attempt to continue by skipping some text */ + if (vim_strchr(p, '(') != NULL) + p = vim_strchr(p, '('); + } + p = skipwhite(p + 1); + + ga_init2(&newlines, (int)sizeof(char_u *), 3); + + if (!eap->skip) + { + /* Check the name of the function. Unless it's a dictionary function + * (that we are overwriting). */ + if (name != NULL) + arg = name; + else + arg = fudi.fd_newkey; + if (arg != NULL && (fudi.fd_di == NULL + || (fudi.fd_di->di_tv.v_type != VAR_FUNC + && fudi.fd_di->di_tv.v_type != VAR_PARTIAL))) + { + if (*arg == K_SPECIAL) + j = 3; + else + j = 0; + while (arg[j] != NUL && (j == 0 ? eval_isnamec1(arg[j]) + : eval_isnamec(arg[j]))) + ++j; + if (arg[j] != NUL) + emsg_funcname((char *)e_invarg2, arg); + } + /* Disallow using the g: dict. */ + if (fudi.fd_dict != NULL && fudi.fd_dict->dv_scope == VAR_DEF_SCOPE) + EMSG(_("E862: Cannot use g: here")); + } + + if (get_function_args(&p, ')', &newargs, &varargs, eap->skip) == FAIL) + goto errret_2; + + /* find extra arguments "range", "dict" and "abort" */ + for (;;) + { + p = skipwhite(p); + if (STRNCMP(p, "range", 5) == 0) + { + flags |= FC_RANGE; + p += 5; + } + else if (STRNCMP(p, "dict", 4) == 0) + { + flags |= FC_DICT; + p += 4; + } + else if (STRNCMP(p, "abort", 5) == 0) + { + flags |= FC_ABORT; + p += 5; + } + else + break; + } + + /* When there is a line break use what follows for the function body. + * Makes 'exe "func Test()\n...\nendfunc"' work. */ + if (*p == '\n') + line_arg = p + 1; + else if (*p != NUL && *p != '"' && !eap->skip && !did_emsg) + EMSG(_(e_trailing)); + + /* + * Read the body of the function, until ":endfunction" is found. + */ + if (KeyTyped) + { + /* Check if the function already exists, don't let the user type the + * whole function before telling him it doesn't work! For a script we + * need to skip the body to be able to find what follows. */ + if (!eap->skip && !eap->forceit) + { + if (fudi.fd_dict != NULL && fudi.fd_newkey == NULL) + EMSG(_(e_funcdict)); + else if (name != NULL && find_func(name) != NULL) + emsg_funcname(e_funcexts, name); + } + + if (!eap->skip && did_emsg) + goto erret; + + msg_putchar('\n'); /* don't overwrite the function name */ + cmdline_row = msg_row; + } + + indent = 2; + nesting = 0; + for (;;) + { + if (KeyTyped) + { + msg_scroll = TRUE; + saved_wait_return = FALSE; + } + need_wait_return = FALSE; + sourcing_lnum_off = sourcing_lnum; + + if (line_arg != NULL) + { + /* Use eap->arg, split up in parts by line breaks. */ + theline = line_arg; + p = vim_strchr(theline, '\n'); + if (p == NULL) + line_arg += STRLEN(line_arg); + else + { + *p = NUL; + line_arg = p + 1; + } + } + else if (eap->getline == NULL) + theline = getcmdline(':', 0L, indent); + else + theline = eap->getline(':', eap->cookie, indent); + if (KeyTyped) + lines_left = Rows - 1; + if (theline == NULL) + { + EMSG(_("E126: Missing :endfunction")); + goto erret; + } + + /* Detect line continuation: sourcing_lnum increased more than one. */ + if (sourcing_lnum > sourcing_lnum_off + 1) + sourcing_lnum_off = sourcing_lnum - sourcing_lnum_off - 1; + else + sourcing_lnum_off = 0; + + if (skip_until != NULL) + { + /* between ":append" and "." and between ":python < 2 && STRNCMP(p, "end", 3) == 0) + indent -= 2; + else if (STRNCMP(p, "if", 2) == 0 + || STRNCMP(p, "wh", 2) == 0 + || STRNCMP(p, "for", 3) == 0 + || STRNCMP(p, "try", 3) == 0) + indent += 2; + + /* Check for defining a function inside this function. */ + if (checkforcmd(&p, "function", 2)) + { + if (*p == '!') + p = skipwhite(p + 1); + p += eval_fname_script(p); + vim_free(trans_function_name(&p, TRUE, 0, NULL, NULL)); + if (*skipwhite(p) == '(') + { + ++nesting; + indent += 2; + } + } + + /* Check for ":append" or ":insert". */ + p = skip_range(p, NULL); + if ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p')) + || (p[0] == 'i' + && (!ASCII_ISALPHA(p[1]) || (p[1] == 'n' + && (!ASCII_ISALPHA(p[2]) || (p[2] == 's')))))) + skip_until = vim_strsave((char_u *)"."); + + /* Check for ":python < 0) + ((char_u **)(newlines.ga_data))[newlines.ga_len++] = NULL; + + /* Check for end of eap->arg. */ + if (line_arg != NULL && *line_arg == NUL) + line_arg = NULL; + } + + /* Don't define the function when skipping commands or when an error was + * detected. */ + if (eap->skip || did_emsg) + goto erret; + + /* + * If there are no errors, add the function + */ + if (fudi.fd_dict == NULL) + { + v = find_var(name, &ht, FALSE); + if (v != NULL && v->di_tv.v_type == VAR_FUNC) + { + emsg_funcname(N_("E707: Function name conflicts with variable: %s"), + name); + goto erret; + } + + fp = find_func(name); + if (fp != NULL) + { + if (!eap->forceit) + { + emsg_funcname(e_funcexts, name); + goto erret; + } + if (fp->uf_calls > 0) + { + emsg_funcname(N_("E127: Cannot redefine function %s: It is in use"), + name); + goto erret; + } + /* redefine existing function */ + ga_clear_strings(&(fp->uf_args)); + ga_clear_strings(&(fp->uf_lines)); + vim_free(name); + name = NULL; + } + } + else + { + char numbuf[20]; + + fp = NULL; + if (fudi.fd_newkey == NULL && !eap->forceit) + { + EMSG(_(e_funcdict)); + goto erret; + } + if (fudi.fd_di == NULL) + { + /* Can't add a function to a locked dictionary */ + if (tv_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE)) + goto erret; + } + /* Can't change an existing function if it is locked */ + else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE)) + goto erret; + + /* Give the function a sequential number. Can only be used with a + * Funcref! */ + vim_free(name); + sprintf(numbuf, "%d", ++func_nr); + name = vim_strsave((char_u *)numbuf); + if (name == NULL) + goto erret; + } + + if (fp == NULL) + { + if (fudi.fd_dict == NULL && vim_strchr(name, AUTOLOAD_CHAR) != NULL) + { + int slen, plen; + char_u *scriptname; + + /* Check that the autoload name matches the script name. */ + j = FAIL; + if (sourcing_name != NULL) + { + scriptname = autoload_name(name); + if (scriptname != NULL) + { + p = vim_strchr(scriptname, '/'); + plen = (int)STRLEN(p); + slen = (int)STRLEN(sourcing_name); + if (slen > plen && fnamecmp(p, + sourcing_name + slen - plen) == 0) + j = OK; + vim_free(scriptname); + } + } + if (j == FAIL) + { + EMSG2(_("E746: Function name does not match script file name: %s"), name); + goto erret; + } + } + + fp = (ufunc_T *)alloc((unsigned)(sizeof(ufunc_T) + STRLEN(name))); + if (fp == NULL) + goto erret; + + if (fudi.fd_dict != NULL) + { + if (fudi.fd_di == NULL) + { + /* add new dict entry */ + fudi.fd_di = dictitem_alloc(fudi.fd_newkey); + if (fudi.fd_di == NULL) + { + vim_free(fp); + goto erret; + } + if (dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) + { + vim_free(fudi.fd_di); + vim_free(fp); + goto erret; + } + } + else + /* overwrite existing dict entry */ + clear_tv(&fudi.fd_di->di_tv); + fudi.fd_di->di_tv.v_type = VAR_FUNC; + fudi.fd_di->di_tv.v_lock = 0; + fudi.fd_di->di_tv.vval.v_string = vim_strsave(name); + fp->uf_refcount = 1; + + /* behave like "dict" was used */ + flags |= FC_DICT; + } + + /* insert the new function in the function list */ + STRCPY(fp->uf_name, name); + if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) + { + vim_free(fp); + goto erret; + } + } + fp->uf_args = newargs; + fp->uf_lines = newlines; +#ifdef FEAT_PROFILE + fp->uf_tml_count = NULL; + fp->uf_tml_total = NULL; + fp->uf_tml_self = NULL; + fp->uf_profiling = FALSE; + if (prof_def_func()) + func_do_profile(fp); +#endif + fp->uf_varargs = varargs; + fp->uf_flags = flags; + fp->uf_calls = 0; + fp->uf_script_ID = current_SID; + goto ret_free; + +erret: + ga_clear_strings(&newargs); +errret_2: + ga_clear_strings(&newlines); +ret_free: + vim_free(skip_until); + vim_free(fudi.fd_newkey); + vim_free(name); + did_emsg |= saved_did_emsg; + need_wait_return |= saved_wait_return; +} + +/* + * Return 5 if "p" starts with "" or "" (ignoring case). + * Return 2 if "p" starts with "s:". + * Return 0 otherwise. + */ + int +eval_fname_script(char_u *p) +{ + /* Use MB_STRICMP() because in Turkish comparing the "I" may not work with + * the standard library function. */ + if (p[0] == '<' && (MB_STRNICMP(p + 1, "SID>", 4) == 0 + || MB_STRNICMP(p + 1, "SNR>", 4) == 0)) + return 5; + if (p[0] == 's' && p[1] == ':') + return 2; + return 0; +} + + int +translated_function_exists(char_u *name) +{ + if (builtin_function(name, -1)) + return find_internal_func(name) >= 0; + return find_func(name) != NULL; +} + +/* + * Return TRUE if a function "name" exists. + */ + int +function_exists(char_u *name) +{ + char_u *nm = name; + char_u *p; + int n = FALSE; + + p = trans_function_name(&nm, FALSE, TFN_INT|TFN_QUIET|TFN_NO_AUTOLOAD, + NULL, NULL); + nm = skipwhite(nm); + + /* Only accept "funcname", "funcname ", "funcname (..." and + * "funcname(...", not "funcname!...". */ + if (p != NULL && (*nm == NUL || *nm == '(')) + n = translated_function_exists(p); + vim_free(p); + return n; +} + + char_u * +get_expanded_name(char_u *name, int check) +{ + char_u *nm = name; + char_u *p; + + p = trans_function_name(&nm, FALSE, TFN_INT|TFN_QUIET, NULL, NULL); + + if (p != NULL && *nm == NUL) + if (!check || translated_function_exists(p)) + return p; + + vim_free(p); + return NULL; +} + +#if defined(FEAT_PROFILE) || defined(PROTO) +/* + * Start profiling function "fp". + */ + static void +func_do_profile(ufunc_T *fp) +{ + int len = fp->uf_lines.ga_len; + + if (len == 0) + len = 1; /* avoid getting error for allocating zero bytes */ + fp->uf_tm_count = 0; + profile_zero(&fp->uf_tm_self); + profile_zero(&fp->uf_tm_total); + if (fp->uf_tml_count == NULL) + fp->uf_tml_count = (int *)alloc_clear((unsigned) (sizeof(int) * len)); + if (fp->uf_tml_total == NULL) + fp->uf_tml_total = (proftime_T *)alloc_clear((unsigned) + (sizeof(proftime_T) * len)); + if (fp->uf_tml_self == NULL) + fp->uf_tml_self = (proftime_T *)alloc_clear((unsigned) + (sizeof(proftime_T) * len)); + fp->uf_tml_idx = -1; + if (fp->uf_tml_count == NULL || fp->uf_tml_total == NULL + || fp->uf_tml_self == NULL) + return; /* out of memory */ + + fp->uf_profiling = TRUE; +} + +/* + * Dump the profiling results for all functions in file "fd". + */ + void +func_dump_profile(FILE *fd) +{ + hashitem_T *hi; + int todo; + ufunc_T *fp; + int i; + ufunc_T **sorttab; + int st_len = 0; + + todo = (int)func_hashtab.ht_used; + if (todo == 0) + return; /* nothing to dump */ + + sorttab = (ufunc_T **)alloc((unsigned)(sizeof(ufunc_T *) * todo)); + + for (hi = func_hashtab.ht_array; todo > 0; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + --todo; + fp = HI2UF(hi); + if (fp->uf_profiling) + { + if (sorttab != NULL) + sorttab[st_len++] = fp; + + if (fp->uf_name[0] == K_SPECIAL) + fprintf(fd, "FUNCTION %s()\n", fp->uf_name + 3); + else + fprintf(fd, "FUNCTION %s()\n", fp->uf_name); + if (fp->uf_tm_count == 1) + fprintf(fd, "Called 1 time\n"); + else + fprintf(fd, "Called %d times\n", fp->uf_tm_count); + fprintf(fd, "Total time: %s\n", profile_msg(&fp->uf_tm_total)); + fprintf(fd, " Self time: %s\n", profile_msg(&fp->uf_tm_self)); + fprintf(fd, "\n"); + fprintf(fd, "count total (s) self (s)\n"); + + for (i = 0; i < fp->uf_lines.ga_len; ++i) + { + if (FUNCLINE(fp, i) == NULL) + continue; + prof_func_line(fd, fp->uf_tml_count[i], + &fp->uf_tml_total[i], &fp->uf_tml_self[i], TRUE); + fprintf(fd, "%s\n", FUNCLINE(fp, i)); + } + fprintf(fd, "\n"); + } + } + } + + if (sorttab != NULL && st_len > 0) + { + qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *), + prof_total_cmp); + prof_sort_list(fd, sorttab, st_len, "TOTAL", FALSE); + qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *), + prof_self_cmp); + prof_sort_list(fd, sorttab, st_len, "SELF", TRUE); + } + + vim_free(sorttab); +} + + static void +prof_sort_list( + FILE *fd, + ufunc_T **sorttab, + int st_len, + char *title, + int prefer_self) /* when equal print only self time */ +{ + int i; + ufunc_T *fp; + + fprintf(fd, "FUNCTIONS SORTED ON %s TIME\n", title); + fprintf(fd, "count total (s) self (s) function\n"); + for (i = 0; i < 20 && i < st_len; ++i) + { + fp = sorttab[i]; + prof_func_line(fd, fp->uf_tm_count, &fp->uf_tm_total, &fp->uf_tm_self, + prefer_self); + if (fp->uf_name[0] == K_SPECIAL) + fprintf(fd, " %s()\n", fp->uf_name + 3); + else + fprintf(fd, " %s()\n", fp->uf_name); + } + fprintf(fd, "\n"); +} + +/* + * Print the count and times for one function or function line. + */ + static void +prof_func_line( + FILE *fd, + int count, + proftime_T *total, + proftime_T *self, + int prefer_self) /* when equal print only self time */ +{ + if (count > 0) + { + fprintf(fd, "%5d ", count); + if (prefer_self && profile_equal(total, self)) + fprintf(fd, " "); + else + fprintf(fd, "%s ", profile_msg(total)); + if (!prefer_self && profile_equal(total, self)) + fprintf(fd, " "); + else + fprintf(fd, "%s ", profile_msg(self)); + } + else + fprintf(fd, " "); +} + +/* + * Compare function for total time sorting. + */ + static int +#ifdef __BORLANDC__ +_RTLENTRYF +#endif +prof_total_cmp(const void *s1, const void *s2) +{ + ufunc_T *p1, *p2; + + p1 = *(ufunc_T **)s1; + p2 = *(ufunc_T **)s2; + return profile_cmp(&p1->uf_tm_total, &p2->uf_tm_total); +} + +/* + * Compare function for self time sorting. + */ + static int +#ifdef __BORLANDC__ +_RTLENTRYF +#endif +prof_self_cmp(const void *s1, const void *s2) +{ + ufunc_T *p1, *p2; + + p1 = *(ufunc_T **)s1; + p2 = *(ufunc_T **)s2; + return profile_cmp(&p1->uf_tm_self, &p2->uf_tm_self); +} + +/* + * Prepare profiling for entering a child or something else that is not + * counted for the script/function itself. + * Should always be called in pair with prof_child_exit(). + */ + void +prof_child_enter( + proftime_T *tm) /* place to store waittime */ +{ + funccall_T *fc = current_funccal; + + if (fc != NULL && fc->func->uf_profiling) + profile_start(&fc->prof_child); + script_prof_save(tm); +} + +/* + * Take care of time spent in a child. + * Should always be called after prof_child_enter(). + */ + void +prof_child_exit( + proftime_T *tm) /* where waittime was stored */ +{ + funccall_T *fc = current_funccal; + + if (fc != NULL && fc->func->uf_profiling) + { + profile_end(&fc->prof_child); + profile_sub_wait(tm, &fc->prof_child); /* don't count waiting time */ + profile_add(&fc->func->uf_tm_children, &fc->prof_child); + profile_add(&fc->func->uf_tml_children, &fc->prof_child); + } + script_prof_restore(tm); +} + +#endif /* FEAT_PROFILE */ + +#if defined(FEAT_CMDL_COMPL) || defined(PROTO) + +/* + * Function given to ExpandGeneric() to obtain the list of user defined + * function names. + */ + char_u * +get_user_func_name(expand_T *xp, int idx) +{ + static long_u done; + static hashitem_T *hi; + ufunc_T *fp; + + if (idx == 0) + { + done = 0; + hi = func_hashtab.ht_array; + } + if (done < func_hashtab.ht_used) + { + if (done++ > 0) + ++hi; + while (HASHITEM_EMPTY(hi)) + ++hi; + fp = HI2UF(hi); + + if (fp->uf_flags & FC_DICT) + return (char_u *)""; /* don't show dict functions */ + + if (STRLEN(fp->uf_name) + 4 >= IOSIZE) + return fp->uf_name; /* prevents overflow */ + + cat_func_name(IObuff, fp); + if (xp->xp_context != EXPAND_USER_FUNC) + { + STRCAT(IObuff, "("); + if (!fp->uf_varargs && fp->uf_args.ga_len == 0) + STRCAT(IObuff, ")"); + } + return IObuff; + } + return NULL; +} + +#endif /* FEAT_CMDL_COMPL */ + +/* + * ":delfunction {name}" + */ + void +ex_delfunction(exarg_T *eap) +{ + ufunc_T *fp = NULL; + char_u *p; + char_u *name; + funcdict_T fudi; + + p = eap->arg; + name = trans_function_name(&p, eap->skip, 0, &fudi, NULL); + vim_free(fudi.fd_newkey); + if (name == NULL) + { + if (fudi.fd_dict != NULL && !eap->skip) + EMSG(_(e_funcref)); + return; + } + if (!ends_excmd(*skipwhite(p))) + { + vim_free(name); + EMSG(_(e_trailing)); + return; + } + eap->nextcmd = check_nextcmd(p); + if (eap->nextcmd != NULL) + *p = NUL; + + if (!eap->skip) + fp = find_func(name); + vim_free(name); + + if (!eap->skip) + { + if (fp == NULL) + { + EMSG2(_(e_nofunc), eap->arg); + return; + } + if (fp->uf_calls > 0) + { + EMSG2(_("E131: Cannot delete function %s: It is in use"), eap->arg); + return; + } + + if (fudi.fd_dict != NULL) + { + /* Delete the dict item that refers to the function, it will + * invoke func_unref() and possibly delete the function. */ + dictitem_remove(fudi.fd_dict, fudi.fd_di); + } + else + func_free(fp); + } +} + +/* + * Unreference a Function: decrement the reference count and free it when it + * becomes zero. Only for numbered functions. + */ + void +func_unref(char_u *name) +{ + ufunc_T *fp; + + if (name == NULL) + return; + else if (isdigit(*name)) + { + fp = find_func(name); + if (fp == NULL) + { +#ifdef EXITFREE + if (!entered_free_all_mem) +#endif + EMSG2(_(e_intern2), "func_unref()"); + } + else if (--fp->uf_refcount <= 0) + { + /* Only delete it when it's not being used. Otherwise it's done + * when "uf_calls" becomes zero. */ + if (fp->uf_calls == 0) + func_free(fp); + } + } + else if (STRNCMP(name, "", 8) == 0) + { + /* fail silently, when lambda function isn't found. */ + fp = find_func(name); + if (fp != NULL && --fp->uf_refcount <= 0) + { + /* Only delete it when it's not being used. Otherwise it's done + * when "uf_calls" becomes zero. */ + if (fp->uf_calls == 0) + func_free(fp); + } + } +} + +/* + * Count a reference to a Function. + */ + void +func_ref(char_u *name) +{ + ufunc_T *fp; + + if (name == NULL) + return; + else if (isdigit(*name)) + { + fp = find_func(name); + if (fp == NULL) + EMSG2(_(e_intern2), "func_ref()"); + else + ++fp->uf_refcount; + } + else if (STRNCMP(name, "", 8) == 0) + { + /* fail silently, when lambda function isn't found. */ + fp = find_func(name); + if (fp != NULL) + ++fp->uf_refcount; + } +} + +/* + * Return TRUE if items in "fc" do not have "copyID". That means they are not + * referenced from anywhere that is in use. + */ + static int +can_free_funccal(funccall_T *fc, int copyID) +{ + return (fc->l_varlist.lv_copyID != copyID + && fc->l_vars.dv_copyID != copyID + && fc->l_avars.dv_copyID != copyID); +} + +/* + * ":return [expr]" + */ + void +ex_return(exarg_T *eap) +{ + char_u *arg = eap->arg; + typval_T rettv; + int returning = FALSE; + + if (current_funccal == NULL) + { + EMSG(_("E133: :return not inside a function")); + return; + } + + if (eap->skip) + ++emsg_skip; + + eap->nextcmd = NULL; + if ((*arg != NUL && *arg != '|' && *arg != '\n') + && eval0(arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) + { + if (!eap->skip) + returning = do_return(eap, FALSE, TRUE, &rettv); + else + clear_tv(&rettv); + } + /* It's safer to return also on error. */ + else if (!eap->skip) + { + /* + * Return unless the expression evaluation has been cancelled due to an + * aborting error, an interrupt, or an exception. + */ + if (!aborting()) + returning = do_return(eap, FALSE, TRUE, NULL); + } + + /* When skipping or the return gets pending, advance to the next command + * in this line (!returning). Otherwise, ignore the rest of the line. + * Following lines will be ignored by get_func_line(). */ + if (returning) + eap->nextcmd = NULL; + else if (eap->nextcmd == NULL) /* no argument */ + eap->nextcmd = check_nextcmd(arg); + + if (eap->skip) + --emsg_skip; +} + +/* + * ":1,25call func(arg1, arg2)" function call. + */ + void +ex_call(exarg_T *eap) +{ + char_u *arg = eap->arg; + char_u *startarg; + char_u *name; + char_u *tofree; + int len; + typval_T rettv; + linenr_T lnum; + int doesrange; + int failed = FALSE; + funcdict_T fudi; + partial_T *partial = NULL; + + if (eap->skip) + { + /* trans_function_name() doesn't work well when skipping, use eval0() + * instead to skip to any following command, e.g. for: + * :if 0 | call dict.foo().bar() | endif */ + ++emsg_skip; + if (eval0(eap->arg, &rettv, &eap->nextcmd, FALSE) != FAIL) + clear_tv(&rettv); + --emsg_skip; + return; + } + + tofree = trans_function_name(&arg, eap->skip, TFN_INT, &fudi, &partial); + if (fudi.fd_newkey != NULL) + { + /* Still need to give an error message for missing key. */ + EMSG2(_(e_dictkey), fudi.fd_newkey); + vim_free(fudi.fd_newkey); + } + if (tofree == NULL) + return; + + /* Increase refcount on dictionary, it could get deleted when evaluating + * the arguments. */ + if (fudi.fd_dict != NULL) + ++fudi.fd_dict->dv_refcount; + + /* If it is the name of a variable of type VAR_FUNC or VAR_PARTIAL use its + * contents. For VAR_PARTIAL get its partial, unless we already have one + * from trans_function_name(). */ + len = (int)STRLEN(tofree); + name = deref_func_name(tofree, &len, + partial != NULL ? NULL : &partial, FALSE); + + /* Skip white space to allow ":call func ()". Not good, but required for + * backward compatibility. */ + startarg = skipwhite(arg); + rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ + + if (*startarg != '(') + { + EMSG2(_("E107: Missing parentheses: %s"), eap->arg); + goto end; + } + + /* + * When skipping, evaluate the function once, to find the end of the + * arguments. + * When the function takes a range, this is discovered after the first + * call, and the loop is broken. + */ + if (eap->skip) + { + ++emsg_skip; + lnum = eap->line2; /* do it once, also with an invalid range */ + } + else + lnum = eap->line1; + for ( ; lnum <= eap->line2; ++lnum) + { + if (!eap->skip && eap->addr_count > 0) + { + curwin->w_cursor.lnum = lnum; + curwin->w_cursor.col = 0; +#ifdef FEAT_VIRTUALEDIT + curwin->w_cursor.coladd = 0; +#endif + } + arg = startarg; + if (get_func_tv(name, (int)STRLEN(name), &rettv, &arg, + eap->line1, eap->line2, &doesrange, + !eap->skip, partial, fudi.fd_dict) == FAIL) + { + failed = TRUE; + break; + } + + /* Handle a function returning a Funcref, Dictionary or List. */ + if (handle_subscript(&arg, &rettv, !eap->skip, TRUE) == FAIL) + { + failed = TRUE; + break; + } + + clear_tv(&rettv); + if (doesrange || eap->skip) + break; + + /* Stop when immediately aborting on error, or when an interrupt + * occurred or an exception was thrown but not caught. + * get_func_tv() returned OK, so that the check for trailing + * characters below is executed. */ + if (aborting()) + break; + } + if (eap->skip) + --emsg_skip; + + if (!failed) + { + /* Check for trailing illegal characters and a following command. */ + if (!ends_excmd(*arg)) + { + emsg_severe = TRUE; + EMSG(_(e_trailing)); + } + else + eap->nextcmd = check_nextcmd(arg); + } + +end: + dict_unref(fudi.fd_dict); + vim_free(tofree); +} + +/* + * Return from a function. Possibly makes the return pending. Also called + * for a pending return at the ":endtry" or after returning from an extra + * do_cmdline(). "reanimate" is used in the latter case. "is_cmd" is set + * when called due to a ":return" command. "rettv" may point to a typval_T + * with the return rettv. Returns TRUE when the return can be carried out, + * FALSE when the return gets pending. + */ + int +do_return( + exarg_T *eap, + int reanimate, + int is_cmd, + void *rettv) +{ + int idx; + struct condstack *cstack = eap->cstack; + + if (reanimate) + /* Undo the return. */ + current_funccal->returned = FALSE; + + /* + * Cleanup (and inactivate) conditionals, but stop when a try conditional + * not in its finally clause (which then is to be executed next) is found. + * In this case, make the ":return" pending for execution at the ":endtry". + * Otherwise, return normally. + */ + idx = cleanup_conditionals(eap->cstack, 0, TRUE); + if (idx >= 0) + { + cstack->cs_pending[idx] = CSTP_RETURN; + + if (!is_cmd && !reanimate) + /* A pending return again gets pending. "rettv" points to an + * allocated variable with the rettv of the original ":return"'s + * argument if present or is NULL else. */ + cstack->cs_rettv[idx] = rettv; + else + { + /* When undoing a return in order to make it pending, get the stored + * return rettv. */ + if (reanimate) + rettv = current_funccal->rettv; + + if (rettv != NULL) + { + /* Store the value of the pending return. */ + if ((cstack->cs_rettv[idx] = alloc_tv()) != NULL) + *(typval_T *)cstack->cs_rettv[idx] = *(typval_T *)rettv; + else + EMSG(_(e_outofmem)); + } + else + cstack->cs_rettv[idx] = NULL; + + if (reanimate) + { + /* The pending return value could be overwritten by a ":return" + * without argument in a finally clause; reset the default + * return value. */ + current_funccal->rettv->v_type = VAR_NUMBER; + current_funccal->rettv->vval.v_number = 0; + } + } + report_make_pending(CSTP_RETURN, rettv); + } + else + { + current_funccal->returned = TRUE; + + /* If the return is carried out now, store the return value. For + * a return immediately after reanimation, the value is already + * there. */ + if (!reanimate && rettv != NULL) + { + clear_tv(current_funccal->rettv); + *current_funccal->rettv = *(typval_T *)rettv; + if (!is_cmd) + vim_free(rettv); + } + } + + return idx < 0; +} + +/* + * Free the variable with a pending return value. + */ + void +discard_pending_return(void *rettv) +{ + free_tv((typval_T *)rettv); +} + +/* + * Generate a return command for producing the value of "rettv". The result + * is an allocated string. Used by report_pending() for verbose messages. + */ + char_u * +get_return_cmd(void *rettv) +{ + char_u *s = NULL; + char_u *tofree = NULL; + char_u numbuf[NUMBUFLEN]; + + if (rettv != NULL) + s = echo_string((typval_T *)rettv, &tofree, numbuf, 0); + if (s == NULL) + s = (char_u *)""; + + STRCPY(IObuff, ":return "); + STRNCPY(IObuff + 8, s, IOSIZE - 8); + if (STRLEN(s) + 8 >= IOSIZE) + STRCPY(IObuff + IOSIZE - 4, "..."); + vim_free(tofree); + return vim_strsave(IObuff); +} + +/* + * Get next function line. + * Called by do_cmdline() to get the next line. + * Returns allocated string, or NULL for end of function. + */ + char_u * +get_func_line( + int c UNUSED, + void *cookie, + int indent UNUSED) +{ + funccall_T *fcp = (funccall_T *)cookie; + ufunc_T *fp = fcp->func; + char_u *retval; + garray_T *gap; /* growarray with function lines */ + + /* If breakpoints have been added/deleted need to check for it. */ + if (fcp->dbg_tick != debug_tick) + { + fcp->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, + sourcing_lnum); + fcp->dbg_tick = debug_tick; + } +#ifdef FEAT_PROFILE + if (do_profiling == PROF_YES) + func_line_end(cookie); +#endif + + gap = &fp->uf_lines; + if (((fp->uf_flags & FC_ABORT) && did_emsg && !aborted_in_try()) + || fcp->returned) + retval = NULL; + else + { + /* Skip NULL lines (continuation lines). */ + while (fcp->linenr < gap->ga_len + && ((char_u **)(gap->ga_data))[fcp->linenr] == NULL) + ++fcp->linenr; + if (fcp->linenr >= gap->ga_len) + retval = NULL; + else + { + retval = vim_strsave(((char_u **)(gap->ga_data))[fcp->linenr++]); + sourcing_lnum = fcp->linenr; +#ifdef FEAT_PROFILE + if (do_profiling == PROF_YES) + func_line_start(cookie); +#endif + } + } + + /* Did we encounter a breakpoint? */ + if (fcp->breakpoint != 0 && fcp->breakpoint <= sourcing_lnum) + { + dbg_breakpoint(fp->uf_name, sourcing_lnum); + /* Find next breakpoint. */ + fcp->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, + sourcing_lnum); + fcp->dbg_tick = debug_tick; + } + + return retval; +} + +#if defined(FEAT_PROFILE) || defined(PROTO) +/* + * Called when starting to read a function line. + * "sourcing_lnum" must be correct! + * When skipping lines it may not actually be executed, but we won't find out + * until later and we need to store the time now. + */ + void +func_line_start(void *cookie) +{ + funccall_T *fcp = (funccall_T *)cookie; + ufunc_T *fp = fcp->func; + + if (fp->uf_profiling && sourcing_lnum >= 1 + && sourcing_lnum <= fp->uf_lines.ga_len) + { + fp->uf_tml_idx = sourcing_lnum - 1; + /* Skip continuation lines. */ + while (fp->uf_tml_idx > 0 && FUNCLINE(fp, fp->uf_tml_idx) == NULL) + --fp->uf_tml_idx; + fp->uf_tml_execed = FALSE; + profile_start(&fp->uf_tml_start); + profile_zero(&fp->uf_tml_children); + profile_get_wait(&fp->uf_tml_wait); + } +} + +/* + * Called when actually executing a function line. + */ + void +func_line_exec(void *cookie) +{ + funccall_T *fcp = (funccall_T *)cookie; + ufunc_T *fp = fcp->func; + + if (fp->uf_profiling && fp->uf_tml_idx >= 0) + fp->uf_tml_execed = TRUE; +} + +/* + * Called when done with a function line. + */ + void +func_line_end(void *cookie) +{ + funccall_T *fcp = (funccall_T *)cookie; + ufunc_T *fp = fcp->func; + + if (fp->uf_profiling && fp->uf_tml_idx >= 0) + { + if (fp->uf_tml_execed) + { + ++fp->uf_tml_count[fp->uf_tml_idx]; + profile_end(&fp->uf_tml_start); + profile_sub_wait(&fp->uf_tml_wait, &fp->uf_tml_start); + profile_add(&fp->uf_tml_total[fp->uf_tml_idx], &fp->uf_tml_start); + profile_self(&fp->uf_tml_self[fp->uf_tml_idx], &fp->uf_tml_start, + &fp->uf_tml_children); + } + fp->uf_tml_idx = -1; + } +} +#endif + +/* + * Return TRUE if the currently active function should be ended, because a + * return was encountered or an error occurred. Used inside a ":while". + */ + int +func_has_ended(void *cookie) +{ + funccall_T *fcp = (funccall_T *)cookie; + + /* Ignore the "abort" flag if the abortion behavior has been changed due to + * an error inside a try conditional. */ + return (((fcp->func->uf_flags & FC_ABORT) && did_emsg && !aborted_in_try()) + || fcp->returned); +} + +/* + * return TRUE if cookie indicates a function which "abort"s on errors. + */ + int +func_has_abort( + void *cookie) +{ + return ((funccall_T *)cookie)->func->uf_flags & FC_ABORT; +} + + +/* + * Turn "dict.Func" into a partial for "Func" bound to "dict". + * Don't do this when "Func" is already a partial that was bound + * explicitly (pt_auto is FALSE). + * Changes "rettv" in-place. + * Returns the updated "selfdict_in". + */ + dict_T * +make_partial(dict_T *selfdict_in, typval_T *rettv) +{ + char_u *fname = rettv->v_type == VAR_FUNC ? rettv->vval.v_string + : rettv->vval.v_partial->pt_name; + char_u *tofree = NULL; + ufunc_T *fp; + char_u fname_buf[FLEN_FIXED + 1]; + int error; + dict_T *selfdict = selfdict_in; + + /* Translate "s:func" to the stored function name. */ + fname = fname_trans_sid(fname, fname_buf, &tofree, &error); + fp = find_func(fname); + vim_free(tofree); + + if (fp != NULL && (fp->uf_flags & FC_DICT)) + { + partial_T *pt = (partial_T *)alloc_clear(sizeof(partial_T)); + + if (pt != NULL) + { + pt->pt_refcount = 1; + pt->pt_dict = selfdict; + pt->pt_auto = TRUE; + selfdict = NULL; + if (rettv->v_type == VAR_FUNC) + { + /* Just a function: Take over the function name and use + * selfdict. */ + pt->pt_name = rettv->vval.v_string; + } + else + { + partial_T *ret_pt = rettv->vval.v_partial; + int i; + + /* Partial: copy the function name, use selfdict and copy + * args. Can't take over name or args, the partial might + * be referenced elsewhere. */ + pt->pt_name = vim_strsave(ret_pt->pt_name); + func_ref(pt->pt_name); + if (ret_pt->pt_argc > 0) + { + pt->pt_argv = (typval_T *)alloc( + sizeof(typval_T) * ret_pt->pt_argc); + if (pt->pt_argv == NULL) + /* out of memory: drop the arguments */ + pt->pt_argc = 0; + else + { + pt->pt_argc = ret_pt->pt_argc; + for (i = 0; i < pt->pt_argc; i++) + copy_tv(&ret_pt->pt_argv[i], &pt->pt_argv[i]); + } + } + partial_unref(ret_pt); + } + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = pt; + } + } + return selfdict; +} + +/* + * Return the name of the executed function. + */ + char_u * +func_name(void *cookie) +{ + return ((funccall_T *)cookie)->func->uf_name; +} + +/* + * Return the address holding the next breakpoint line for a funccall cookie. + */ + linenr_T * +func_breakpoint(void *cookie) +{ + return &((funccall_T *)cookie)->breakpoint; +} + +/* + * Return the address holding the debug tick for a funccall cookie. + */ + int * +func_dbg_tick(void *cookie) +{ + return &((funccall_T *)cookie)->dbg_tick; +} + +/* + * Return the nesting level for a funccall cookie. + */ + int +func_level(void *cookie) +{ + return ((funccall_T *)cookie)->level; +} + +/* + * Return TRUE when a function was ended by a ":return" command. + */ + int +current_func_returned(void) +{ + return current_funccal->returned; +} + +/* + * Save the current function call pointer, and set it to NULL. + * Used when executing autocommands and for ":source". + */ + void * +save_funccal(void) +{ + funccall_T *fc = current_funccal; + + current_funccal = NULL; + return (void *)fc; +} + + void +restore_funccal(void *vfc) +{ + funccall_T *fc = (funccall_T *)vfc; + + current_funccal = fc; +} + + int +free_unref_funccal(int copyID, int testing) +{ + int did_free = FALSE; + int did_free_funccal = FALSE; + funccall_T *fc, **pfc; + + for (pfc = &previous_funccal; *pfc != NULL; ) + { + if (can_free_funccal(*pfc, copyID)) + { + fc = *pfc; + *pfc = fc->caller; + free_funccal(fc, TRUE); + did_free = TRUE; + did_free_funccal = TRUE; + } + else + pfc = &(*pfc)->caller; + } + if (did_free_funccal) + /* When a funccal was freed some more items might be garbage + * collected, so run again. */ + (void)garbage_collect(testing); + + return did_free; +} + +/* + * Get function call environment based on bactrace debug level + */ + static funccall_T * +get_funccal(void) +{ + int i; + funccall_T *funccal; + funccall_T *temp_funccal; + + funccal = current_funccal; + if (debug_backtrace_level > 0) + { + for (i = 0; i < debug_backtrace_level; i++) + { + temp_funccal = funccal->caller; + if (temp_funccal) + funccal = temp_funccal; + else + /* backtrace level overflow. reset to max */ + debug_backtrace_level = i; + } + } + return funccal; +} + +/* + * Return the hashtable used for local variables in the current funccal. + * Return NULL if there is no current funccal. + */ + hashtab_T * +get_funccal_local_ht() +{ + if (current_funccal == NULL) + return NULL; + return &get_funccal()->l_vars.dv_hashtab; +} + +/* + * Return the l: scope variable. + * Return NULL if there is no current funccal. + */ + dictitem_T * +get_funccal_local_var() +{ + if (current_funccal == NULL) + return NULL; + return &get_funccal()->l_vars_var; +} + +/* + * Return the hashtable used for argument in the current funccal. + * Return NULL if there is no current funccal. + */ + hashtab_T * +get_funccal_args_ht() +{ + if (current_funccal == NULL) + return NULL; + return &get_funccal()->l_avars.dv_hashtab; +} + +/* + * Return the a: scope variable. + * Return NULL if there is no current funccal. + */ + dictitem_T * +get_funccal_args_var() +{ + if (current_funccal == NULL) + return NULL; + return ¤t_funccal->l_avars_var; +} + +/* + * Clear the current_funccal and return the old value. + * Caller is expected to invoke restore_current_funccal(). + */ + void * +clear_current_funccal() +{ + funccall_T *f = current_funccal; + + current_funccal = NULL; + return f; +} + + void +restore_current_funccal(void *f) +{ + current_funccal = f; +} + +/* + * List function variables, if there is a function. + */ + void +list_func_vars(int *first) +{ + if (current_funccal != NULL) + list_hashtable_vars(¤t_funccal->l_vars.dv_hashtab, + (char_u *)"l:", FALSE, first); +} + +/* + * If "ht" is the hashtable for local variables in the current funccal, return + * the dict that contains it. + * Otherwise return NULL. + */ + dict_T * +get_current_funccal_dict(hashtab_T *ht) +{ + if (current_funccal != NULL + && ht == ¤t_funccal->l_vars.dv_hashtab) + return ¤t_funccal->l_vars; + return NULL; +} + +/* + * Set "copyID + 1" in previous_funccal and callers. + */ + int +set_ref_in_previous_funccal(int copyID) +{ + int abort = FALSE; + funccall_T *fc; + + for (fc = previous_funccal; fc != NULL; fc = fc->caller) + { + abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1, + NULL); + abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1, + NULL); + } + return abort; +} + +/* + * Set "copyID" in all local vars and arguments in the call stack. + */ + int +set_ref_in_call_stack(int copyID) +{ + int abort = FALSE; + funccall_T *fc; + + for (fc = current_funccal; fc != NULL; fc = fc->caller) + { + abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL); + abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL); + } + return abort; +} + +/* + * Set "copyID" in all function arguments. + */ + int +set_ref_in_func_args(int copyID) +{ + int i; + int abort = FALSE; + + for (i = 0; i < funcargs.ga_len; ++i) + abort = abort || set_ref_in_item(((typval_T **)funcargs.ga_data)[i], + copyID, NULL, NULL); + return abort; +} + +#endif /* FEAT_EVAL */ diff --git a/src/version.c b/src/version.c index 4fa35917a4..8354d92c6e 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2058, /**/ 2057, /**/ diff --git a/src/vim.h b/src/vim.h index abe0b2409b..5067c5a885 100644 --- a/src/vim.h +++ b/src/vim.h @@ -2407,4 +2407,29 @@ int vim_main2(int argc, char **argv); #define COPYID_INC 2 #define COPYID_MASK (~0x1) +/* Values for trans_function_name() argument: */ +#define TFN_INT 1 /* internal function name OK */ +#define TFN_QUIET 2 /* no error messages */ +#define TFN_NO_AUTOLOAD 4 /* do not use script autoloading */ + +/* Values for get_lval() flags argument: */ +#define GLV_QUIET TFN_QUIET /* no error messages */ +#define GLV_NO_AUTOLOAD TFN_NO_AUTOLOAD /* do not use script autoloading */ + +#define DO_NOT_FREE_CNT 99999 /* refcount for dict or list that should not + be freed. */ + +/* errors for when calling a function */ +#define ERROR_UNKNOWN 0 +#define ERROR_TOOMANY 1 +#define ERROR_TOOFEW 2 +#define ERROR_SCRIPT 3 +#define ERROR_DICT 4 +#define ERROR_NONE 5 +#define ERROR_OTHER 6 + +/* flags for find_name_end() */ +#define FNE_INCL_BR 1 /* include [] in name */ +#define FNE_CHECK_START 2 /* check name starts with valid character */ + #endif /* VIM__H */ From 6583c44857368f28c802dabe10ac7b7b0c266f50 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 17 Jul 2016 18:41:47 +0200 Subject: [PATCH 2/9] patch 7.4.2059 Problem: Non-Unix builds fail. Solution: Update Makefiles for new files. --- src/Make_bc5.mak | 3 +++ src/Make_cyg_ming.mak | 3 +++ src/Make_dice.mak | 18 +++++++++++++++--- src/Make_ivc.mak | 15 +++++++++++++++ src/Make_manx.mak | 18 ++++++++++++++++++ src/Make_morph.mak | 3 +++ src/Make_mvc.mak | 12 ++++++++++++ src/Make_sas.mak | 15 +++++++++++++++ src/version.c | 2 ++ 9 files changed, 86 insertions(+), 3 deletions(-) diff --git a/src/Make_bc5.mak b/src/Make_bc5.mak index 84637dede3..55a9e0f582 100644 --- a/src/Make_bc5.mak +++ b/src/Make_bc5.mak @@ -543,6 +543,7 @@ vimobj = \ $(OBJDIR)\charset.obj \ $(OBJDIR)\crypt.obj \ $(OBJDIR)\crypt_zip.obj \ + $(OBJDIR)\dict.obj \ $(OBJDIR)\diff.obj \ $(OBJDIR)\digraph.obj \ $(OBJDIR)\edit.obj \ @@ -559,6 +560,7 @@ vimobj = \ $(OBJDIR)\hardcopy.obj \ $(OBJDIR)\hashtab.obj \ $(OBJDIR)\json.obj \ + $(OBJDIR)\list.obj \ $(OBJDIR)\main.obj \ $(OBJDIR)\mark.obj \ $(OBJDIR)\memfile.obj \ @@ -584,6 +586,7 @@ vimobj = \ $(OBJDIR)\term.obj \ $(OBJDIR)\ui.obj \ $(OBJDIR)\undo.obj \ + $(OBJDIR)\userfunc.obj \ $(OBJDIR)\version.obj \ $(OBJDIR)\window.obj \ $(OBJDIR)\pathdef.obj diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index 73db7fea1f..cc68f442cd 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -610,6 +610,7 @@ OBJ = \ $(OUTDIR)/charset.o \ $(OUTDIR)/crypt.o \ $(OUTDIR)/crypt_zip.o \ + $(OUTDIR)/dict.o \ $(OUTDIR)/diff.o \ $(OUTDIR)/digraph.o \ $(OUTDIR)/edit.o \ @@ -626,6 +627,7 @@ OBJ = \ $(OUTDIR)/hardcopy.o \ $(OUTDIR)/hashtab.o \ $(OUTDIR)/json.o \ + $(OUTDIR)/list.o \ $(OUTDIR)/main.o \ $(OUTDIR)/mark.o \ $(OUTDIR)/memfile.o \ @@ -655,6 +657,7 @@ OBJ = \ $(OUTDIR)/term.o \ $(OUTDIR)/ui.o \ $(OUTDIR)/undo.o \ + $(OUTDIR)/userfunc.o \ $(OUTDIR)/version.o \ $(OUTDIR)/vimrc.o \ $(OUTDIR)/window.o diff --git a/src/Make_dice.mak b/src/Make_dice.mak index 708fdfb81c..fe8f2c8b46 100644 --- a/src/Make_dice.mak +++ b/src/Make_dice.mak @@ -32,6 +32,7 @@ SRC = \ charset.c \ crypt.c \ crypt_zip.c \ + dict.c \ diff.c \ digraph.c \ edit.c \ @@ -48,6 +49,7 @@ SRC = \ hardcopy.c \ hashtab.c \ json.c \ + list.c \ main.c \ mark.c \ memfile.c \ @@ -74,6 +76,7 @@ SRC = \ term.c \ ui.c \ undo.c \ + userfunc.c \ window.c \ version.c @@ -83,6 +86,7 @@ OBJ = o/arabic.o \ o/charset.o \ o/crypt.o \ o/crypt_zip.o \ + o/dict.o \ o/diff.o \ o/digraph.o \ o/edit.o \ @@ -99,6 +103,7 @@ OBJ = o/arabic.o \ o/hardcopy.o \ o/hashtab.o \ o/json.o \ + o/list.o \ o/main.o \ o/mark.o \ o/memfile.o \ @@ -125,6 +130,7 @@ OBJ = o/arabic.o \ o/term.o \ o/ui.o \ o/undo.o \ + o/userfunc.o \ o/window.o \ $(TERMLIB) @@ -159,6 +165,8 @@ o/crypt.o: crypt.c $(SYMS) o/crypt_zip.o: crypt_zip.c $(SYMS) +o/dict.o: dict.c $(SYMS) + o/diff.o: diff.c $(SYMS) o/digraph.o: digraph.c $(SYMS) @@ -191,6 +199,8 @@ o/hashtab.o: hashtab.c $(SYMS) o/json.o: json.c $(SYMS) +o/list.o: list.c $(SYMS) + o/main.o: main.c $(SYMS) o/mark.o: mark.c $(SYMS) @@ -246,8 +256,10 @@ o/term.o: term.c $(SYMS) term.h o/termlib.o: termlib.c $(SYMS) -o/ui.o: ui.c $(SYMS) +o/ui.o: ui.c $(SYMS) -o/undo.o: undo.c $(SYMS) +o/undo.o: undo.c $(SYMS) -o/window.o: window.c $(SYMS) +o/userfunc.o: userfunc.c $(SYMS) + +o/window.o: window.c $(SYMS) diff --git a/src/Make_ivc.mak b/src/Make_ivc.mak index f3251c3edf..71cdef05ad 100644 --- a/src/Make_ivc.mak +++ b/src/Make_ivc.mak @@ -216,6 +216,7 @@ LINK32_OBJS= \ "$(INTDIR)/charset.obj" \ "$(INTDIR)/crypt.obj" \ "$(INTDIR)/crypt_zip.obj" \ + "$(INTDIR)/dict.obj" \ "$(INTDIR)/diff.obj" \ "$(INTDIR)/digraph.obj" \ "$(INTDIR)/edit.obj" \ @@ -232,6 +233,7 @@ LINK32_OBJS= \ "$(INTDIR)/hardcopy.obj" \ "$(INTDIR)/hashtab.obj" \ "$(INTDIR)/json.obj" \ + "$(INTDIR)/list.obj" \ "$(INTDIR)/main.obj" \ "$(INTDIR)/mark.obj" \ "$(INTDIR)/mbyte.obj" \ @@ -260,6 +262,7 @@ LINK32_OBJS= \ "$(INTDIR)/term.obj" \ "$(INTDIR)/ui.obj" \ "$(INTDIR)/undo.obj" \ + "$(INTDIR)/userfunc.obj" \ "$(INTDIR)/version.obj" \ "$(INTDIR)/window.obj" @@ -356,6 +359,10 @@ SOURCE=.\crypt_zip.c # End Source File # Begin Source File +SOURCE=.\dict.c +# End Source File +# Begin Source File + SOURCE=.\diff.c # End Source File # Begin Source File @@ -570,6 +577,10 @@ SOURCE=.\json.c # End Source File # Begin Source File +SOURCE=.\list.c +# End Source File +# Begin Source File + SOURCE=.\main.c # End Source File # Begin Source File @@ -682,6 +693,10 @@ SOURCE=.\undo.c # End Source File # Begin Source File +SOURCE=.\userfunc.c +# End Source File +# Begin Source File + SOURCE=.\version.c # End Source File # Begin Source File diff --git a/src/Make_manx.mak b/src/Make_manx.mak index 38fef5679d..81a2c192e8 100644 --- a/src/Make_manx.mak +++ b/src/Make_manx.mak @@ -42,6 +42,7 @@ SRC = arabic.c \ charset.c \ crypt.c \ crypt_zip.c \ + dict.c \ diff.c \ digraph.c \ edit.c \ @@ -58,6 +59,7 @@ SRC = arabic.c \ hardcopy.c \ hashtab.c \ json.c \ + list.c \ main.c \ mark.c \ memfile.c \ @@ -84,6 +86,7 @@ SRC = arabic.c \ term.c \ ui.c \ undo.c \ + userfunc.c \ window.c \ version.c @@ -95,6 +98,7 @@ OBJ = obj/arabic.o \ obj/charset.o \ obj/crypt.o \ obj/crypt_zip.o \ + obj/dict.o \ obj/diff.o \ obj/digraph.o \ obj/edit.o \ @@ -111,6 +115,7 @@ OBJ = obj/arabic.o \ obj/hardcopy.o \ obj/hashtab.o \ obj/json.o \ + obj/list.o \ obj/main.o \ obj/mark.o \ obj/memfile.o \ @@ -137,6 +142,7 @@ OBJ = obj/arabic.o \ obj/term.o \ obj/ui.o \ obj/undo.o \ + obj/userfunc.o \ obj/window.o \ $(TERMLIB) @@ -146,6 +152,7 @@ PRO = proto/arabic.pro \ proto/charset.pro \ proto/crypt.pro \ proto/crypt_zip.pro \ + proto/dict.pro \ proto/diff.pro \ proto/digraph.pro \ proto/edit.pro \ @@ -162,6 +169,7 @@ PRO = proto/arabic.pro \ proto/hardcopy.pro \ proto/hashtab.pro \ proto/json.pro \ + proto/list.pro \ proto/main.pro \ proto/mark.pro \ proto/memfile.pro \ @@ -189,6 +197,7 @@ PRO = proto/arabic.pro \ proto/termlib.pro \ proto/ui.pro \ proto/undo.pro \ + proto/userfunc.pro \ proto/window.pro all: Vim xxd/Xxd @@ -253,6 +262,9 @@ obj/crypt.o: crypt.c obj/crypt_zip.o: crypt_zip.c $(CCSYM) $@ crypt_zip.c +obj/dict.o: dict.c + $(CCSYM) $@ dict.c + obj/diff.o: diff.c $(CCSYM) $@ diff.c @@ -302,6 +314,9 @@ obj/hashtab.o: hashtab.c obj/json.o: json.c $(CCSYM) $@ json.c +obj/list.o: list.c + $(CCSYM) $@ list.c + # Don't use $(SYMS) here, because main.c defines EXTERN obj/main.o: main.c option.h globals.h $(CCNOSYM) $@ main.c @@ -386,5 +401,8 @@ obj/ui.o: ui.c obj/undo.o: undo.c $(CCSYM) $@ undo.c +obj/userfunc.o: userfunc.c + $(CCSYM) $@ userfunc.c + obj/window.o: window.c $(CCSYM) $@ window.c diff --git a/src/Make_morph.mak b/src/Make_morph.mak index 33342eba12..cfac107813 100644 --- a/src/Make_morph.mak +++ b/src/Make_morph.mak @@ -30,6 +30,7 @@ SRC = arabic.c \ charset.c \ crypt.c \ crypt_zip.c \ + dict.c \ diff.c \ digraph.c \ edit.c \ @@ -46,6 +47,7 @@ SRC = arabic.c \ hardcopy.c \ hashtab.c \ json.c \ + list.c \ main.c \ mark.c \ mbyte.c \ @@ -72,6 +74,7 @@ SRC = arabic.c \ term.c \ ui.c \ undo.c \ + userfunc.c \ version.c \ window.c \ diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index dc27809b22..fd6b999275 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -554,6 +554,7 @@ OBJ = \ $(OUTDIR)\charset.obj \ $(OUTDIR)\crypt.obj \ $(OUTDIR)\crypt_zip.obj \ + $(OUTDIR)\dict.obj \ $(OUTDIR)\diff.obj \ $(OUTDIR)\digraph.obj \ $(OUTDIR)\edit.obj \ @@ -570,6 +571,7 @@ OBJ = \ $(OUTDIR)\hardcopy.obj \ $(OUTDIR)\hashtab.obj \ $(OUTDIR)\json.obj \ + $(OUTDIR)\list.obj \ $(OUTDIR)\main.obj \ $(OUTDIR)\mark.obj \ $(OUTDIR)\mbyte.obj \ @@ -599,6 +601,7 @@ OBJ = \ $(OUTDIR)\term.obj \ $(OUTDIR)\ui.obj \ $(OUTDIR)\undo.obj \ + $(OUTDIR)\userfunc.obj \ $(OUTDIR)\window.obj \ $(OUTDIR)\vim.res @@ -1162,6 +1165,8 @@ $(OUTDIR)/crypt.obj: $(OUTDIR) crypt.c $(INCL) $(OUTDIR)/crypt_zip.obj: $(OUTDIR) crypt_zip.c $(INCL) +$(OUTDIR)/dict.obj: $(OUTDIR) dictf.c $(INCL) + $(OUTDIR)/diff.obj: $(OUTDIR) diff.c $(INCL) $(OUTDIR)/digraph.obj: $(OUTDIR) digraph.c $(INCL) @@ -1241,6 +1246,8 @@ $(OUTDIR)/iscygpty.obj: $(OUTDIR) iscygpty.c $(CUI_INCL) $(OUTDIR)/json.obj: $(OUTDIR) json.c $(INCL) +$(OUTDIR)/list.obj: $(OUTDIR) list.c $(INCL) + $(OUTDIR)/main.obj: $(OUTDIR) main.c $(INCL) $(CUI_INCL) $(OUTDIR)/mark.obj: $(OUTDIR) mark.c $(INCL) @@ -1306,6 +1313,8 @@ $(OUTDIR)/ui.obj: $(OUTDIR) ui.c $(INCL) $(OUTDIR)/undo.obj: $(OUTDIR) undo.c $(INCL) +$(OUTDIR)/userfunc.obj: $(OUTDIR) userfunc.c $(INCL) + $(OUTDIR)/window.obj: $(OUTDIR) window.c $(INCL) $(OUTDIR)/xpm_w32.obj: $(OUTDIR) xpm_w32.c @@ -1358,6 +1367,7 @@ proto.h: \ proto/charset.pro \ proto/crypt.pro \ proto/crypt_zip.pro \ + proto/dict.pro \ proto/diff.pro \ proto/digraph.pro \ proto/edit.pro \ @@ -1373,6 +1383,7 @@ proto.h: \ proto/hardcopy.pro \ proto/hashtab.pro \ proto/json.pro \ + proto/list.pro \ proto/main.pro \ proto/mark.pro \ proto/memfile.pro \ @@ -1401,6 +1412,7 @@ proto.h: \ proto/term.pro \ proto/ui.pro \ proto/undo.pro \ + proto/userfunc.pro \ proto/window.pro \ $(NETBEANS_PRO) \ $(CHANNEL_PRO) diff --git a/src/Make_sas.mak b/src/Make_sas.mak index 6ebd7d0847..b466173c77 100644 --- a/src/Make_sas.mak +++ b/src/Make_sas.mak @@ -95,6 +95,7 @@ SRC = \ charset.c \ crypt.c \ crypt_zip.c \ + dict.c \ diff.c \ digraph.c \ edit.c \ @@ -111,6 +112,7 @@ SRC = \ hardcopy.c \ hashtab.c \ json.c \ + list.c \ main.c \ mark.c \ memfile.c \ @@ -137,6 +139,7 @@ SRC = \ term.c \ ui.c \ undo.c \ + userfunc.c \ window.c \ version.c @@ -147,6 +150,7 @@ OBJ = \ charset.o \ crypt.o \ crypt_zip.o \ + dict.o \ diff.o \ digraph.o \ edit.o \ @@ -163,6 +167,7 @@ OBJ = \ hardcopy.o \ hashtab.o \ json.o \ + list.o \ main.o \ mark.o \ memfile.o \ @@ -189,6 +194,7 @@ OBJ = \ term.o \ ui.o \ undo.o \ + userfunc.o \ window.o \ $(TERMLIB) @@ -199,6 +205,7 @@ PRO = \ proto/charset.pro \ proto/crypt.pro \ proto/crypt_zip.pro \ + proto/dict.pro \ proto/diff.pro \ proto/digraph.pro \ proto/edit.pro \ @@ -215,6 +222,7 @@ PRO = \ proto/hardcopy.pro \ proto/hashtab.pro \ proto/json.pro \ + proto/list.pro \ proto/main.pro \ proto/mark.pro \ proto/memfile.pro \ @@ -242,6 +250,7 @@ PRO = \ proto/termlib.pro \ proto/ui.pro \ proto/undo.pro \ + proto/userfunc.pro \ proto/window.pro all: proto Vim @@ -311,6 +320,8 @@ crypt.o: crypt.c proto/crypt.pro: crypt.c crypt_zip.o: crypt_zip.c proto/crypt_zip.pro: crypt_zip.c +dict.o: dict.c +proto/dict.pro: dict.c diff.o: diff.c proto/diff.pro: diff.c digraph.o: digraph.c @@ -343,6 +354,8 @@ hashtab.o: hashtab.c proto/hashtab.pro: hashtab.c json.o: json.c proto/json.pro: json.c +list.o: list.c +proto/list.pro: list.c main.o: main.c proto/main.pro: main.c mark.o: mark.c @@ -397,4 +410,6 @@ ui.o: ui.c proto/ui.pro: ui.c undo.o: undo.c proto/undo.pro: undo.c +userfunc.o: userfunc.c +proto/userfunc.pro: userfunc.c window.o: window.c diff --git a/src/version.c b/src/version.c index 8354d92c6e..3c04f4c305 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2059, /**/ 2058, /**/ From a9093fe0946032b1bcaecaad82bfaf6763195aa4 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 17 Jul 2016 19:02:16 +0200 Subject: [PATCH 3/9] patch 7.4.2060 Problem: Wrong file name. Solution: Fix typo. --- src/Make_mvc.mak | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index fd6b999275..10a1cd8427 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -1165,7 +1165,7 @@ $(OUTDIR)/crypt.obj: $(OUTDIR) crypt.c $(INCL) $(OUTDIR)/crypt_zip.obj: $(OUTDIR) crypt_zip.c $(INCL) -$(OUTDIR)/dict.obj: $(OUTDIR) dictf.c $(INCL) +$(OUTDIR)/dict.obj: $(OUTDIR) dict.c $(INCL) $(OUTDIR)/diff.obj: $(OUTDIR) diff.c $(INCL) diff --git a/src/version.c b/src/version.c index 3c04f4c305..66bc07ea84 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2060, /**/ 2059, /**/ From e87e6dddc2b2a99572ec0db0833c052214c4fbd3 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 17 Jul 2016 19:25:04 +0200 Subject: [PATCH 4/9] patch 7.4.2061 Problem: qf_init_ext() is too big. Solution: Move code to qf_parse_line() (Yegappan Lakshmanan) --- src/quickfix.c | 677 ++++++++++++++++++---------------- src/testdir/test_quickfix.vim | 24 ++ src/version.c | 2 + 3 files changed, 382 insertions(+), 321 deletions(-) diff --git a/src/quickfix.c b/src/quickfix.c index 1145896e1e..b2b6e8e294 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -410,6 +410,11 @@ parse_efm_option(char_u *efm) if (errmsg == NULL) goto parse_efm_end; + /* + * Each part of the format string is copied and modified from errorformat + * to regex prog. Only a few % characters are allowed. + */ + /* * Get some space to modify the format string into. */ @@ -474,7 +479,8 @@ enum { QF_FAIL = 0, QF_OK = 1, QF_END_OF_INPUT = 2, - QF_NOMEM = 3 + QF_NOMEM = 3, + QF_IGNORE_LINE = 4 }; typedef struct { @@ -764,6 +770,326 @@ qf_get_nextline(qfstate_T *state) return QF_OK; } +typedef struct { + char_u *namebuf; + char_u *errmsg; + int errmsglen; + long lnum; + int col; + char_u use_viscol; + char_u *pattern; + int enr; + int type; + int valid; +} qffields_T; + +/* + * Parse a line and get the quickfix fields. + * Return the QF_ status. + */ + static int +qf_parse_line( + qf_info_T *qi, + char_u *linebuf, + int linelen, + efm_T *fmt_first, + qffields_T *fields) +{ + efm_T *fmt_ptr; + static efm_T *fmt_start = NULL; /* cached across calls */ + char_u *ptr; + int len; + int i; + int idx = 0; + char_u *tail = NULL; + regmatch_T regmatch; + + /* Always ignore case when looking for a matching error. */ + regmatch.rm_ic = TRUE; + + /* If there was no %> item start at the first pattern */ + if (fmt_start == NULL) + fmt_ptr = fmt_first; + else + { + fmt_ptr = fmt_start; + fmt_start = NULL; + } + + /* + * Try to match each part of 'errorformat' until we find a complete + * match or no match. + */ + fields->valid = TRUE; +restofline: + for ( ; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next) + { + int r; + + idx = fmt_ptr->prefix; + if (qi->qf_multiscan && vim_strchr((char_u *)"OPQ", idx) == NULL) + continue; + fields->namebuf[0] = NUL; + fields->pattern[0] = NUL; + if (!qi->qf_multiscan) + fields->errmsg[0] = NUL; + fields->lnum = 0; + fields->col = 0; + fields->use_viscol = FALSE; + fields->enr = -1; + fields->type = 0; + tail = NULL; + + regmatch.regprog = fmt_ptr->prog; + r = vim_regexec(®match, linebuf, (colnr_T)0); + fmt_ptr->prog = regmatch.regprog; + if (r) + { + if ((idx == 'C' || idx == 'Z') && !qi->qf_multiline) + continue; + if (vim_strchr((char_u *)"EWI", idx) != NULL) + fields->type = idx; + else + fields->type = 0; + /* + * Extract error message data from matched line. + * We check for an actual submatch, because "\[" and "\]" in + * the 'errorformat' may cause the wrong submatch to be used. + */ + if ((i = (int)fmt_ptr->addr[0]) > 0) /* %f */ + { + int c; + + if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) + continue; + + /* Expand ~/file and $HOME/file to full path. */ + c = *regmatch.endp[i]; + *regmatch.endp[i] = NUL; + expand_env(regmatch.startp[i], fields->namebuf, CMDBUFFSIZE); + *regmatch.endp[i] = c; + + if (vim_strchr((char_u *)"OPQ", idx) != NULL + && mch_getperm(fields->namebuf) == -1) + continue; + } + if ((i = (int)fmt_ptr->addr[1]) > 0) /* %n */ + { + if (regmatch.startp[i] == NULL) + continue; + fields->enr = (int)atol((char *)regmatch.startp[i]); + } + if ((i = (int)fmt_ptr->addr[2]) > 0) /* %l */ + { + if (regmatch.startp[i] == NULL) + continue; + fields->lnum = atol((char *)regmatch.startp[i]); + } + if ((i = (int)fmt_ptr->addr[3]) > 0) /* %c */ + { + if (regmatch.startp[i] == NULL) + continue; + fields->col = (int)atol((char *)regmatch.startp[i]); + } + if ((i = (int)fmt_ptr->addr[4]) > 0) /* %t */ + { + if (regmatch.startp[i] == NULL) + continue; + fields->type = *regmatch.startp[i]; + } + if (fmt_ptr->flags == '+' && !qi->qf_multiscan) /* %+ */ + { + if (linelen > fields->errmsglen) { + /* linelen + null terminator */ + if ((fields->errmsg = vim_realloc(fields->errmsg, + linelen + 1)) == NULL) + return QF_NOMEM; + fields->errmsglen = linelen + 1; + } + vim_strncpy(fields->errmsg, linebuf, linelen); + } + else if ((i = (int)fmt_ptr->addr[5]) > 0) /* %m */ + { + if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) + continue; + len = (int)(regmatch.endp[i] - regmatch.startp[i]); + if (len > fields->errmsglen) { + /* len + null terminator */ + if ((fields->errmsg = vim_realloc(fields->errmsg, len + 1)) + == NULL) + return QF_NOMEM; + fields->errmsglen = len + 1; + } + vim_strncpy(fields->errmsg, regmatch.startp[i], len); + } + if ((i = (int)fmt_ptr->addr[6]) > 0) /* %r */ + { + if (regmatch.startp[i] == NULL) + continue; + tail = regmatch.startp[i]; + } + if ((i = (int)fmt_ptr->addr[7]) > 0) /* %p */ + { + char_u *match_ptr; + + if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) + continue; + fields->col = 0; + for (match_ptr = regmatch.startp[i]; + match_ptr != regmatch.endp[i]; ++match_ptr) + { + ++fields->col; + if (*match_ptr == TAB) + { + fields->col += 7; + fields->col -= fields->col % 8; + } + } + ++fields->col; + fields->use_viscol = TRUE; + } + if ((i = (int)fmt_ptr->addr[8]) > 0) /* %v */ + { + if (regmatch.startp[i] == NULL) + continue; + fields->col = (int)atol((char *)regmatch.startp[i]); + fields->use_viscol = TRUE; + } + if ((i = (int)fmt_ptr->addr[9]) > 0) /* %s */ + { + if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) + continue; + len = (int)(regmatch.endp[i] - regmatch.startp[i]); + if (len > CMDBUFFSIZE - 5) + len = CMDBUFFSIZE - 5; + STRCPY(fields->pattern, "^\\V"); + STRNCAT(fields->pattern, regmatch.startp[i], len); + fields->pattern[len + 3] = '\\'; + fields->pattern[len + 4] = '$'; + fields->pattern[len + 5] = NUL; + } + break; + } + } + qi->qf_multiscan = FALSE; + + if (fmt_ptr == NULL || idx == 'D' || idx == 'X') + { + if (fmt_ptr != NULL) + { + if (idx == 'D') /* enter directory */ + { + if (*fields->namebuf == NUL) + { + EMSG(_("E379: Missing or empty directory name")); + return QF_FAIL; + } + qi->qf_directory = + qf_push_dir(fields->namebuf, &qi->qf_dir_stack, FALSE); + if (qi->qf_directory == NULL) + return QF_FAIL; + } + else if (idx == 'X') /* leave directory */ + qi->qf_directory = qf_pop_dir(&qi->qf_dir_stack); + } + fields->namebuf[0] = NUL; /* no match found, remove file name */ + fields->lnum = 0; /* don't jump to this line */ + fields->valid = FALSE; + if (linelen > fields->errmsglen) { + /* linelen + null terminator */ + if ((fields->errmsg = vim_realloc(fields->errmsg, + linelen + 1)) == NULL) + return QF_NOMEM; + fields->errmsglen = linelen + 1; + } + /* copy whole line to error message */ + vim_strncpy(fields->errmsg, linebuf, linelen); + if (fmt_ptr == NULL) + qi->qf_multiline = qi->qf_multiignore = FALSE; + } + else if (fmt_ptr != NULL) + { + /* honor %> item */ + if (fmt_ptr->conthere) + fmt_start = fmt_ptr; + + if (vim_strchr((char_u *)"AEWI", idx) != NULL) + { + qi->qf_multiline = TRUE; /* start of a multi-line message */ + qi->qf_multiignore = FALSE; /* reset continuation */ + } + else if (vim_strchr((char_u *)"CZ", idx) != NULL) + { /* continuation of multi-line msg */ + qfline_T *qfprev = qi->qf_lists[qi->qf_curlist].qf_last; + + if (qfprev == NULL) + return QF_FAIL; + if (*fields->errmsg && !qi->qf_multiignore) + { + len = (int)STRLEN(qfprev->qf_text); + if ((ptr = alloc((unsigned)(len + STRLEN(fields->errmsg) + 2))) + == NULL) + return QF_FAIL; + STRCPY(ptr, qfprev->qf_text); + vim_free(qfprev->qf_text); + qfprev->qf_text = ptr; + *(ptr += len) = '\n'; + STRCPY(++ptr, fields->errmsg); + } + if (qfprev->qf_nr == -1) + qfprev->qf_nr = fields->enr; + if (vim_isprintc(fields->type) && !qfprev->qf_type) + /* only printable chars allowed */ + qfprev->qf_type = fields->type; + + if (!qfprev->qf_lnum) + qfprev->qf_lnum = fields->lnum; + if (!qfprev->qf_col) + qfprev->qf_col = fields->col; + qfprev->qf_viscol = fields->use_viscol; + if (!qfprev->qf_fnum) + qfprev->qf_fnum = qf_get_fnum(qi, qi->qf_directory, + *fields->namebuf || qi->qf_directory != NULL + ? fields->namebuf + : qi->qf_currfile != NULL && fields->valid + ? qi->qf_currfile : 0); + if (idx == 'Z') + qi->qf_multiline = qi->qf_multiignore = FALSE; + line_breakcheck(); + return QF_IGNORE_LINE; + } + else if (vim_strchr((char_u *)"OPQ", idx) != NULL) + { + /* global file names */ + fields->valid = FALSE; + if (*fields->namebuf == NUL || mch_getperm(fields->namebuf) >= 0) + { + if (*fields->namebuf && idx == 'P') + qi->qf_currfile = + qf_push_dir(fields->namebuf, &qi->qf_file_stack, TRUE); + else if (idx == 'Q') + qi->qf_currfile = qf_pop_dir(&qi->qf_file_stack); + *fields->namebuf = NUL; + if (tail && *tail) + { + STRMOVE(IObuff, skipwhite(tail)); + qi->qf_multiscan = TRUE; + goto restofline; + } + } + } + if (fmt_ptr->flags == '-') /* generally exclude this line */ + { + if (qi->qf_multiline) + /* also exclude continuation lines */ + qi->qf_multiignore = TRUE; + return QF_IGNORE_LINE; + } + } + + return QF_OK; +} + /* * Read the errorfile "efile" into memory, line by line, building the error * list. @@ -786,40 +1112,24 @@ qf_init_ext( linenr_T lnumlast, /* last line number to use */ char_u *qf_title) { - char_u *namebuf; - char_u *errmsg; - int errmsglen; - char_u *pattern; qfstate_T state = {NULL, 0, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, 0}; - int col = 0; - char_u use_viscol = FALSE; - int type = 0; - int valid; - long lnum = 0L; - int enr = 0; + qffields_T fields = {NULL, NULL, 0, 0L, 0, FALSE, NULL, 0, 0, 0}; #ifdef FEAT_WINDOWS qfline_T *old_last = NULL; #endif static efm_T *fmt_first = NULL; - efm_T *fmt_ptr; - efm_T *fmt_start = NULL; char_u *efm; static char_u *last_efm = NULL; - char_u *ptr; - int len; - int i; - int idx = 0; int retval = -1; /* default: return error flag */ int status; - char_u *tail = NULL; - regmatch_T regmatch; - namebuf = alloc_id(CMDBUFFSIZE + 1, aid_qf_namebuf); - errmsglen = CMDBUFFSIZE + 1; - errmsg = alloc_id(errmsglen, aid_qf_errmsg); - pattern = alloc_id(CMDBUFFSIZE + 1, aid_qf_pattern); - if (namebuf == NULL || errmsg == NULL || pattern == NULL) + fields.namebuf = alloc_id(CMDBUFFSIZE + 1, aid_qf_namebuf); + fields.errmsglen = CMDBUFFSIZE + 1; + fields.errmsg = alloc_id(fields.errmsglen, aid_qf_errmsg); + fields.pattern = alloc_id(CMDBUFFSIZE + 1, aid_qf_pattern); + if (fields.namebuf == NULL || fields.errmsg == NULL || + fields.pattern == NULL) goto qf_init_end; if (efile != NULL && (state.fd = mch_fopen((char *)efile, "r")) == NULL) @@ -839,10 +1149,6 @@ qf_init_ext( } #endif -/* - * Each part of the format string is copied and modified from errorformat to - * regex prog. Only a few % characters are allowed. - */ /* Use the local value of 'errorformat' if it's set. */ if (errorformat == p_efm && tv == NULL && *buf->b_p_efm != NUL) efm = buf->b_p_efm; @@ -890,9 +1196,6 @@ qf_init_ext( */ got_int = FALSE; - /* Always ignore case when looking for a matching error. */ - regmatch.rm_ic = TRUE; - if (tv != NULL) { if (tv->v_type == VAR_STRING) @@ -918,298 +1221,30 @@ qf_init_ext( if (status == QF_END_OF_INPUT) /* end of input */ break; - /* If there was no %> item start at the first pattern */ - if (fmt_start == NULL) - fmt_ptr = fmt_first; - else - { - fmt_ptr = fmt_start; - fmt_start = NULL; - } - - /* - * Try to match each part of 'errorformat' until we find a complete - * match or no match. - */ - valid = TRUE; -restofline: - for ( ; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next) - { - int r; - - idx = fmt_ptr->prefix; - if (qi->qf_multiscan && vim_strchr((char_u *)"OPQ", idx) == NULL) - continue; - namebuf[0] = NUL; - pattern[0] = NUL; - if (!qi->qf_multiscan) - errmsg[0] = NUL; - lnum = 0; - col = 0; - use_viscol = FALSE; - enr = -1; - type = 0; - tail = NULL; - - regmatch.regprog = fmt_ptr->prog; - r = vim_regexec(®match, state.linebuf, (colnr_T)0); - fmt_ptr->prog = regmatch.regprog; - if (r) - { - if ((idx == 'C' || idx == 'Z') && !qi->qf_multiline) - continue; - if (vim_strchr((char_u *)"EWI", idx) != NULL) - type = idx; - else - type = 0; - /* - * Extract error message data from matched line. - * We check for an actual submatch, because "\[" and "\]" in - * the 'errorformat' may cause the wrong submatch to be used. - */ - if ((i = (int)fmt_ptr->addr[0]) > 0) /* %f */ - { - int c; - - if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) - continue; - - /* Expand ~/file and $HOME/file to full path. */ - c = *regmatch.endp[i]; - *regmatch.endp[i] = NUL; - expand_env(regmatch.startp[i], namebuf, CMDBUFFSIZE); - *regmatch.endp[i] = c; - - if (vim_strchr((char_u *)"OPQ", idx) != NULL - && mch_getperm(namebuf) == -1) - continue; - } - if ((i = (int)fmt_ptr->addr[1]) > 0) /* %n */ - { - if (regmatch.startp[i] == NULL) - continue; - enr = (int)atol((char *)regmatch.startp[i]); - } - if ((i = (int)fmt_ptr->addr[2]) > 0) /* %l */ - { - if (regmatch.startp[i] == NULL) - continue; - lnum = atol((char *)regmatch.startp[i]); - } - if ((i = (int)fmt_ptr->addr[3]) > 0) /* %c */ - { - if (regmatch.startp[i] == NULL) - continue; - col = (int)atol((char *)regmatch.startp[i]); - } - if ((i = (int)fmt_ptr->addr[4]) > 0) /* %t */ - { - if (regmatch.startp[i] == NULL) - continue; - type = *regmatch.startp[i]; - } - if (fmt_ptr->flags == '+' && !qi->qf_multiscan) /* %+ */ - { - if (state.linelen > errmsglen) { - /* linelen + null terminator */ - if ((errmsg = vim_realloc(errmsg, - state.linelen + 1)) == NULL) - goto qf_init_end; - errmsglen = state.linelen + 1; - } - vim_strncpy(errmsg, state.linebuf, state.linelen); - } - else if ((i = (int)fmt_ptr->addr[5]) > 0) /* %m */ - { - if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) - continue; - len = (int)(regmatch.endp[i] - regmatch.startp[i]); - if (len > errmsglen) { - /* len + null terminator */ - if ((errmsg = vim_realloc(errmsg, len + 1)) - == NULL) - goto qf_init_end; - errmsglen = len + 1; - } - vim_strncpy(errmsg, regmatch.startp[i], len); - } - if ((i = (int)fmt_ptr->addr[6]) > 0) /* %r */ - { - if (regmatch.startp[i] == NULL) - continue; - tail = regmatch.startp[i]; - } - if ((i = (int)fmt_ptr->addr[7]) > 0) /* %p */ - { - char_u *match_ptr; - - if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) - continue; - col = 0; - for (match_ptr = regmatch.startp[i]; - match_ptr != regmatch.endp[i]; ++match_ptr) - { - ++col; - if (*match_ptr == TAB) - { - col += 7; - col -= col % 8; - } - } - ++col; - use_viscol = TRUE; - } - if ((i = (int)fmt_ptr->addr[8]) > 0) /* %v */ - { - if (regmatch.startp[i] == NULL) - continue; - col = (int)atol((char *)regmatch.startp[i]); - use_viscol = TRUE; - } - if ((i = (int)fmt_ptr->addr[9]) > 0) /* %s */ - { - if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) - continue; - len = (int)(regmatch.endp[i] - regmatch.startp[i]); - if (len > CMDBUFFSIZE - 5) - len = CMDBUFFSIZE - 5; - STRCPY(pattern, "^\\V"); - STRNCAT(pattern, regmatch.startp[i], len); - pattern[len + 3] = '\\'; - pattern[len + 4] = '$'; - pattern[len + 5] = NUL; - } - break; - } - } - qi->qf_multiscan = FALSE; - - if (fmt_ptr == NULL || idx == 'D' || idx == 'X') - { - if (fmt_ptr != NULL) - { - if (idx == 'D') /* enter directory */ - { - if (*namebuf == NUL) - { - EMSG(_("E379: Missing or empty directory name")); - goto error2; - } - qi->qf_directory = - qf_push_dir(namebuf, &qi->qf_dir_stack, FALSE); - if (qi->qf_directory == NULL) - goto error2; - } - else if (idx == 'X') /* leave directory */ - qi->qf_directory = qf_pop_dir(&qi->qf_dir_stack); - } - namebuf[0] = NUL; /* no match found, remove file name */ - lnum = 0; /* don't jump to this line */ - valid = FALSE; - if (state.linelen > errmsglen) { - /* linelen + null terminator */ - if ((errmsg = vim_realloc(errmsg, state.linelen + 1)) == NULL) - goto qf_init_end; - errmsglen = state.linelen + 1; - } - /* copy whole line to error message */ - vim_strncpy(errmsg, state.linebuf, state.linelen); - if (fmt_ptr == NULL) - qi->qf_multiline = qi->qf_multiignore = FALSE; - } - else if (fmt_ptr != NULL) - { - /* honor %> item */ - if (fmt_ptr->conthere) - fmt_start = fmt_ptr; - - if (vim_strchr((char_u *)"AEWI", idx) != NULL) - { - qi->qf_multiline = TRUE; /* start of a multi-line message */ - qi->qf_multiignore = FALSE; /* reset continuation */ - } - else if (vim_strchr((char_u *)"CZ", idx) != NULL) - { /* continuation of multi-line msg */ - qfline_T *qfprev = qi->qf_lists[qi->qf_curlist].qf_last; - - if (qfprev == NULL) - goto error2; - if (*errmsg && !qi->qf_multiignore) - { - len = (int)STRLEN(qfprev->qf_text); - if ((ptr = alloc((unsigned)(len + STRLEN(errmsg) + 2))) - == NULL) - goto error2; - STRCPY(ptr, qfprev->qf_text); - vim_free(qfprev->qf_text); - qfprev->qf_text = ptr; - *(ptr += len) = '\n'; - STRCPY(++ptr, errmsg); - } - if (qfprev->qf_nr == -1) - qfprev->qf_nr = enr; - if (vim_isprintc(type) && !qfprev->qf_type) - qfprev->qf_type = type; /* only printable chars allowed */ - if (!qfprev->qf_lnum) - qfprev->qf_lnum = lnum; - if (!qfprev->qf_col) - qfprev->qf_col = col; - qfprev->qf_viscol = use_viscol; - if (!qfprev->qf_fnum) - qfprev->qf_fnum = qf_get_fnum(qi, qi->qf_directory, - *namebuf || qi->qf_directory != NULL - ? namebuf - : qi->qf_currfile != NULL && valid - ? qi->qf_currfile : 0); - if (idx == 'Z') - qi->qf_multiline = qi->qf_multiignore = FALSE; - line_breakcheck(); - continue; - } - else if (vim_strchr((char_u *)"OPQ", idx) != NULL) - { - /* global file names */ - valid = FALSE; - if (*namebuf == NUL || mch_getperm(namebuf) >= 0) - { - if (*namebuf && idx == 'P') - qi->qf_currfile = - qf_push_dir(namebuf, &qi->qf_file_stack, TRUE); - else if (idx == 'Q') - qi->qf_currfile = qf_pop_dir(&qi->qf_file_stack); - *namebuf = NUL; - if (tail && *tail) - { - STRMOVE(IObuff, skipwhite(tail)); - qi->qf_multiscan = TRUE; - goto restofline; - } - } - } - if (fmt_ptr->flags == '-') /* generally exclude this line */ - { - if (qi->qf_multiline) - /* also exclude continuation lines */ - qi->qf_multiignore = TRUE; - continue; - } - } + status = qf_parse_line(qi, state.linebuf, state.linelen, fmt_first, + &fields); + if (status == QF_FAIL) + goto error2; + if (status == QF_NOMEM) + goto qf_init_end; + if (status == QF_IGNORE_LINE) + continue; if (qf_add_entry(qi, qi->qf_directory, - (*namebuf || qi->qf_directory != NULL) - ? namebuf - : ((qi->qf_currfile != NULL && valid) + (*fields.namebuf || qi->qf_directory != NULL) + ? fields.namebuf + : ((qi->qf_currfile != NULL && fields.valid) ? qi->qf_currfile : (char_u *)NULL), 0, - errmsg, - lnum, - col, - use_viscol, - pattern, - enr, - type, - valid) == FAIL) + fields.errmsg, + fields.lnum, + fields.col, + fields.use_viscol, + fields.pattern, + fields.enr, + fields.type, + fields.valid) == FAIL) goto error2; line_breakcheck(); } @@ -1243,9 +1278,9 @@ error2: qf_init_end: if (state.fd != NULL) fclose(state.fd); - vim_free(namebuf); - vim_free(errmsg); - vim_free(pattern); + vim_free(fields.namebuf); + vim_free(fields.errmsg); + vim_free(fields.pattern); vim_free(state.growbuf); #ifdef FEAT_WINDOWS diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index a4f8b35957..83f4d0cbef 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -922,6 +922,30 @@ function! Test_efm2() call assert_equal('E', l[0].type) call assert_equal("\nunknown variable 'i'", l[0].text) + " Test for %A, %C and other formats + let lines = [ + \"==============================================================", + \"FAIL: testGetTypeIdCachesResult (dbfacadeTest.DjsDBFacadeTest)", + \"--------------------------------------------------------------", + \"Traceback (most recent call last):", + \' File "unittests/dbfacadeTest.py", line 89, in testFoo', + \" self.assertEquals(34, dtid)", + \' File "/usr/lib/python2.2/unittest.py", line 286, in', + \" failUnlessEqual", + \" raise self.failureException, \\", + \"AssertionError: 34 != 33", + \"", + \"--------------------------------------------------------------", + \"Ran 27 tests in 0.063s" + \] + set efm=%C\ %.%#,%A\ \ File\ \"%f\"\\,\ line\ %l%.%#,%Z%[%^\ ]%\\@=%m + cgetexpr lines + let l = getqflist() + call assert_equal(8, len(l)) + call assert_equal(89, l[4].lnum) + call assert_equal(1, l[4].valid) + call assert_equal('unittests/dbfacadeTest.py', bufname(l[4].bufnr)) + let &efm = save_efm endfunction diff --git a/src/version.c b/src/version.c index 66bc07ea84..5be01ae8e8 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2061, /**/ 2060, /**/ From 840268400dc8fda62a14f8a084e8b1ea46619454 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 17 Jul 2016 20:37:43 +0200 Subject: [PATCH 5/9] patch 7.4.2062 Problem: Using dummy variable to compute struct member offset. Solution: Use offsetof(). --- src/globals.h | 12 ------------ src/macros.h | 11 +++++++++++ src/spell.c | 3 +-- src/version.c | 2 ++ src/vim.h | 3 +++ 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/globals.h b/src/globals.h index 07e3cf7b26..f33120214a 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1649,18 +1649,6 @@ EXTERN int did_add_timer INIT(= FALSE); #ifdef FEAT_EVAL EXTERN time_T time_for_testing INIT(= 0); -/* - * In a hashtab item "hi_key" points to "di_key" in a dictitem. - * This avoids adding a pointer to the hashtab item. - * DI2HIKEY() converts a dictitem pointer to a hashitem key pointer. - * HIKEY2DI() converts a hashitem key pointer to a dictitem pointer. - * HI2DI() converts a hashitem pointer to a dictitem pointer. - */ -EXTERN dictitem_T dumdi; -# define DI2HIKEY(di) ((di)->di_key) -# define HIKEY2DI(p) ((dictitem_T *)(p - (dumdi.di_key - (char_u *)&dumdi))) -# define HI2DI(hi) HIKEY2DI((hi)->hi_key) - /* Abort conversion to string after a recursion error. */ EXTERN int did_echo_string_emsg INIT(= FALSE); #endif diff --git a/src/macros.h b/src/macros.h index ca54cb42e0..ae784d6cc1 100644 --- a/src/macros.h +++ b/src/macros.h @@ -353,3 +353,14 @@ # endif # endif #endif + +/* + * In a hashtab item "hi_key" points to "di_key" in a dictitem. + * This avoids adding a pointer to the hashtab item. + * DI2HIKEY() converts a dictitem pointer to a hashitem key pointer. + * HIKEY2DI() converts a hashitem key pointer to a dictitem pointer. + * HI2DI() converts a hashitem pointer to a dictitem pointer. + */ +# define DI2HIKEY(di) ((di)->di_key) +# define HIKEY2DI(p) ((dictitem_T *)(p - offsetof(dictitem_T, di_key))) +# define HI2DI(hi) HIKEY2DI((hi)->hi_key) diff --git a/src/spell.c b/src/spell.c index af96891076..f6ab0dffb1 100644 --- a/src/spell.c +++ b/src/spell.c @@ -600,8 +600,7 @@ typedef struct wordcount_S char_u wc_word[1]; /* word, actually longer */ } wordcount_T; -static wordcount_T dumwc; -#define WC_KEY_OFF (unsigned)(dumwc.wc_word - (char_u *)&dumwc) +#define WC_KEY_OFF offsetof(wordcount_T, wc_word) #define HI2WC(hi) ((wordcount_T *)((hi)->hi_key - WC_KEY_OFF)) #define MAXWORDCOUNT 0xffff diff --git a/src/version.c b/src/version.c index 5be01ae8e8..665c93e928 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2062, /**/ 2061, /**/ diff --git a/src/vim.h b/src/vim.h index 5067c5a885..49fb8704cd 100644 --- a/src/vim.h +++ b/src/vim.h @@ -505,6 +505,9 @@ typedef unsigned long u8char_T; /* long should be 32 bits or more */ #endif #include +/* for offsetof() */ +#include + #if defined(HAVE_SYS_SELECT_H) && \ (!defined(HAVE_SYS_TIME_H) || defined(SYS_SELECT_WITH_SYS_TIME)) # include From 73dad1e64cb42842d8259cb1a255a6fa59822f76 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 17 Jul 2016 22:13:49 +0200 Subject: [PATCH 6/9] patch 7.4.2063 Problem: eval.c is still too big. Solution: Split off internal functions to evalfunc.c. --- Filelist | 2 + src/Make_bc5.mak | 1 + src/Make_cyg_ming.mak | 1 + src/Make_dice.mak | 4 + src/Make_ivc.mak | 5 + src/Make_manx.mak | 6 + src/Make_morph.mak | 1 + src/Make_mvc.mak | 4 + src/Make_sas.mak | 5 + src/Makefile | 10 + src/eval.c | 14145 +++------------------------------------ src/evalfunc.c | 12552 ++++++++++++++++++++++++++++++++++ src/globals.h | 2 + src/list.c | 42 +- src/proto.h | 1 + src/proto/eval.pro | 36 +- src/proto/evalfunc.pro | 13 + src/proto/list.pro | 1 + src/version.c | 2 + src/vim.h | 9 + 20 files changed, 13458 insertions(+), 13384 deletions(-) create mode 100644 src/evalfunc.c create mode 100644 src/proto/evalfunc.pro diff --git a/Filelist b/Filelist index dc9a2308b7..48778fc6c9 100644 --- a/Filelist +++ b/Filelist @@ -23,6 +23,7 @@ SRC_ALL = \ src/digraph.c \ src/edit.c \ src/eval.c \ + src/evalfunc.c \ src/ex_cmds.c \ src/ex_cmds.h \ src/ex_cmds2.c \ @@ -134,6 +135,7 @@ SRC_ALL = \ src/proto/digraph.pro \ src/proto/edit.pro \ src/proto/eval.pro \ + src/proto/evalfunc.pro \ src/proto/ex_cmds.pro \ src/proto/ex_cmds2.pro \ src/proto/ex_docmd.pro \ diff --git a/src/Make_bc5.mak b/src/Make_bc5.mak index 55a9e0f582..54665668ab 100644 --- a/src/Make_bc5.mak +++ b/src/Make_bc5.mak @@ -548,6 +548,7 @@ vimobj = \ $(OBJDIR)\digraph.obj \ $(OBJDIR)\edit.obj \ $(OBJDIR)\eval.obj \ + $(OBJDIR)\evalfunc.obj \ $(OBJDIR)\ex_cmds.obj \ $(OBJDIR)\ex_cmds2.obj \ $(OBJDIR)\ex_docmd.obj \ diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index cc68f442cd..a6edef632b 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -615,6 +615,7 @@ OBJ = \ $(OUTDIR)/digraph.o \ $(OUTDIR)/edit.o \ $(OUTDIR)/eval.o \ + $(OUTDIR)/evalfunc.o \ $(OUTDIR)/ex_cmds.o \ $(OUTDIR)/ex_cmds2.o \ $(OUTDIR)/ex_docmd.o \ diff --git a/src/Make_dice.mak b/src/Make_dice.mak index fe8f2c8b46..00d38f6f9a 100644 --- a/src/Make_dice.mak +++ b/src/Make_dice.mak @@ -37,6 +37,7 @@ SRC = \ digraph.c \ edit.c \ eval.c \ + evalfunc.c \ ex_cmds.c \ ex_cmds2.c \ ex_docmd.c \ @@ -91,6 +92,7 @@ OBJ = o/arabic.o \ o/digraph.o \ o/edit.o \ o/eval.o \ + o/evalfunc.o \ o/ex_cmds.o \ o/ex_cmds2.o \ o/ex_docmd.o \ @@ -175,6 +177,8 @@ o/edit.o: edit.c $(SYMS) o/eval.o: eval.c $(SYMS) +o/evalfunc.o: evalfunc.c $(SYMS) + o/ex_cmds.o: ex_cmds.c $(SYMS) o/ex_cmds2.o: ex_cmds2.c $(SYMS) diff --git a/src/Make_ivc.mak b/src/Make_ivc.mak index 71cdef05ad..449d537bc8 100644 --- a/src/Make_ivc.mak +++ b/src/Make_ivc.mak @@ -221,6 +221,7 @@ LINK32_OBJS= \ "$(INTDIR)/digraph.obj" \ "$(INTDIR)/edit.obj" \ "$(INTDIR)/eval.obj" \ + "$(INTDIR)/evalfunc.obj" \ "$(INTDIR)/ex_cmds.obj" \ "$(INTDIR)/ex_cmds2.obj" \ "$(INTDIR)/ex_docmd.obj" \ @@ -379,6 +380,10 @@ SOURCE=.\eval.c # End Source File # Begin Source File +SOURCE=.\evalfunc.c +# End Source File +# Begin Source File + SOURCE=.\ex_cmds.c # End Source File # Begin Source File diff --git a/src/Make_manx.mak b/src/Make_manx.mak index 81a2c192e8..1d8686c249 100644 --- a/src/Make_manx.mak +++ b/src/Make_manx.mak @@ -47,6 +47,7 @@ SRC = arabic.c \ digraph.c \ edit.c \ eval.c \ + evalfunc.c \ ex_cmds.c \ ex_cmds2.c \ ex_docmd.c \ @@ -103,6 +104,7 @@ OBJ = obj/arabic.o \ obj/digraph.o \ obj/edit.o \ obj/eval.o \ + obj/evalfunc.o \ obj/ex_cmds.o \ obj/ex_cmds2.o \ obj/ex_docmd.o \ @@ -157,6 +159,7 @@ PRO = proto/arabic.pro \ proto/digraph.pro \ proto/edit.pro \ proto/eval.pro \ + proto/evalfunc.pro \ proto/ex_cmds.pro \ proto/ex_cmds2.pro \ proto/ex_docmd.pro \ @@ -277,6 +280,9 @@ obj/edit.o: edit.c obj/eval.o: eval.c $(CCSYM) $@ eval.c +obj/evalfunc.o: evalfunc.c + $(CCSYM) $@ evalfunc.c + obj/ex_cmds.o: ex_cmds.c $(CCSYM) $@ ex_cmds.c diff --git a/src/Make_morph.mak b/src/Make_morph.mak index cfac107813..daebf81a3e 100644 --- a/src/Make_morph.mak +++ b/src/Make_morph.mak @@ -35,6 +35,7 @@ SRC = arabic.c \ digraph.c \ edit.c \ eval.c \ + evalfunc.c \ ex_cmds.c \ ex_cmds2.c \ ex_docmd.c \ diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 10a1cd8427..b331bf9dbf 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -559,6 +559,7 @@ OBJ = \ $(OUTDIR)\digraph.obj \ $(OUTDIR)\edit.obj \ $(OUTDIR)\eval.obj \ + $(OUTDIR)\evalfunc.obj \ $(OUTDIR)\ex_cmds.obj \ $(OUTDIR)\ex_cmds2.obj \ $(OUTDIR)\ex_docmd.obj \ @@ -1175,6 +1176,8 @@ $(OUTDIR)/edit.obj: $(OUTDIR) edit.c $(INCL) $(OUTDIR)/eval.obj: $(OUTDIR) eval.c $(INCL) +$(OUTDIR)/evalfunc.obj: $(OUTDIR) evalfunc.c $(INCL) + $(OUTDIR)/ex_cmds.obj: $(OUTDIR) ex_cmds.c $(INCL) $(OUTDIR)/ex_cmds2.obj: $(OUTDIR) ex_cmds2.c $(INCL) @@ -1372,6 +1375,7 @@ proto.h: \ proto/digraph.pro \ proto/edit.pro \ proto/eval.pro \ + proto/evalfunc.pro \ proto/ex_cmds.pro \ proto/ex_cmds2.pro \ proto/ex_docmd.pro \ diff --git a/src/Make_sas.mak b/src/Make_sas.mak index b466173c77..f29c4d35e9 100644 --- a/src/Make_sas.mak +++ b/src/Make_sas.mak @@ -100,6 +100,7 @@ SRC = \ digraph.c \ edit.c \ eval.c \ + evalfunc.c \ ex_cmds.c \ ex_cmds2.c \ ex_docmd.c \ @@ -155,6 +156,7 @@ OBJ = \ digraph.o \ edit.o \ eval.o \ + evalfunc.o \ ex_cmds.o \ ex_cmds2.o \ ex_docmd.o \ @@ -210,6 +212,7 @@ PRO = \ proto/digraph.pro \ proto/edit.pro \ proto/eval.pro \ + proto/evalfunc.pro \ proto/ex_cmds.pro \ proto/ex_cmds2.pro \ proto/ex_docmd.pro \ @@ -330,6 +333,8 @@ edit.o: edit.c proto/edit.pro: edit.c eval.o: eval.c proto/eval.pro: eval.c +evalfunc.o: evalfunc.c +proto/evalfunc.pro: evalfunc.c ex_cmds.o: ex_cmds.c proto/ex_cmds.pro: ex_cmds.c ex_cmds2.o: ex_cmds2.c diff --git a/src/Makefile b/src/Makefile index 7ab4d328f6..c888661eb3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1489,6 +1489,7 @@ BASIC_SRC = \ digraph.c \ edit.c \ eval.c \ + evalfunc.c \ ex_cmds.c \ ex_cmds2.c \ ex_docmd.c \ @@ -1593,6 +1594,7 @@ OBJ_COMMON = \ objects/digraph.o \ objects/edit.o \ objects/eval.o \ + objects/evalfunc.o \ objects/ex_cmds.o \ objects/ex_cmds2.o \ objects/ex_docmd.o \ @@ -1683,6 +1685,7 @@ PRO_AUTO = \ digraph.pro \ edit.pro \ eval.pro \ + evalfunc.pro \ ex_cmds.pro \ ex_cmds2.pro \ ex_docmd.pro \ @@ -2830,6 +2833,9 @@ objects/edit.o: edit.c objects/eval.o: eval.c $(CCC) -o $@ eval.c +objects/evalfunc.o: evalfunc.c + $(CCC) -o $@ evalfunc.c + objects/ex_cmds.o: ex_cmds.c $(CCC) -o $@ ex_cmds.c @@ -3220,6 +3226,10 @@ objects/eval.o: eval.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \ ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ gui_beval.h proto/gui_beval.pro alloc.h ex_cmds.h proto.h globals.h \ farsi.h arabic.h version.h +objects/evalfunc.o: evalfunc.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \ + ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ + gui_beval.h proto/gui_beval.pro alloc.h ex_cmds.h proto.h globals.h \ + farsi.h arabic.h version.h objects/ex_cmds.o: ex_cmds.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \ ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ gui_beval.h proto/gui_beval.pro alloc.h ex_cmds.h proto.h globals.h \ diff --git a/src/eval.c b/src/eval.c index f930e47b62..5821079a10 100644 --- a/src/eval.c +++ b/src/eval.c @@ -16,29 +16,15 @@ #if defined(FEAT_EVAL) || defined(PROTO) -#ifdef AMIGA -# include /* for strftime() */ -#endif - #ifdef VMS # include #endif -#ifdef MACOS -# include /* for time_t */ -#endif - #define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */ static char *e_letunexp = N_("E18: Unexpected characters in :let"); static char *e_undefvar = N_("E121: Undefined variable: %s"); static char *e_missbrac = N_("E111: Missing ']'"); -static char *e_listarg = N_("E686: Argument of %s must be a List"); -static char *e_listdictarg = N_("E712: Argument of %s must be a List or Dictionary"); -static char *e_listreq = N_("E714: List required"); -#ifdef FEAT_QUICKFIX -static char *e_stringreq = N_("E928: String required"); -#endif static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); static char *e_letwrong = N_("E734: Wrong variable type for %s="); static char *e_illvar = N_("E461: Illegal variable name: %s"); @@ -101,7 +87,6 @@ typedef struct * The reason to use this table anyway is for very quick access to the * variables with the VV_ defines. */ -#include "version.h" /* values for vv_flags: */ #define VV_COMPAT 1 /* compatible, also used without "v:" */ @@ -206,8 +191,6 @@ static struct vimvar static dictitem_T vimvars_var; /* variable used for v: */ #define vimvarht vimvardict.dv_hashtab -static void prepare_vimvar(int idx, typval_T *save_tv); -static void restore_vimvar(int idx, typval_T *save_tv); static int ex_let_vars(char_u *arg, typval_T *tv, int copy, int semicolon, int var_count, char_u *nextchars); static char_u *skip_var_list(char_u *arg, int *var_count, int *semicolon); static char_u *skip_var_one(char_u *arg); @@ -221,14 +204,12 @@ static void list_vim_vars(int *first); static void list_script_vars(int *first); static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first); static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, char_u *endchars, char_u *op); -static int check_changedtick(char_u *arg); static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, char_u *op); static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op); static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep); static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit); static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock); static void item_lock(typval_T *tv, int deep, int lock); -static int tv_islocked(typval_T *tv); static int eval2(char_u **arg, typval_T *rettv, int evaluate); static int eval3(char_u **arg, typval_T *rettv, int evaluate); @@ -238,445 +219,29 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string); static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string); static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose); -static int get_option_tv(char_u **arg, typval_T *rettv, int evaluate); static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate); static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate); static int free_unref_items(int copyID); static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate); -static int non_zero_arg(typval_T *argvars); -#ifdef FEAT_FLOAT -static void f_abs(typval_T *argvars, typval_T *rettv); -static void f_acos(typval_T *argvars, typval_T *rettv); -#endif -static void f_add(typval_T *argvars, typval_T *rettv); -static void f_and(typval_T *argvars, typval_T *rettv); -static void f_append(typval_T *argvars, typval_T *rettv); -static void f_argc(typval_T *argvars, typval_T *rettv); -static void f_argidx(typval_T *argvars, typval_T *rettv); -static void f_arglistid(typval_T *argvars, typval_T *rettv); -static void f_argv(typval_T *argvars, typval_T *rettv); -static void f_assert_equal(typval_T *argvars, typval_T *rettv); -static void f_assert_exception(typval_T *argvars, typval_T *rettv); -static void f_assert_fails(typval_T *argvars, typval_T *rettv); -static void f_assert_false(typval_T *argvars, typval_T *rettv); -static void f_assert_match(typval_T *argvars, typval_T *rettv); -static void f_assert_notequal(typval_T *argvars, typval_T *rettv); -static void f_assert_notmatch(typval_T *argvars, typval_T *rettv); -static void f_assert_true(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_asin(typval_T *argvars, typval_T *rettv); -static void f_atan(typval_T *argvars, typval_T *rettv); -static void f_atan2(typval_T *argvars, typval_T *rettv); -#endif -static void f_browse(typval_T *argvars, typval_T *rettv); -static void f_browsedir(typval_T *argvars, typval_T *rettv); -static void f_bufexists(typval_T *argvars, typval_T *rettv); -static void f_buflisted(typval_T *argvars, typval_T *rettv); -static void f_bufloaded(typval_T *argvars, typval_T *rettv); -static void f_bufname(typval_T *argvars, typval_T *rettv); -static void f_bufnr(typval_T *argvars, typval_T *rettv); -static void f_bufwinid(typval_T *argvars, typval_T *rettv); -static void f_bufwinnr(typval_T *argvars, typval_T *rettv); -static void f_byte2line(typval_T *argvars, typval_T *rettv); -static void byteidx(typval_T *argvars, typval_T *rettv, int comp); -static void f_byteidx(typval_T *argvars, typval_T *rettv); -static void f_byteidxcomp(typval_T *argvars, typval_T *rettv); -static void f_call(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_ceil(typval_T *argvars, typval_T *rettv); -#endif -#ifdef FEAT_JOB_CHANNEL -static void f_ch_close(typval_T *argvars, typval_T *rettv); -static void f_ch_evalexpr(typval_T *argvars, typval_T *rettv); -static void f_ch_evalraw(typval_T *argvars, typval_T *rettv); -static void f_ch_getbufnr(typval_T *argvars, typval_T *rettv); -static void f_ch_getjob(typval_T *argvars, typval_T *rettv); -static void f_ch_info(typval_T *argvars, typval_T *rettv); -static void f_ch_log(typval_T *argvars, typval_T *rettv); -static void f_ch_logfile(typval_T *argvars, typval_T *rettv); -static void f_ch_open(typval_T *argvars, typval_T *rettv); -static void f_ch_read(typval_T *argvars, typval_T *rettv); -static void f_ch_readraw(typval_T *argvars, typval_T *rettv); -static void f_ch_sendexpr(typval_T *argvars, typval_T *rettv); -static void f_ch_sendraw(typval_T *argvars, typval_T *rettv); -static void f_ch_setoptions(typval_T *argvars, typval_T *rettv); -static void f_ch_status(typval_T *argvars, typval_T *rettv); -#endif -static void f_changenr(typval_T *argvars, typval_T *rettv); -static void f_char2nr(typval_T *argvars, typval_T *rettv); -static void f_cindent(typval_T *argvars, typval_T *rettv); -static void f_clearmatches(typval_T *argvars, typval_T *rettv); -static void f_col(typval_T *argvars, typval_T *rettv); -#if defined(FEAT_INS_EXPAND) -static void f_complete(typval_T *argvars, typval_T *rettv); -static void f_complete_add(typval_T *argvars, typval_T *rettv); -static void f_complete_check(typval_T *argvars, typval_T *rettv); -#endif -static void f_confirm(typval_T *argvars, typval_T *rettv); -static void f_copy(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_cos(typval_T *argvars, typval_T *rettv); -static void f_cosh(typval_T *argvars, typval_T *rettv); -#endif -static void f_count(typval_T *argvars, typval_T *rettv); -static void f_cscope_connection(typval_T *argvars, typval_T *rettv); -static void f_cursor(typval_T *argsvars, typval_T *rettv); -static void f_deepcopy(typval_T *argvars, typval_T *rettv); -static void f_delete(typval_T *argvars, typval_T *rettv); -static void f_did_filetype(typval_T *argvars, typval_T *rettv); -static void f_diff_filler(typval_T *argvars, typval_T *rettv); -static void f_diff_hlID(typval_T *argvars, typval_T *rettv); -static void f_empty(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_executable(typval_T *argvars, typval_T *rettv); -static void f_execute(typval_T *argvars, typval_T *rettv); -static void f_exepath(typval_T *argvars, typval_T *rettv); -static void f_exists(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_exp(typval_T *argvars, typval_T *rettv); -#endif -static void f_expand(typval_T *argvars, typval_T *rettv); -static void f_extend(typval_T *argvars, typval_T *rettv); -static void f_feedkeys(typval_T *argvars, typval_T *rettv); -static void f_filereadable(typval_T *argvars, typval_T *rettv); -static void f_filewritable(typval_T *argvars, typval_T *rettv); -static void f_filter(typval_T *argvars, typval_T *rettv); -static void f_finddir(typval_T *argvars, typval_T *rettv); -static void f_findfile(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_float2nr(typval_T *argvars, typval_T *rettv); -static void f_floor(typval_T *argvars, typval_T *rettv); -static void f_fmod(typval_T *argvars, typval_T *rettv); -#endif -static void f_fnameescape(typval_T *argvars, typval_T *rettv); -static void f_fnamemodify(typval_T *argvars, typval_T *rettv); -static void f_foldclosed(typval_T *argvars, typval_T *rettv); -static void f_foldclosedend(typval_T *argvars, typval_T *rettv); -static void f_foldlevel(typval_T *argvars, typval_T *rettv); -static void f_foldtext(typval_T *argvars, typval_T *rettv); -static void f_foldtextresult(typval_T *argvars, typval_T *rettv); -static void f_foreground(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_getbufline(typval_T *argvars, typval_T *rettv); -static void f_getbufvar(typval_T *argvars, typval_T *rettv); -static void f_getchar(typval_T *argvars, typval_T *rettv); -static void f_getcharmod(typval_T *argvars, typval_T *rettv); -static void f_getcharsearch(typval_T *argvars, typval_T *rettv); -static void f_getcmdline(typval_T *argvars, typval_T *rettv); -#if defined(FEAT_CMDL_COMPL) -static void f_getcompletion(typval_T *argvars, typval_T *rettv); -#endif -static void f_getcmdpos(typval_T *argvars, typval_T *rettv); -static void f_getcmdtype(typval_T *argvars, typval_T *rettv); -static void f_getcmdwintype(typval_T *argvars, typval_T *rettv); -static void f_getcwd(typval_T *argvars, typval_T *rettv); -static void f_getfontname(typval_T *argvars, typval_T *rettv); -static void f_getfperm(typval_T *argvars, typval_T *rettv); -static void f_getfsize(typval_T *argvars, typval_T *rettv); -static void f_getftime(typval_T *argvars, typval_T *rettv); -static void f_getftype(typval_T *argvars, typval_T *rettv); -static void f_getline(typval_T *argvars, typval_T *rettv); -static void f_getmatches(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_getpos(typval_T *argvars, typval_T *rettv); -static void f_getqflist(typval_T *argvars, typval_T *rettv); -static void f_getreg(typval_T *argvars, typval_T *rettv); -static void f_getregtype(typval_T *argvars, typval_T *rettv); -static void f_gettabvar(typval_T *argvars, typval_T *rettv); -static void f_gettabwinvar(typval_T *argvars, typval_T *rettv); -static void f_getwinposx(typval_T *argvars, typval_T *rettv); -static void f_getwinposy(typval_T *argvars, typval_T *rettv); -static void f_getwinvar(typval_T *argvars, typval_T *rettv); -static void f_glob(typval_T *argvars, typval_T *rettv); -static void f_globpath(typval_T *argvars, typval_T *rettv); -static void f_glob2regpat(typval_T *argvars, typval_T *rettv); -static void f_has(typval_T *argvars, typval_T *rettv); -static void f_has_key(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_histadd(typval_T *argvars, typval_T *rettv); -static void f_histdel(typval_T *argvars, typval_T *rettv); -static void f_histget(typval_T *argvars, typval_T *rettv); -static void f_histnr(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_iconv(typval_T *argvars, typval_T *rettv); -static void f_indent(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_insert(typval_T *argvars, typval_T *rettv); -static void f_invert(typval_T *argvars, typval_T *rettv); -static void f_isdirectory(typval_T *argvars, typval_T *rettv); -static void f_islocked(typval_T *argvars, typval_T *rettv); -#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H) -static void f_isnan(typval_T *argvars, typval_T *rettv); -#endif -static void f_items(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_JOB_CHANNEL -static void f_job_getchannel(typval_T *argvars, typval_T *rettv); -static void f_job_info(typval_T *argvars, typval_T *rettv); -static void f_job_setoptions(typval_T *argvars, typval_T *rettv); -static void f_job_start(typval_T *argvars, typval_T *rettv); -static void f_job_stop(typval_T *argvars, typval_T *rettv); -static void f_job_status(typval_T *argvars, typval_T *rettv); -#endif -static void f_join(typval_T *argvars, typval_T *rettv); -static void f_js_decode(typval_T *argvars, typval_T *rettv); -static void f_js_encode(typval_T *argvars, typval_T *rettv); -static void f_json_decode(typval_T *argvars, typval_T *rettv); -static void f_json_encode(typval_T *argvars, typval_T *rettv); -static void f_keys(typval_T *argvars, typval_T *rettv); -static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv); -static void f_len(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); -static void f_lispindent(typval_T *argvars, typval_T *rettv); -static void f_localtime(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_log(typval_T *argvars, typval_T *rettv); -static void f_log10(typval_T *argvars, typval_T *rettv); -#endif -#ifdef FEAT_LUA -static void f_luaeval(typval_T *argvars, typval_T *rettv); -#endif -static void f_map(typval_T *argvars, typval_T *rettv); -static void f_maparg(typval_T *argvars, typval_T *rettv); -static void f_mapcheck(typval_T *argvars, typval_T *rettv); -static void f_match(typval_T *argvars, typval_T *rettv); -static void f_matchadd(typval_T *argvars, typval_T *rettv); -static void f_matchaddpos(typval_T *argvars, typval_T *rettv); -static void f_matcharg(typval_T *argvars, typval_T *rettv); -static void f_matchdelete(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 vim_mkdir -static void f_mkdir(typval_T *argvars, typval_T *rettv); -#endif -static void f_mode(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); -static void f_pathshorten(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_PERL -static void f_perleval(typval_T *argvars, typval_T *rettv); -#endif -#ifdef FEAT_FLOAT -static void f_pow(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_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 -static void f_range(typval_T *argvars, typval_T *rettv); -static void f_readfile(typval_T *argvars, typval_T *rettv); -static void f_reltime(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_reltimefloat(typval_T *argvars, typval_T *rettv); -#endif -static void f_reltimestr(typval_T *argvars, typval_T *rettv); -static void f_remote_expr(typval_T *argvars, typval_T *rettv); -static void f_remote_foreground(typval_T *argvars, typval_T *rettv); -static void f_remote_peek(typval_T *argvars, typval_T *rettv); -static void f_remote_read(typval_T *argvars, typval_T *rettv); -static void f_remote_send(typval_T *argvars, typval_T *rettv); -static void f_remove(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); -static void f_resolve(typval_T *argvars, typval_T *rettv); -static void f_reverse(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_round(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_screencol(typval_T *argvars, typval_T *rettv); -static void f_screenrow(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_server2client(typval_T *argvars, typval_T *rettv); -static void f_serverlist(typval_T *argvars, typval_T *rettv); -static void f_setbufvar(typval_T *argvars, typval_T *rettv); -static void f_setcharsearch(typval_T *argvars, typval_T *rettv); -static void f_setcmdpos(typval_T *argvars, typval_T *rettv); -static void f_setfperm(typval_T *argvars, typval_T *rettv); -static void f_setline(typval_T *argvars, typval_T *rettv); -static void f_setloclist(typval_T *argvars, typval_T *rettv); -static void f_setmatches(typval_T *argvars, typval_T *rettv); -static void f_setpos(typval_T *argvars, typval_T *rettv); -static void f_setqflist(typval_T *argvars, typval_T *rettv); -static void f_setreg(typval_T *argvars, typval_T *rettv); -static void f_settabvar(typval_T *argvars, typval_T *rettv); -static void f_settabwinvar(typval_T *argvars, typval_T *rettv); -static void f_setwinvar(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_CRYPT -static void f_sha256(typval_T *argvars, typval_T *rettv); -#endif /* FEAT_CRYPT */ -static void f_shellescape(typval_T *argvars, typval_T *rettv); -static void f_shiftwidth(typval_T *argvars, typval_T *rettv); -static void f_simplify(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_sin(typval_T *argvars, typval_T *rettv); -static void f_sinh(typval_T *argvars, typval_T *rettv); -#endif -static void f_sort(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); -#ifdef FEAT_FLOAT -static void f_sqrt(typval_T *argvars, typval_T *rettv); -static void f_str2float(typval_T *argvars, typval_T *rettv); -#endif -static void f_str2nr(typval_T *argvars, typval_T *rettv); -static void f_strchars(typval_T *argvars, typval_T *rettv); -#ifdef HAVE_STRFTIME -static void f_strftime(typval_T *argvars, typval_T *rettv); -#endif -static void f_strgetchar(typval_T *argvars, typval_T *rettv); -static void f_stridx(typval_T *argvars, typval_T *rettv); -static void f_string(typval_T *argvars, typval_T *rettv); -static void f_strlen(typval_T *argvars, typval_T *rettv); -static void f_strcharpart(typval_T *argvars, typval_T *rettv); -static void f_strpart(typval_T *argvars, typval_T *rettv); -static void f_strridx(typval_T *argvars, typval_T *rettv); -static void f_strtrans(typval_T *argvars, typval_T *rettv); -static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv); -static void f_strwidth(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_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_system(typval_T *argvars, typval_T *rettv); -static void f_systemlist(typval_T *argvars, typval_T *rettv); -static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv); -static void f_tabpagenr(typval_T *argvars, typval_T *rettv); -static void f_tabpagewinnr(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_tempname(typval_T *argvars, typval_T *rettv); -static void f_test_alloc_fail(typval_T *argvars, typval_T *rettv); -static void f_test_autochdir(typval_T *argvars, typval_T *rettv); -static void f_test_disable_char_avail(typval_T *argvars, typval_T *rettv); -static void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_JOB_CHANNEL -static void f_test_null_channel(typval_T *argvars, typval_T *rettv); -#endif -static void f_test_null_dict(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_JOB_CHANNEL -static void f_test_null_job(typval_T *argvars, typval_T *rettv); -#endif -static void f_test_null_list(typval_T *argvars, typval_T *rettv); -static void f_test_null_partial(typval_T *argvars, typval_T *rettv); -static void f_test_null_string(typval_T *argvars, typval_T *rettv); -static void f_test_settime(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_tan(typval_T *argvars, typval_T *rettv); -static void f_tanh(typval_T *argvars, typval_T *rettv); -#endif -#ifdef FEAT_TIMERS -static void f_timer_start(typval_T *argvars, typval_T *rettv); -static void f_timer_stop(typval_T *argvars, typval_T *rettv); -#endif -static void f_tolower(typval_T *argvars, typval_T *rettv); -static void f_toupper(typval_T *argvars, typval_T *rettv); -static void f_tr(typval_T *argvars, typval_T *rettv); -#ifdef FEAT_FLOAT -static void f_trunc(typval_T *argvars, typval_T *rettv); -#endif -static void f_type(typval_T *argvars, typval_T *rettv); -static void f_undofile(typval_T *argvars, typval_T *rettv); -static void f_undotree(typval_T *argvars, typval_T *rettv); -static void f_uniq(typval_T *argvars, typval_T *rettv); -static void f_values(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_win_findbuf(typval_T *argvars, typval_T *rettv); -static void f_win_getid(typval_T *argvars, typval_T *rettv); -static void f_win_gotoid(typval_T *argvars, typval_T *rettv); -static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv); -static void f_win_id2win(typval_T *argvars, typval_T *rettv); -static void f_winbufnr(typval_T *argvars, typval_T *rettv); -static void f_wincol(typval_T *argvars, typval_T *rettv); -static void f_winheight(typval_T *argvars, typval_T *rettv); -static void f_winline(typval_T *argvars, typval_T *rettv); -static void f_winnr(typval_T *argvars, typval_T *rettv); -static void f_winrestcmd(typval_T *argvars, typval_T *rettv); -static void f_winrestview(typval_T *argvars, typval_T *rettv); -static void f_winsaveview(typval_T *argvars, typval_T *rettv); -static void f_winwidth(typval_T *argvars, typval_T *rettv); -static void f_writefile(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); -static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp); -static pos_T *var2fpos(typval_T *varp, int dollar_lnum, int *fnum); static int get_env_len(char_u **arg); -static int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose); static char_u * make_expanded_name(char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end); -static int get_var_tv(char_u *name, int len, typval_T *rettv, dictitem_T **dip, int verbose, int no_autoload); static typval_T *alloc_string_tv(char_u *string); -static void init_tv(typval_T *varp); -#ifdef FEAT_FLOAT -static float_T get_tv_float(typval_T *varp); -#endif -static linenr_T get_tv_lnum(typval_T *argvars); -static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf); -static dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload); static hashtab_T *find_var_ht(char_u *name, char_u **varname); static void delete_var(hashtab_T *ht, hashitem_T *hi); static void list_one_var(dictitem_T *v, char_u *prefix, int *first); static void list_one_var_a(char_u *prefix, char_u *name, int type, char_u *string, int *first); -static void set_var(char_u *name, typval_T *varp, int copy); -static int var_check_fixed(int flags, char_u *name, int use_gettext); static char_u *find_option_end(char_u **arg, int *opt_flags); -static win_T *find_win_by_nr(typval_T *vp, tabpage_T *tp); -static win_T *find_tabwin(typval_T *wvp, typval_T *tvp); -static void getwinvar(typval_T *argvars, typval_T *rettv, int off); -static int searchpair_cmn(typval_T *argvars, pos_T *match_pos); -static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp); -static void setwinvar(typval_T *argvars, typval_T *rettv, int off); -static int write_list(FILE *fd, list_T *list, int binary); -static void get_cmd_output_as_rettv(typval_T *argvars, typval_T *rettv, int retlist); - #ifdef EBCDIC static int compare_func_name(const void *s1, const void *s2); static void sortFunctions(); #endif +/* for VIM_VERSION_ defines */ +#include "version.h" + /* * Initialize the global and v: variables. */ @@ -2200,7 +1765,7 @@ ex_let_one( /* * If "arg" is equal to "b:changedtick" give an error and return TRUE. */ - static int + int check_changedtick(char_u *arg) { if (STRNCMP(arg, "b:changedtick", 13) == 0 && !eval_isnamec(arg[13])) @@ -3413,22 +2978,6 @@ item_lock(typval_T *tv, int deep, int lock) --recurse; } -/* - * Return TRUE if typeval "tv" is locked: Either that value is locked itself - * or it refers to a List or Dictionary that is locked. - */ - static int -tv_islocked(typval_T *tv) -{ - return (tv->v_lock & VAR_LOCKED) - || (tv->v_type == VAR_LIST - && tv->vval.v_list != NULL - && (tv->vval.v_list->lv_lock & VAR_LOCKED)) - || (tv->v_type == VAR_DICT - && tv->vval.v_dict != NULL - && (tv->vval.v_dict->dv_lock & VAR_LOCKED)); -} - #if (defined(FEAT_MENU) && defined(FEAT_MULTI_LANG)) || defined(PROTO) /* * Delete all "menutrans_" variables. @@ -5159,7 +4708,7 @@ eval_index( * "arg" is advanced to character after the option name. * Return OK or FAIL. */ - static int + int get_option_tv( char_u **arg, typval_T *rettv, /* when NULL, only check if option exists */ @@ -6421,12882 +5970,13 @@ get_env_tv(char_u **arg, typval_T *rettv, int evaluate) return OK; } -/* - * Array with names and number of arguments of all internal functions - * MUST BE KEPT SORTED IN strcmp() ORDER FOR BINARY SEARCH! - */ -static struct fst -{ - char *f_name; /* function name */ - char f_min_argc; /* minimal number of arguments */ - char f_max_argc; /* maximal number of arguments */ - void (*f_func)(typval_T *args, typval_T *rvar); - /* implementation of function */ -} functions[] = -{ -#ifdef FEAT_FLOAT - {"abs", 1, 1, f_abs}, - {"acos", 1, 1, f_acos}, /* WJMc */ -#endif - {"add", 2, 2, f_add}, - {"and", 2, 2, f_and}, - {"append", 2, 2, f_append}, - {"argc", 0, 0, f_argc}, - {"argidx", 0, 0, f_argidx}, - {"arglistid", 0, 2, f_arglistid}, - {"argv", 0, 1, f_argv}, -#ifdef FEAT_FLOAT - {"asin", 1, 1, f_asin}, /* WJMc */ -#endif - {"assert_equal", 2, 3, f_assert_equal}, - {"assert_exception", 1, 2, f_assert_exception}, - {"assert_fails", 1, 2, f_assert_fails}, - {"assert_false", 1, 2, f_assert_false}, - {"assert_match", 2, 3, f_assert_match}, - {"assert_notequal", 2, 3, f_assert_notequal}, - {"assert_notmatch", 2, 3, f_assert_notmatch}, - {"assert_true", 1, 2, f_assert_true}, -#ifdef FEAT_FLOAT - {"atan", 1, 1, f_atan}, - {"atan2", 2, 2, f_atan2}, -#endif - {"browse", 4, 4, f_browse}, - {"browsedir", 2, 2, f_browsedir}, - {"bufexists", 1, 1, f_bufexists}, - {"buffer_exists", 1, 1, f_bufexists}, /* obsolete */ - {"buffer_name", 1, 1, f_bufname}, /* obsolete */ - {"buffer_number", 1, 1, f_bufnr}, /* obsolete */ - {"buflisted", 1, 1, f_buflisted}, - {"bufloaded", 1, 1, f_bufloaded}, - {"bufname", 1, 1, f_bufname}, - {"bufnr", 1, 2, f_bufnr}, - {"bufwinid", 1, 1, f_bufwinid}, - {"bufwinnr", 1, 1, f_bufwinnr}, - {"byte2line", 1, 1, f_byte2line}, - {"byteidx", 2, 2, f_byteidx}, - {"byteidxcomp", 2, 2, f_byteidxcomp}, - {"call", 2, 3, f_call}, -#ifdef FEAT_FLOAT - {"ceil", 1, 1, f_ceil}, -#endif -#ifdef FEAT_JOB_CHANNEL - {"ch_close", 1, 1, f_ch_close}, - {"ch_evalexpr", 2, 3, f_ch_evalexpr}, - {"ch_evalraw", 2, 3, f_ch_evalraw}, - {"ch_getbufnr", 2, 2, f_ch_getbufnr}, - {"ch_getjob", 1, 1, f_ch_getjob}, - {"ch_info", 1, 1, f_ch_info}, - {"ch_log", 1, 2, f_ch_log}, - {"ch_logfile", 1, 2, f_ch_logfile}, - {"ch_open", 1, 2, f_ch_open}, - {"ch_read", 1, 2, f_ch_read}, - {"ch_readraw", 1, 2, f_ch_readraw}, - {"ch_sendexpr", 2, 3, f_ch_sendexpr}, - {"ch_sendraw", 2, 3, f_ch_sendraw}, - {"ch_setoptions", 2, 2, f_ch_setoptions}, - {"ch_status", 1, 1, f_ch_status}, -#endif - {"changenr", 0, 0, f_changenr}, - {"char2nr", 1, 2, f_char2nr}, - {"cindent", 1, 1, f_cindent}, - {"clearmatches", 0, 0, f_clearmatches}, - {"col", 1, 1, f_col}, -#if defined(FEAT_INS_EXPAND) - {"complete", 2, 2, f_complete}, - {"complete_add", 1, 1, f_complete_add}, - {"complete_check", 0, 0, f_complete_check}, -#endif - {"confirm", 1, 4, f_confirm}, - {"copy", 1, 1, f_copy}, -#ifdef FEAT_FLOAT - {"cos", 1, 1, f_cos}, - {"cosh", 1, 1, f_cosh}, -#endif - {"count", 2, 4, f_count}, - {"cscope_connection",0,3, f_cscope_connection}, - {"cursor", 1, 3, f_cursor}, - {"deepcopy", 1, 2, f_deepcopy}, - {"delete", 1, 2, f_delete}, - {"did_filetype", 0, 0, f_did_filetype}, - {"diff_filler", 1, 1, f_diff_filler}, - {"diff_hlID", 2, 2, f_diff_hlID}, - {"empty", 1, 1, f_empty}, - {"escape", 2, 2, f_escape}, - {"eval", 1, 1, f_eval}, - {"eventhandler", 0, 0, f_eventhandler}, - {"executable", 1, 1, f_executable}, - {"execute", 1, 2, f_execute}, - {"exepath", 1, 1, f_exepath}, - {"exists", 1, 1, f_exists}, -#ifdef FEAT_FLOAT - {"exp", 1, 1, f_exp}, -#endif - {"expand", 1, 3, f_expand}, - {"extend", 2, 3, f_extend}, - {"feedkeys", 1, 2, f_feedkeys}, - {"file_readable", 1, 1, f_filereadable}, /* obsolete */ - {"filereadable", 1, 1, f_filereadable}, - {"filewritable", 1, 1, f_filewritable}, - {"filter", 2, 2, f_filter}, - {"finddir", 1, 3, f_finddir}, - {"findfile", 1, 3, f_findfile}, -#ifdef FEAT_FLOAT - {"float2nr", 1, 1, f_float2nr}, - {"floor", 1, 1, f_floor}, - {"fmod", 2, 2, f_fmod}, -#endif - {"fnameescape", 1, 1, f_fnameescape}, - {"fnamemodify", 2, 2, f_fnamemodify}, - {"foldclosed", 1, 1, f_foldclosed}, - {"foldclosedend", 1, 1, f_foldclosedend}, - {"foldlevel", 1, 1, f_foldlevel}, - {"foldtext", 0, 0, f_foldtext}, - {"foldtextresult", 1, 1, f_foldtextresult}, - {"foreground", 0, 0, f_foreground}, - {"function", 1, 3, f_function}, - {"garbagecollect", 0, 1, f_garbagecollect}, - {"get", 2, 3, f_get}, - {"getbufline", 2, 3, f_getbufline}, - {"getbufvar", 2, 3, f_getbufvar}, - {"getchar", 0, 1, f_getchar}, - {"getcharmod", 0, 0, f_getcharmod}, - {"getcharsearch", 0, 0, f_getcharsearch}, - {"getcmdline", 0, 0, f_getcmdline}, - {"getcmdpos", 0, 0, f_getcmdpos}, - {"getcmdtype", 0, 0, f_getcmdtype}, - {"getcmdwintype", 0, 0, f_getcmdwintype}, -#if defined(FEAT_CMDL_COMPL) - {"getcompletion", 2, 2, f_getcompletion}, -#endif - {"getcurpos", 0, 0, f_getcurpos}, - {"getcwd", 0, 2, f_getcwd}, - {"getfontname", 0, 1, f_getfontname}, - {"getfperm", 1, 1, f_getfperm}, - {"getfsize", 1, 1, f_getfsize}, - {"getftime", 1, 1, f_getftime}, - {"getftype", 1, 1, f_getftype}, - {"getline", 1, 2, f_getline}, - {"getloclist", 1, 1, f_getqflist}, - {"getmatches", 0, 0, f_getmatches}, - {"getpid", 0, 0, f_getpid}, - {"getpos", 1, 1, f_getpos}, - {"getqflist", 0, 0, f_getqflist}, - {"getreg", 0, 3, f_getreg}, - {"getregtype", 0, 1, f_getregtype}, - {"gettabvar", 2, 3, f_gettabvar}, - {"gettabwinvar", 3, 4, f_gettabwinvar}, - {"getwinposx", 0, 0, f_getwinposx}, - {"getwinposy", 0, 0, f_getwinposy}, - {"getwinvar", 2, 3, f_getwinvar}, - {"glob", 1, 4, f_glob}, - {"glob2regpat", 1, 1, f_glob2regpat}, - {"globpath", 2, 5, f_globpath}, - {"has", 1, 1, f_has}, - {"has_key", 2, 2, f_has_key}, - {"haslocaldir", 0, 2, f_haslocaldir}, - {"hasmapto", 1, 3, f_hasmapto}, - {"highlightID", 1, 1, f_hlID}, /* obsolete */ - {"highlight_exists",1, 1, f_hlexists}, /* obsolete */ - {"histadd", 2, 2, f_histadd}, - {"histdel", 1, 2, f_histdel}, - {"histget", 1, 2, f_histget}, - {"histnr", 1, 1, f_histnr}, - {"hlID", 1, 1, f_hlID}, - {"hlexists", 1, 1, f_hlexists}, - {"hostname", 0, 0, f_hostname}, - {"iconv", 3, 3, f_iconv}, - {"indent", 1, 1, f_indent}, - {"index", 2, 4, f_index}, - {"input", 1, 3, f_input}, - {"inputdialog", 1, 3, f_inputdialog}, - {"inputlist", 1, 1, f_inputlist}, - {"inputrestore", 0, 0, f_inputrestore}, - {"inputsave", 0, 0, f_inputsave}, - {"inputsecret", 1, 2, f_inputsecret}, - {"insert", 2, 3, f_insert}, - {"invert", 1, 1, f_invert}, - {"isdirectory", 1, 1, f_isdirectory}, - {"islocked", 1, 1, f_islocked}, -#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H) - {"isnan", 1, 1, f_isnan}, -#endif - {"items", 1, 1, f_items}, -#ifdef FEAT_JOB_CHANNEL - {"job_getchannel", 1, 1, f_job_getchannel}, - {"job_info", 1, 1, f_job_info}, - {"job_setoptions", 2, 2, f_job_setoptions}, - {"job_start", 1, 2, f_job_start}, - {"job_status", 1, 1, f_job_status}, - {"job_stop", 1, 2, f_job_stop}, -#endif - {"join", 1, 2, f_join}, - {"js_decode", 1, 1, f_js_decode}, - {"js_encode", 1, 1, f_js_encode}, - {"json_decode", 1, 1, f_json_decode}, - {"json_encode", 1, 1, f_json_encode}, - {"keys", 1, 1, f_keys}, - {"last_buffer_nr", 0, 0, f_last_buffer_nr},/* obsolete */ - {"len", 1, 1, f_len}, - {"libcall", 3, 3, f_libcall}, - {"libcallnr", 3, 3, f_libcallnr}, - {"line", 1, 1, f_line}, - {"line2byte", 1, 1, f_line2byte}, - {"lispindent", 1, 1, f_lispindent}, - {"localtime", 0, 0, f_localtime}, -#ifdef FEAT_FLOAT - {"log", 1, 1, f_log}, - {"log10", 1, 1, f_log10}, -#endif -#ifdef FEAT_LUA - {"luaeval", 1, 2, f_luaeval}, -#endif - {"map", 2, 2, f_map}, - {"maparg", 1, 4, f_maparg}, - {"mapcheck", 1, 3, f_mapcheck}, - {"match", 2, 4, f_match}, - {"matchadd", 2, 5, f_matchadd}, - {"matchaddpos", 2, 5, f_matchaddpos}, - {"matcharg", 1, 1, f_matcharg}, - {"matchdelete", 1, 1, f_matchdelete}, - {"matchend", 2, 4, f_matchend}, - {"matchlist", 2, 4, f_matchlist}, - {"matchstr", 2, 4, f_matchstr}, - {"matchstrpos", 2, 4, f_matchstrpos}, - {"max", 1, 1, f_max}, - {"min", 1, 1, f_min}, -#ifdef vim_mkdir - {"mkdir", 1, 3, f_mkdir}, -#endif - {"mode", 0, 1, f_mode}, -#ifdef FEAT_MZSCHEME - {"mzeval", 1, 1, f_mzeval}, -#endif - {"nextnonblank", 1, 1, f_nextnonblank}, - {"nr2char", 1, 2, f_nr2char}, - {"or", 2, 2, f_or}, - {"pathshorten", 1, 1, f_pathshorten}, -#ifdef FEAT_PERL - {"perleval", 1, 1, f_perleval}, -#endif -#ifdef FEAT_FLOAT - {"pow", 2, 2, f_pow}, -#endif - {"prevnonblank", 1, 1, f_prevnonblank}, - {"printf", 2, 19, f_printf}, - {"pumvisible", 0, 0, f_pumvisible}, -#ifdef FEAT_PYTHON3 - {"py3eval", 1, 1, f_py3eval}, -#endif -#ifdef FEAT_PYTHON - {"pyeval", 1, 1, f_pyeval}, -#endif - {"range", 1, 3, f_range}, - {"readfile", 1, 3, f_readfile}, - {"reltime", 0, 2, f_reltime}, -#ifdef FEAT_FLOAT - {"reltimefloat", 1, 1, f_reltimefloat}, -#endif - {"reltimestr", 1, 1, f_reltimestr}, - {"remote_expr", 2, 3, f_remote_expr}, - {"remote_foreground", 1, 1, f_remote_foreground}, - {"remote_peek", 1, 2, f_remote_peek}, - {"remote_read", 1, 1, f_remote_read}, - {"remote_send", 2, 3, f_remote_send}, - {"remove", 2, 3, f_remove}, - {"rename", 2, 2, f_rename}, - {"repeat", 2, 2, f_repeat}, - {"resolve", 1, 1, f_resolve}, - {"reverse", 1, 1, f_reverse}, -#ifdef FEAT_FLOAT - {"round", 1, 1, f_round}, -#endif - {"screenattr", 2, 2, f_screenattr}, - {"screenchar", 2, 2, f_screenchar}, - {"screencol", 0, 0, f_screencol}, - {"screenrow", 0, 0, f_screenrow}, - {"search", 1, 4, f_search}, - {"searchdecl", 1, 3, f_searchdecl}, - {"searchpair", 3, 7, f_searchpair}, - {"searchpairpos", 3, 7, f_searchpairpos}, - {"searchpos", 1, 4, f_searchpos}, - {"server2client", 2, 2, f_server2client}, - {"serverlist", 0, 0, f_serverlist}, - {"setbufvar", 3, 3, f_setbufvar}, - {"setcharsearch", 1, 1, f_setcharsearch}, - {"setcmdpos", 1, 1, f_setcmdpos}, - {"setfperm", 2, 2, f_setfperm}, - {"setline", 2, 2, f_setline}, - {"setloclist", 2, 3, f_setloclist}, - {"setmatches", 1, 1, f_setmatches}, - {"setpos", 2, 2, f_setpos}, - {"setqflist", 1, 2, f_setqflist}, - {"setreg", 2, 3, f_setreg}, - {"settabvar", 3, 3, f_settabvar}, - {"settabwinvar", 4, 4, f_settabwinvar}, - {"setwinvar", 3, 3, f_setwinvar}, -#ifdef FEAT_CRYPT - {"sha256", 1, 1, f_sha256}, -#endif - {"shellescape", 1, 2, f_shellescape}, - {"shiftwidth", 0, 0, f_shiftwidth}, - {"simplify", 1, 1, f_simplify}, -#ifdef FEAT_FLOAT - {"sin", 1, 1, f_sin}, - {"sinh", 1, 1, f_sinh}, -#endif - {"sort", 1, 3, f_sort}, - {"soundfold", 1, 1, f_soundfold}, - {"spellbadword", 0, 1, f_spellbadword}, - {"spellsuggest", 1, 3, f_spellsuggest}, - {"split", 1, 3, f_split}, -#ifdef FEAT_FLOAT - {"sqrt", 1, 1, f_sqrt}, - {"str2float", 1, 1, f_str2float}, -#endif - {"str2nr", 1, 2, f_str2nr}, - {"strcharpart", 2, 3, f_strcharpart}, - {"strchars", 1, 2, f_strchars}, - {"strdisplaywidth", 1, 2, f_strdisplaywidth}, -#ifdef HAVE_STRFTIME - {"strftime", 1, 2, f_strftime}, -#endif - {"strgetchar", 2, 2, f_strgetchar}, - {"stridx", 2, 3, f_stridx}, - {"string", 1, 1, f_string}, - {"strlen", 1, 1, f_strlen}, - {"strpart", 2, 3, f_strpart}, - {"strridx", 2, 3, f_strridx}, - {"strtrans", 1, 1, f_strtrans}, - {"strwidth", 1, 1, f_strwidth}, - {"submatch", 1, 2, f_submatch}, - {"substitute", 4, 4, f_substitute}, - {"synID", 3, 3, f_synID}, - {"synIDattr", 2, 3, f_synIDattr}, - {"synIDtrans", 1, 1, f_synIDtrans}, - {"synconcealed", 2, 2, f_synconcealed}, - {"synstack", 2, 2, f_synstack}, - {"system", 1, 2, f_system}, - {"systemlist", 1, 2, f_systemlist}, - {"tabpagebuflist", 0, 1, f_tabpagebuflist}, - {"tabpagenr", 0, 1, f_tabpagenr}, - {"tabpagewinnr", 1, 2, f_tabpagewinnr}, - {"tagfiles", 0, 0, f_tagfiles}, - {"taglist", 1, 1, f_taglist}, -#ifdef FEAT_FLOAT - {"tan", 1, 1, f_tan}, - {"tanh", 1, 1, f_tanh}, -#endif - {"tempname", 0, 0, f_tempname}, - {"test_alloc_fail", 3, 3, f_test_alloc_fail}, - {"test_autochdir", 0, 0, f_test_autochdir}, - {"test_disable_char_avail", 1, 1, f_test_disable_char_avail}, - {"test_garbagecollect_now", 0, 0, f_test_garbagecollect_now}, -#ifdef FEAT_JOB_CHANNEL - {"test_null_channel", 0, 0, f_test_null_channel}, -#endif - {"test_null_dict", 0, 0, f_test_null_dict}, -#ifdef FEAT_JOB_CHANNEL - {"test_null_job", 0, 0, f_test_null_job}, -#endif - {"test_null_list", 0, 0, f_test_null_list}, - {"test_null_partial", 0, 0, f_test_null_partial}, - {"test_null_string", 0, 0, f_test_null_string}, - {"test_settime", 1, 1, f_test_settime}, -#ifdef FEAT_TIMERS - {"timer_start", 2, 3, f_timer_start}, - {"timer_stop", 1, 1, f_timer_stop}, -#endif - {"tolower", 1, 1, f_tolower}, - {"toupper", 1, 1, f_toupper}, - {"tr", 3, 3, f_tr}, -#ifdef FEAT_FLOAT - {"trunc", 1, 1, f_trunc}, -#endif - {"type", 1, 1, f_type}, - {"undofile", 1, 1, f_undofile}, - {"undotree", 0, 0, f_undotree}, - {"uniq", 1, 3, f_uniq}, - {"values", 1, 1, f_values}, - {"virtcol", 1, 1, f_virtcol}, - {"visualmode", 0, 1, f_visualmode}, - {"wildmenumode", 0, 0, f_wildmenumode}, - {"win_findbuf", 1, 1, f_win_findbuf}, - {"win_getid", 0, 2, f_win_getid}, - {"win_gotoid", 1, 1, f_win_gotoid}, - {"win_id2tabwin", 1, 1, f_win_id2tabwin}, - {"win_id2win", 1, 1, f_win_id2win}, - {"winbufnr", 1, 1, f_winbufnr}, - {"wincol", 0, 0, f_wincol}, - {"winheight", 1, 1, f_winheight}, - {"winline", 0, 0, f_winline}, - {"winnr", 0, 1, f_winnr}, - {"winrestcmd", 0, 0, f_winrestcmd}, - {"winrestview", 1, 1, f_winrestview}, - {"winsaveview", 0, 0, f_winsaveview}, - {"winwidth", 1, 1, f_winwidth}, - {"wordcount", 0, 0, f_wordcount}, - {"writefile", 2, 3, f_writefile}, - {"xor", 2, 2, f_xor}, -}; - -#if defined(FEAT_CMDL_COMPL) || defined(PROTO) - -/* - * 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) - return name; - } - if (++intidx < (int)(sizeof(functions) / sizeof(struct fst))) - { - STRCPY(IObuff, functions[intidx].f_name); - STRCAT(IObuff, "("); - if (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); -} - -#endif /* FEAT_CMDL_COMPL */ - -#if defined(EBCDIC) || defined(PROTO) -/* - * Compare struct fst by function name. - */ - static int -compare_func_name(const void *s1, const void *s2) -{ - struct fst *p1 = (struct fst *)s1; - struct fst *p2 = (struct fst *)s2; - - return STRCMP(p1->f_name, p2->f_name); -} - -/* - * Sort the function table by function name. - * The sorting of the table above is ASCII dependant. - * On machines using EBCDIC we have to sort it. - */ - static void -sortFunctions(void) -{ - int funcCnt = (int)(sizeof(functions) / sizeof(struct fst)) - 1; - - qsort(functions, (size_t)funcCnt, sizeof(struct fst), compare_func_name); -} -#endif - - -/* - * Find internal function in table above. - * Return index, or -1 if not found - */ - int -find_internal_func( - char_u *name) /* name of the function */ -{ - int first = 0; - int last = (int)(sizeof(functions) / sizeof(struct fst)) - 1; - int cmp; - int x; - - /* - * Find the function name in the table. Binary search. - */ - while (first <= last) - { - x = first + ((unsigned)(last - first) >> 1); - cmp = STRCMP(name, functions[x].f_name); - if (cmp < 0) - last = x - 1; - else if (cmp > 0) - first = x + 1; - else - return x; - } - 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 ERROR_UNKNOWN; - if (argcount < functions[i].f_min_argc) - return ERROR_TOOFEW; - if (argcount > functions[i].f_max_argc) - return ERROR_TOOMANY; - argvars[argcount].v_type = VAR_UNKNOWN; - functions[i].f_func(argvars, rettv); - return ERROR_NONE; -} - -/* - * Return TRUE for a non-zero Number and a non-empty String. - */ - static 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_SPECIAL - && 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)); -} - -/********************************************* - * Implementation of the built-in functions - */ - -#ifdef FEAT_FLOAT -static int get_float_arg(typval_T *argvars, float_T *f); - -/* - * Get the float value of "argvars[0]" into "f". - * Returns FAIL when the argument is not a Number or Float. - */ - static int -get_float_arg(typval_T *argvars, float_T *f) -{ - if (argvars[0].v_type == VAR_FLOAT) - { - *f = argvars[0].vval.v_float; - return OK; - } - if (argvars[0].v_type == VAR_NUMBER) - { - *f = (float_T)argvars[0].vval.v_number; - return OK; - } - EMSG(_("E808: Number or Float required")); - return FAIL; -} - -/* - * "abs(expr)" function - */ - static void -f_abs(typval_T *argvars, typval_T *rettv) -{ - if (argvars[0].v_type == VAR_FLOAT) - { - rettv->v_type = VAR_FLOAT; - rettv->vval.v_float = fabs(argvars[0].vval.v_float); - } - else - { - varnumber_T n; - int error = FALSE; - - n = get_tv_number_chk(&argvars[0], &error); - if (error) - rettv->vval.v_number = -1; - else if (n > 0) - rettv->vval.v_number = n; - else - rettv->vval.v_number = -n; - } -} - -/* - * "acos()" function - */ - static void -f_acos(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = acos(f); - else - rettv->vval.v_float = 0.0; -} -#endif - -/* - * "add(list, item)" function - */ - static void -f_add(typval_T *argvars, typval_T *rettv) -{ - list_T *l; - - rettv->vval.v_number = 1; /* Default: Failed */ - if (argvars[0].v_type == VAR_LIST) - { - if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, - (char_u *)N_("add() argument"), TRUE) - && list_append_tv(l, &argvars[1]) == OK) - copy_tv(&argvars[0], rettv); - } - else - EMSG(_(e_listreq)); -} - -/* - * "and(expr, expr)" function - */ - static void -f_and(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) - & get_tv_number_chk(&argvars[1], NULL); -} - -/* - * "append(lnum, string/list)" function - */ - static void -f_append(typval_T *argvars, typval_T *rettv) -{ - long lnum; - char_u *line; - list_T *l = NULL; - listitem_T *li = NULL; - typval_T *tv; - long added = 0; - - /* When coming here from Insert mode, sync undo, so that this can be - * undone separately from what was previously inserted. */ - if (u_sync_once == 2) - { - u_sync_once = 1; /* notify that u_sync() was called */ - u_sync(TRUE); - } - - lnum = get_tv_lnum(argvars); - if (lnum >= 0 - && lnum <= curbuf->b_ml.ml_line_count - && u_save(lnum, lnum + 1) == OK) - { - if (argvars[1].v_type == VAR_LIST) - { - l = argvars[1].vval.v_list; - if (l == NULL) - return; - li = l->lv_first; - } - for (;;) - { - if (l == NULL) - tv = &argvars[1]; /* append a string */ - else if (li == NULL) - break; /* end of list */ - else - tv = &li->li_tv; /* append item from list */ - line = get_tv_string_chk(tv); - if (line == NULL) /* type error */ - { - rettv->vval.v_number = 1; /* Failed */ - break; - } - ml_append(lnum + added, line, (colnr_T)0, FALSE); - ++added; - if (l == NULL) - break; - li = li->li_next; - } - - appended_lines_mark(lnum, added); - if (curwin->w_cursor.lnum > lnum) - curwin->w_cursor.lnum += added; - } - else - rettv->vval.v_number = 1; /* Failed */ -} - -/* - * "argc()" function - */ - static void -f_argc(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->vval.v_number = ARGCOUNT; -} - -/* - * "argidx()" function - */ - static void -f_argidx(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->vval.v_number = curwin->w_arg_idx; -} - -/* - * "arglistid()" function - */ - static void -f_arglistid(typval_T *argvars, typval_T *rettv) -{ - win_T *wp; - - rettv->vval.v_number = -1; - wp = find_tabwin(&argvars[0], &argvars[1]); - if (wp != NULL) - rettv->vval.v_number = wp->w_alist->id; -} - -/* - * "argv(nr)" function - */ - static void -f_argv(typval_T *argvars, typval_T *rettv) -{ - int idx; - - if (argvars[0].v_type != VAR_UNKNOWN) - { - idx = (int)get_tv_number_chk(&argvars[0], NULL); - if (idx >= 0 && idx < ARGCOUNT) - rettv->vval.v_string = vim_strsave(alist_name(&ARGLIST[idx])); - else - rettv->vval.v_string = NULL; - rettv->v_type = VAR_STRING; - } - else if (rettv_list_alloc(rettv) == OK) - for (idx = 0; idx < ARGCOUNT; ++idx) - list_append_string(rettv->vval.v_list, - alist_name(&ARGLIST[idx]), -1); -} - -typedef enum -{ - ASSERT_EQUAL, - ASSERT_NOTEQUAL, - ASSERT_MATCH, - ASSERT_NOTMATCH, - ASSERT_OTHER -} assert_type_T; - -static void prepare_assert_error(garray_T*gap); -static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_str, typval_T *exp_tv, typval_T *got_tv, assert_type_T is_match); -static void assert_error(garray_T *gap); -static void assert_bool(typval_T *argvars, int isTrue); - -/* - * Prepare "gap" for an assert error and add the sourcing position. - */ - static void -prepare_assert_error(garray_T *gap) -{ - char buf[NUMBUFLEN]; - - ga_init2(gap, 1, 100); - if (sourcing_name != NULL) - { - ga_concat(gap, sourcing_name); - if (sourcing_lnum > 0) - ga_concat(gap, (char_u *)" "); - } - if (sourcing_lnum > 0) - { - sprintf(buf, "line %ld", (long)sourcing_lnum); - ga_concat(gap, (char_u *)buf); - } - if (sourcing_name != NULL || sourcing_lnum > 0) - ga_concat(gap, (char_u *)": "); -} - -/* - * Append "str" to "gap", escaping unprintable characters. - * Changes NL to \n, CR to \r, etc. - */ - static void -ga_concat_esc(garray_T *gap, char_u *str) -{ - char_u *p; - char_u buf[NUMBUFLEN]; - - if (str == NULL) - { - ga_concat(gap, (char_u *)"NULL"); - return; - } - - for (p = str; *p != NUL; ++p) - switch (*p) - { - case BS: ga_concat(gap, (char_u *)"\\b"); break; - case ESC: ga_concat(gap, (char_u *)"\\e"); break; - case FF: ga_concat(gap, (char_u *)"\\f"); break; - case NL: ga_concat(gap, (char_u *)"\\n"); break; - case TAB: ga_concat(gap, (char_u *)"\\t"); break; - case CAR: ga_concat(gap, (char_u *)"\\r"); break; - case '\\': ga_concat(gap, (char_u *)"\\\\"); break; - default: - if (*p < ' ') - { - vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p); - ga_concat(gap, buf); - } - else - ga_append(gap, *p); - break; - } -} - -/* - * Fill "gap" with information about an assert error. - */ - static void -fill_assert_error( - garray_T *gap, - typval_T *opt_msg_tv, - char_u *exp_str, - typval_T *exp_tv, - typval_T *got_tv, - assert_type_T atype) -{ - char_u numbuf[NUMBUFLEN]; - char_u *tofree; - - if (opt_msg_tv->v_type != VAR_UNKNOWN) - { - ga_concat(gap, tv2string(opt_msg_tv, &tofree, numbuf, 0)); - vim_free(tofree); - } - else - { - if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) - ga_concat(gap, (char_u *)"Pattern "); - else - ga_concat(gap, (char_u *)"Expected "); - if (exp_str == NULL) - { - ga_concat_esc(gap, tv2string(exp_tv, &tofree, numbuf, 0)); - vim_free(tofree); - } - else - ga_concat_esc(gap, exp_str); - if (atype == ASSERT_MATCH) - ga_concat(gap, (char_u *)" does not match "); - else if (atype == ASSERT_NOTMATCH) - ga_concat(gap, (char_u *)" does match "); - else if (atype == ASSERT_NOTEQUAL) - ga_concat(gap, (char_u *)" differs from "); - else - ga_concat(gap, (char_u *)" but got "); - ga_concat_esc(gap, tv2string(got_tv, &tofree, numbuf, 0)); - vim_free(tofree); - } -} - -/* - * Add an assert error to v:errors. - */ - static void -assert_error(garray_T *gap) -{ - struct vimvar *vp = &vimvars[VV_ERRORS]; - - if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL) - /* Make sure v:errors is a list. */ - set_vim_var_list(VV_ERRORS, list_alloc()); - list_append_string(vimvars[VV_ERRORS].vv_list, gap->ga_data, gap->ga_len); -} - - static void -assert_equal_common(typval_T *argvars, assert_type_T atype) -{ - garray_T ga; - - if (tv_equal(&argvars[0], &argvars[1], FALSE, FALSE) - != (atype == ASSERT_EQUAL)) - { - prepare_assert_error(&ga); - fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], - atype); - assert_error(&ga); - ga_clear(&ga); - } -} - -/* - * "assert_equal(expected, actual[, msg])" function - */ - static void -f_assert_equal(typval_T *argvars, typval_T *rettv UNUSED) -{ - assert_equal_common(argvars, ASSERT_EQUAL); -} - -/* - * "assert_notequal(expected, actual[, msg])" function - */ - static void -f_assert_notequal(typval_T *argvars, typval_T *rettv UNUSED) -{ - assert_equal_common(argvars, ASSERT_NOTEQUAL); -} - -/* - * "assert_exception(string[, msg])" function - */ - static void -f_assert_exception(typval_T *argvars, typval_T *rettv UNUSED) -{ - garray_T ga; - char *error; - - error = (char *)get_tv_string_chk(&argvars[0]); - if (vimvars[VV_EXCEPTION].vv_str == NULL) - { - prepare_assert_error(&ga); - ga_concat(&ga, (char_u *)"v:exception is not set"); - assert_error(&ga); - ga_clear(&ga); - } - else if (error != NULL - && strstr((char *)vimvars[VV_EXCEPTION].vv_str, error) == NULL) - { - prepare_assert_error(&ga); - fill_assert_error(&ga, &argvars[1], NULL, &argvars[0], - &vimvars[VV_EXCEPTION].vv_tv, ASSERT_OTHER); - assert_error(&ga); - ga_clear(&ga); - } -} - -/* - * "assert_fails(cmd [, error])" function - */ - static void -f_assert_fails(typval_T *argvars, typval_T *rettv UNUSED) -{ - char_u *cmd = get_tv_string_chk(&argvars[0]); - garray_T ga; - - called_emsg = FALSE; - suppress_errthrow = TRUE; - emsg_silent = TRUE; - do_cmdline_cmd(cmd); - if (!called_emsg) - { - prepare_assert_error(&ga); - ga_concat(&ga, (char_u *)"command did not fail: "); - ga_concat(&ga, cmd); - assert_error(&ga); - ga_clear(&ga); - } - else if (argvars[1].v_type != VAR_UNKNOWN) - { - char_u buf[NUMBUFLEN]; - char *error = (char *)get_tv_string_buf_chk(&argvars[1], buf); - - if (error == NULL - || strstr((char *)vimvars[VV_ERRMSG].vv_str, error) == NULL) - { - prepare_assert_error(&ga); - fill_assert_error(&ga, &argvars[2], NULL, &argvars[1], - &vimvars[VV_ERRMSG].vv_tv, ASSERT_OTHER); - assert_error(&ga); - ga_clear(&ga); - } - } - - called_emsg = FALSE; - suppress_errthrow = FALSE; - emsg_silent = FALSE; - emsg_on_display = FALSE; - set_vim_var_string(VV_ERRMSG, NULL, 0); -} - -/* - * Common for assert_true() and assert_false(). - */ - static void -assert_bool(typval_T *argvars, int isTrue) -{ - int error = FALSE; - garray_T ga; - - if (argvars[0].v_type == VAR_SPECIAL - && argvars[0].vval.v_number == (isTrue ? VVAL_TRUE : VVAL_FALSE)) - return; - if (argvars[0].v_type != VAR_NUMBER - || (get_tv_number_chk(&argvars[0], &error) == 0) == isTrue - || error) - { - prepare_assert_error(&ga); - fill_assert_error(&ga, &argvars[1], - (char_u *)(isTrue ? "True" : "False"), - NULL, &argvars[0], ASSERT_OTHER); - assert_error(&ga); - ga_clear(&ga); - } -} - -/* - * "assert_false(actual[, msg])" function - */ - static void -f_assert_false(typval_T *argvars, typval_T *rettv UNUSED) -{ - assert_bool(argvars, FALSE); -} - - static void -assert_match_common(typval_T *argvars, assert_type_T atype) -{ - garray_T ga; - char_u buf1[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - char_u *pat = get_tv_string_buf_chk(&argvars[0], buf1); - char_u *text = get_tv_string_buf_chk(&argvars[1], buf2); - - if (pat == NULL || text == NULL) - EMSG(_(e_invarg)); - else if (pattern_match(pat, text, FALSE) != (atype == ASSERT_MATCH)) - { - prepare_assert_error(&ga); - fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], - atype); - assert_error(&ga); - ga_clear(&ga); - } -} - -/* - * "assert_match(pattern, actual[, msg])" function - */ - static void -f_assert_match(typval_T *argvars, typval_T *rettv UNUSED) -{ - assert_match_common(argvars, ASSERT_MATCH); -} - -/* - * "assert_notmatch(pattern, actual[, msg])" function - */ - static void -f_assert_notmatch(typval_T *argvars, typval_T *rettv UNUSED) -{ - assert_match_common(argvars, ASSERT_NOTMATCH); -} - -/* - * "assert_true(actual[, msg])" function - */ - static void -f_assert_true(typval_T *argvars, typval_T *rettv UNUSED) -{ - assert_bool(argvars, TRUE); -} - -#ifdef FEAT_FLOAT -/* - * "asin()" function - */ - static void -f_asin(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = asin(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "atan()" function - */ - static void -f_atan(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = atan(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "atan2()" function - */ - static void -f_atan2(typval_T *argvars, typval_T *rettv) -{ - float_T fx = 0.0, fy = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) - rettv->vval.v_float = atan2(fx, fy); - else - rettv->vval.v_float = 0.0; -} -#endif - -/* - * "browse(save, title, initdir, default)" function - */ - static void -f_browse(typval_T *argvars UNUSED, typval_T *rettv) -{ -#ifdef FEAT_BROWSE - int save; - char_u *title; - char_u *initdir; - char_u *defname; - char_u buf[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - int error = FALSE; - - save = (int)get_tv_number_chk(&argvars[0], &error); - title = get_tv_string_chk(&argvars[1]); - initdir = get_tv_string_buf_chk(&argvars[2], buf); - defname = get_tv_string_buf_chk(&argvars[3], buf2); - - if (error || title == NULL || initdir == NULL || defname == NULL) - rettv->vval.v_string = NULL; - else - rettv->vval.v_string = - do_browse(save ? BROWSE_SAVE : 0, - title, defname, NULL, initdir, NULL, curbuf); -#else - rettv->vval.v_string = NULL; -#endif - rettv->v_type = VAR_STRING; -} - -/* - * "browsedir(title, initdir)" function - */ - static void -f_browsedir(typval_T *argvars UNUSED, typval_T *rettv) -{ -#ifdef FEAT_BROWSE - char_u *title; - char_u *initdir; - char_u buf[NUMBUFLEN]; - - title = get_tv_string_chk(&argvars[0]); - initdir = get_tv_string_buf_chk(&argvars[1], buf); - - if (title == NULL || initdir == NULL) - rettv->vval.v_string = NULL; - else - rettv->vval.v_string = do_browse(BROWSE_DIR, - title, NULL, NULL, initdir, NULL, curbuf); -#else - rettv->vval.v_string = NULL; -#endif - rettv->v_type = VAR_STRING; -} - -static buf_T *find_buffer(typval_T *avar); - -/* - * Find a buffer by number or exact name. - */ - static buf_T * -find_buffer(typval_T *avar) -{ - buf_T *buf = NULL; - - if (avar->v_type == VAR_NUMBER) - buf = buflist_findnr((int)avar->vval.v_number); - else if (avar->v_type == VAR_STRING && avar->vval.v_string != NULL) - { - buf = buflist_findname_exp(avar->vval.v_string); - if (buf == NULL) - { - /* No full path name match, try a match with a URL or a "nofile" - * buffer, these don't use the full path. */ - for (buf = firstbuf; buf != NULL; buf = buf->b_next) - if (buf->b_fname != NULL - && (path_with_url(buf->b_fname) -#ifdef FEAT_QUICKFIX - || bt_nofile(buf) -#endif - ) - && STRCMP(buf->b_fname, avar->vval.v_string) == 0) - break; - } - } - return buf; -} - -/* - * "bufexists(expr)" function - */ - static void -f_bufexists(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = (find_buffer(&argvars[0]) != NULL); -} - -/* - * "buflisted(expr)" function - */ - static void -f_buflisted(typval_T *argvars, typval_T *rettv) -{ - buf_T *buf; - - buf = find_buffer(&argvars[0]); - rettv->vval.v_number = (buf != NULL && buf->b_p_bl); -} - -/* - * "bufloaded(expr)" function - */ - static void -f_bufloaded(typval_T *argvars, typval_T *rettv) -{ - buf_T *buf; - - buf = find_buffer(&argvars[0]); - rettv->vval.v_number = (buf != NULL && buf->b_ml.ml_mfp != NULL); -} - - buf_T * -buflist_find_by_name(char_u *name, int curtab_only) -{ - int save_magic; - char_u *save_cpo; - buf_T *buf; - - /* Ignore 'magic' and 'cpoptions' here to make scripts portable */ - save_magic = p_magic; - p_magic = TRUE; - save_cpo = p_cpo; - p_cpo = (char_u *)""; - - buf = buflist_findnr(buflist_findpat(name, name + STRLEN(name), - TRUE, FALSE, curtab_only)); - - p_magic = save_magic; - p_cpo = save_cpo; - return buf; -} - -/* - * Get buffer by number or pattern. - */ - static buf_T * -get_buf_tv(typval_T *tv, int curtab_only) -{ - char_u *name = tv->vval.v_string; - buf_T *buf; - - if (tv->v_type == VAR_NUMBER) - return buflist_findnr((int)tv->vval.v_number); - if (tv->v_type != VAR_STRING) - return NULL; - if (name == NULL || *name == NUL) - return curbuf; - if (name[0] == '$' && name[1] == NUL) - return lastbuf; - - buf = buflist_find_by_name(name, curtab_only); - - /* If not found, try expanding the name, like done for bufexists(). */ - if (buf == NULL) - buf = find_buffer(tv); - - return buf; -} - -/* - * "bufname(expr)" function - */ - static void -f_bufname(typval_T *argvars, typval_T *rettv) -{ - buf_T *buf; - - (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ - ++emsg_off; - buf = get_buf_tv(&argvars[0], FALSE); - rettv->v_type = VAR_STRING; - if (buf != NULL && buf->b_fname != NULL) - rettv->vval.v_string = vim_strsave(buf->b_fname); - else - rettv->vval.v_string = NULL; - --emsg_off; -} - -/* - * "bufnr(expr)" function - */ - static void -f_bufnr(typval_T *argvars, typval_T *rettv) -{ - buf_T *buf; - int error = FALSE; - char_u *name; - - (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ - ++emsg_off; - buf = get_buf_tv(&argvars[0], FALSE); - --emsg_off; - - /* If the buffer isn't found and the second argument is not zero create a - * new buffer. */ - if (buf == NULL - && argvars[1].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[1], &error) != 0 - && !error - && (name = get_tv_string_chk(&argvars[0])) != NULL - && !error) - buf = buflist_new(name, NULL, (linenr_T)1, 0); - - if (buf != NULL) - rettv->vval.v_number = buf->b_fnum; - else - rettv->vval.v_number = -1; -} - - static void -buf_win_common(typval_T *argvars, typval_T *rettv, int get_nr) -{ -#ifdef FEAT_WINDOWS - win_T *wp; - int winnr = 0; -#endif - buf_T *buf; - - (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ - ++emsg_off; - buf = get_buf_tv(&argvars[0], TRUE); -#ifdef FEAT_WINDOWS - for (wp = firstwin; wp; wp = wp->w_next) - { - ++winnr; - if (wp->w_buffer == buf) - break; - } - rettv->vval.v_number = (wp != NULL ? (get_nr ? winnr : wp->w_id) : -1); -#else - rettv->vval.v_number = (curwin->w_buffer == buf - ? (get_nr ? 1 : curwin->w_id) : -1); -#endif - --emsg_off; -} - -/* - * "bufwinid(nr)" function - */ - static void -f_bufwinid(typval_T *argvars, typval_T *rettv) -{ - buf_win_common(argvars, rettv, FALSE); -} - -/* - * "bufwinnr(nr)" function - */ - static void -f_bufwinnr(typval_T *argvars, typval_T *rettv) -{ - buf_win_common(argvars, rettv, TRUE); -} - -/* - * "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; - - boff = get_tv_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 -} - - static void -byteidx(typval_T *argvars, typval_T *rettv, int comp UNUSED) -{ -#ifdef FEAT_MBYTE - char_u *t; -#endif - char_u *str; - varnumber_T idx; - - str = get_tv_string_chk(&argvars[0]); - idx = get_tv_number_chk(&argvars[1], NULL); - rettv->vval.v_number = -1; - if (str == NULL || idx < 0) - return; - -#ifdef FEAT_MBYTE - t = str; - for ( ; idx > 0; idx--) - { - if (*t == NUL) /* EOL reached */ - return; - if (enc_utf8 && comp) - t += utf_ptr2len(t); - else - t += (*mb_ptr2len)(t); - } - rettv->vval.v_number = (varnumber_T)(t - str); -#else - if ((size_t)idx <= STRLEN(str)) - rettv->vval.v_number = idx; -#endif -} - -/* - * "byteidx()" function - */ - static void -f_byteidx(typval_T *argvars, typval_T *rettv) -{ - byteidx(argvars, rettv, FALSE); -} - -/* - * "byteidxcomp()" function - */ - static void -f_byteidxcomp(typval_T *argvars, typval_T *rettv) -{ - byteidx(argvars, rettv, TRUE); -} - -/* - * "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; - - if (argvars[1].v_type != VAR_LIST) - { - EMSG(_(e_listreq)); - 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->pt_name; - } - else - func = get_tv_string(&argvars[0]); - if (*func == NUL) - return; /* type error or empty name */ - - if (argvars[2].v_type != VAR_UNKNOWN) - { - if (argvars[2].v_type != VAR_DICT) - { - EMSG(_(e_dictreq)); - return; - } - selfdict = argvars[2].vval.v_dict; - } - - (void)func_call(func, &argvars[1], partial, selfdict, rettv); -} - -#ifdef FEAT_FLOAT -/* - * "ceil({float})" function - */ - static void -f_ceil(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = ceil(f); - else - rettv->vval.v_float = 0.0; -} -#endif - -#ifdef FEAT_JOB_CHANNEL -/* - * "ch_close()" function - */ - static void -f_ch_close(typval_T *argvars, typval_T *rettv UNUSED) -{ - channel_T *channel = get_channel_arg(&argvars[0], TRUE, FALSE, 0); - - if (channel != NULL) - { - channel_close(channel, FALSE); - channel_clear(channel); - } -} - -/* - * "ch_getbufnr()" function - */ - static void -f_ch_getbufnr(typval_T *argvars, typval_T *rettv) -{ - channel_T *channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0); - - rettv->vval.v_number = -1; - if (channel != NULL) - { - char_u *what = get_tv_string(&argvars[1]); - int part; - - if (STRCMP(what, "err") == 0) - part = PART_ERR; - else if (STRCMP(what, "out") == 0) - part = PART_OUT; - else if (STRCMP(what, "in") == 0) - part = PART_IN; - else - part = PART_SOCK; - if (channel->ch_part[part].ch_bufref.br_buf != NULL) - rettv->vval.v_number = - channel->ch_part[part].ch_bufref.br_buf->b_fnum; - } -} - -/* - * "ch_getjob()" function - */ - static void -f_ch_getjob(typval_T *argvars, typval_T *rettv) -{ - channel_T *channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0); - - if (channel != NULL) - { - rettv->v_type = VAR_JOB; - rettv->vval.v_job = channel->ch_job; - if (channel->ch_job != NULL) - ++channel->ch_job->jv_refcount; - } -} - -/* - * "ch_info()" function - */ - static void -f_ch_info(typval_T *argvars, typval_T *rettv UNUSED) -{ - channel_T *channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0); - - if (channel != NULL && rettv_dict_alloc(rettv) != FAIL) - channel_info(channel, rettv->vval.v_dict); -} - -/* - * "ch_log()" function - */ - static void -f_ch_log(typval_T *argvars, typval_T *rettv UNUSED) -{ - char_u *msg = get_tv_string(&argvars[0]); - channel_T *channel = NULL; - - if (argvars[1].v_type != VAR_UNKNOWN) - channel = get_channel_arg(&argvars[1], FALSE, FALSE, 0); - - ch_log(channel, (char *)msg); -} - -/* - * "ch_logfile()" function - */ - static void -f_ch_logfile(typval_T *argvars, typval_T *rettv UNUSED) -{ - char_u *fname; - char_u *opt = (char_u *)""; - char_u buf[NUMBUFLEN]; - - fname = get_tv_string(&argvars[0]); - if (argvars[1].v_type == VAR_STRING) - opt = get_tv_string_buf(&argvars[1], buf); - ch_logfile(fname, opt); -} - -/* - * "ch_open()" function - */ - static void -f_ch_open(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_CHANNEL; - if (check_restricted() || check_secure()) - return; - rettv->vval.v_channel = channel_open_func(argvars); -} - -/* - * "ch_read()" function - */ - static void -f_ch_read(typval_T *argvars, typval_T *rettv) -{ - common_channel_read(argvars, rettv, FALSE); -} - -/* - * "ch_readraw()" function - */ - static void -f_ch_readraw(typval_T *argvars, typval_T *rettv) -{ - common_channel_read(argvars, rettv, TRUE); -} - -/* - * "ch_evalexpr()" function - */ - static void -f_ch_evalexpr(typval_T *argvars, typval_T *rettv) -{ - ch_expr_common(argvars, rettv, TRUE); -} - -/* - * "ch_sendexpr()" function - */ - static void -f_ch_sendexpr(typval_T *argvars, typval_T *rettv) -{ - ch_expr_common(argvars, rettv, FALSE); -} - -/* - * "ch_evalraw()" function - */ - static void -f_ch_evalraw(typval_T *argvars, typval_T *rettv) -{ - ch_raw_common(argvars, rettv, TRUE); -} - -/* - * "ch_sendraw()" function - */ - static void -f_ch_sendraw(typval_T *argvars, typval_T *rettv) -{ - ch_raw_common(argvars, rettv, FALSE); -} - -/* - * "ch_setoptions()" function - */ - static void -f_ch_setoptions(typval_T *argvars, typval_T *rettv UNUSED) -{ - channel_T *channel; - jobopt_T opt; - - channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0); - if (channel == NULL) - return; - clear_job_options(&opt); - if (get_job_options(&argvars[1], &opt, - JO_CB_ALL + JO_TIMEOUT_ALL + JO_MODE_ALL) == OK) - channel_set_options(channel, &opt); - free_job_options(&opt); -} - -/* - * "ch_status()" function - */ - static void -f_ch_status(typval_T *argvars, typval_T *rettv) -{ - channel_T *channel; - - /* return an empty string by default */ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0); - rettv->vval.v_string = vim_strsave((char_u *)channel_status(channel)); -} -#endif - -/* - * "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) -{ -#ifdef FEAT_MBYTE - if (has_mbyte) - { - int utf8 = 0; - - if (argvars[1].v_type != VAR_UNKNOWN) - utf8 = (int)get_tv_number_chk(&argvars[1], NULL); - - if (utf8) - rettv->vval.v_number = (*utf_ptr2char)(get_tv_string(&argvars[0])); - else - rettv->vval.v_number = (*mb_ptr2char)(get_tv_string(&argvars[0])); - } - else -#endif - rettv->vval.v_number = get_tv_string(&argvars[0])[0]; -} - -/* - * "cindent(lnum)" function - */ - static void -f_cindent(typval_T *argvars UNUSED, typval_T *rettv) -{ -#ifdef FEAT_CINDENT - pos_T pos; - linenr_T lnum; - - pos = curwin->w_cursor; - lnum = get_tv_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) - { - curwin->w_cursor.lnum = lnum; - rettv->vval.v_number = get_c_indent(); - curwin->w_cursor = pos; - } - else -#endif - rettv->vval.v_number = -1; -} - -/* - * "clearmatches()" function - */ - static void -f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_SEARCH_EXTRA - clear_matches(curwin); -#endif -} - -/* - * "col(string)" function - */ - static void -f_col(typval_T *argvars, typval_T *rettv) -{ - colnr_T col = 0; - pos_T *fp; - int fnum = curbuf->b_fnum; - - fp = var2fpos(&argvars[0], FALSE, &fnum); - 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; -#ifdef FEAT_VIRTUALEDIT - /* 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)) - { -# ifdef FEAT_MBYTE - int l; - - if (*p != NUL && p[(l = (*mb_ptr2len)(p))] == NUL) - col += l; -# else - if (*p != NUL && p[1] == NUL) - ++col; -# endif - } - } -#endif - } - } - rettv->vval.v_number = col; -} - -#if defined(FEAT_INS_EXPAND) -/* - * "complete()" function - */ - static void -f_complete(typval_T *argvars, typval_T *rettv UNUSED) -{ - int startcol; - - if ((State & INSERT) == 0) - { - EMSG(_("E785: complete() can only be used in Insert mode")); - return; - } - - /* Check for undo allowed here, because if something was already inserted - * the line was already saved for undo and this check isn't done. */ - if (!undo_allowed()) - return; - - if (argvars[1].v_type != VAR_LIST || argvars[1].vval.v_list == NULL) - { - EMSG(_(e_invarg)); - return; - } - - startcol = (int)get_tv_number_chk(&argvars[0], NULL); - if (startcol <= 0) - return; - - set_completion(startcol - 1, argvars[1].vval.v_list); -} - -/* - * "complete_add()" function - */ - static void -f_complete_add(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = ins_compl_add_tv(&argvars[0], 0); -} - -/* - * "complete_check()" function - */ - static void -f_complete_check(typval_T *argvars UNUSED, typval_T *rettv) -{ - int saved = RedrawingDisabled; - - RedrawingDisabled = 0; - ins_compl_check_keys(0); - rettv->vval.v_number = compl_interrupted; - RedrawingDisabled = saved; -} -#endif - -/* - * "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; - - message = get_tv_string_chk(&argvars[0]); - if (message == NULL) - error = TRUE; - if (argvars[1].v_type != VAR_UNKNOWN) - { - buttons = get_tv_string_buf_chk(&argvars[1], buf); - if (buttons == NULL) - error = TRUE; - if (argvars[2].v_type != VAR_UNKNOWN) - { - def = (int)get_tv_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) - { - typestr = get_tv_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, 0); -} - -#ifdef FEAT_FLOAT -/* - * "cos()" function - */ - static void -f_cos(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = cos(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "cosh()" function - */ - static void -f_cosh(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = cosh(f); - else - rettv->vval.v_float = 0.0; -} -#endif - -/* - * "count()" function - */ - static void -f_count(typval_T *argvars, typval_T *rettv) -{ - long n = 0; - int ic = FALSE; - - if (argvars[0].v_type == VAR_LIST) - { - listitem_T *li; - list_T *l; - long idx; - - if ((l = argvars[0].vval.v_list) != NULL) - { - li = l->lv_first; - if (argvars[2].v_type != VAR_UNKNOWN) - { - int error = FALSE; - - ic = (int)get_tv_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) - { - idx = (long)get_tv_number_chk(&argvars[3], &error); - if (!error) - { - li = list_find(l, idx); - if (li == NULL) - EMSGN(_(e_listidx), idx); - } - } - if (error) - li = NULL; - } - - for ( ; li != NULL; li = li->li_next) - if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE)) - ++n; - } - } - else if (argvars[0].v_type == VAR_DICT) - { - int todo; - dict_T *d; - hashitem_T *hi; - - if ((d = argvars[0].vval.v_dict) != NULL) - { - int error = FALSE; - - if (argvars[2].v_type != VAR_UNKNOWN) - { - ic = (int)get_tv_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) - EMSG(_(e_invarg)); - } - - todo = error ? 0 : (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) - { - if (!HASHITEM_EMPTY(hi)) - { - --todo; - if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE)) - ++n; - } - } - } - } - else - EMSG2(_(e_listdictarg), "count()"); - rettv->vval.v_number = n; -} - -/* - * "cscope_connection([{num} , {dbpath} [, {prepend}]])" function - * - * Checks the existence of a cscope connection. - */ - static void -f_cscope_connection(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_CSCOPE - int num = 0; - char_u *dbpath = NULL; - char_u *prepend = NULL; - char_u buf[NUMBUFLEN]; - - if (argvars[0].v_type != VAR_UNKNOWN - && argvars[1].v_type != VAR_UNKNOWN) - { - num = (int)get_tv_number(&argvars[0]); - dbpath = get_tv_string(&argvars[1]); - if (argvars[2].v_type != VAR_UNKNOWN) - prepend = get_tv_string_buf(&argvars[2], buf); - } - - rettv->vval.v_number = cs_connection(num, dbpath, prepend); -#endif -} - -/* - * "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) -{ - long line, col; -#ifdef FEAT_VIRTUALEDIT - long coladd = 0; -#endif - int set_curswant = TRUE; - - rettv->vval.v_number = -1; - if (argvars[1].v_type == VAR_UNKNOWN) - { - pos_T pos; - colnr_T curswant = -1; - - if (list2fpos(argvars, &pos, NULL, &curswant) == FAIL) - { - EMSG(_(e_invarg)); - return; - } - line = pos.lnum; - col = pos.col; -#ifdef FEAT_VIRTUALEDIT - coladd = pos.coladd; -#endif - if (curswant >= 0) - { - curwin->w_curswant = curswant - 1; - set_curswant = FALSE; - } - } - else - { - line = get_tv_lnum(argvars); - col = (long)get_tv_number_chk(&argvars[1], NULL); -#ifdef FEAT_VIRTUALEDIT - if (argvars[2].v_type != VAR_UNKNOWN) - coladd = (long)get_tv_number_chk(&argvars[2], NULL); -#endif - } - if (line < 0 || col < 0 -#ifdef FEAT_VIRTUALEDIT - || coladd < 0 -#endif - ) - return; /* type error; errmsg already given */ - if (line > 0) - curwin->w_cursor.lnum = line; - if (col > 0) - curwin->w_cursor.col = col - 1; -#ifdef FEAT_VIRTUALEDIT - curwin->w_cursor.coladd = coladd; -#endif - - /* Make sure the cursor is in a valid position. */ - check_cursor(); -#ifdef FEAT_MBYTE - /* Correct cursor for multi-byte character. */ - if (has_mbyte) - mb_adjust_cursor(); -#endif - - curwin->w_set_curswant = set_curswant; - rettv->vval.v_number = 0; -} - -/* - * "deepcopy()" function - */ - static void -f_deepcopy(typval_T *argvars, typval_T *rettv) -{ - int noref = 0; - - if (argvars[1].v_type != VAR_UNKNOWN) - noref = (int)get_tv_number_chk(&argvars[1], NULL); - if (noref < 0 || noref > 1) - EMSG(_(e_invarg)); - else - { - current_copyID += COPYID_INC; - item_copy(&argvars[0], rettv, TRUE, noref == 0 ? current_copyID : 0); - } -} - -/* - * "delete()" function - */ - static void -f_delete(typval_T *argvars, typval_T *rettv) -{ - char_u nbuf[NUMBUFLEN]; - char_u *name; - char_u *flags; - - rettv->vval.v_number = -1; - if (check_restricted() || check_secure()) - return; - - name = get_tv_string(&argvars[0]); - if (name == NULL || *name == NUL) - { - EMSG(_(e_invarg)); - return; - } - - if (argvars[1].v_type != VAR_UNKNOWN) - flags = get_tv_string_buf(&argvars[1], nbuf); - else - flags = (char_u *)""; - - if (*flags == NUL) - /* delete a file */ - rettv->vval.v_number = mch_remove(name) == 0 ? 0 : -1; - else if (STRCMP(flags, "d") == 0) - /* delete an empty directory */ - rettv->vval.v_number = mch_rmdir(name) == 0 ? 0 : -1; - else if (STRCMP(flags, "rf") == 0) - /* delete a directory recursively */ - rettv->vval.v_number = delete_recursive(name); - else - EMSG2(_(e_invexpr2), flags); -} - -/* - * "did_filetype()" function - */ - static void -f_did_filetype(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_AUTOCMD - rettv->vval.v_number = did_filetype; -#endif -} - -/* - * "diff_filler()" function - */ - static void -f_diff_filler(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_DIFF - rettv->vval.v_number = diff_check_fill(curwin, get_tv_lnum(argvars)); -#endif -} - -/* - * "diff_hlID()" function - */ - static void -f_diff_hlID(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_DIFF - linenr_T lnum = get_tv_lnum(argvars); - static linenr_T prev_lnum = 0; - static int changedtick = 0; - static int fnum = 0; - static int change_start = 0; - static int change_end = 0; - static hlf_T hlID = (hlf_T)0; - int filler_lines; - int col; - - if (lnum < 0) /* ignore type error in {lnum} arg */ - lnum = 0; - if (lnum != prev_lnum - || changedtick != curbuf->b_changedtick - || fnum != curbuf->b_fnum) - { - /* New line, buffer, change: need to get the values. */ - filler_lines = diff_check(curwin, lnum); - if (filler_lines < 0) - { - if (filler_lines == -1) - { - change_start = MAXCOL; - change_end = -1; - if (diff_find_change(curwin, lnum, &change_start, &change_end)) - hlID = HLF_ADD; /* added line */ - else - hlID = HLF_CHD; /* changed line */ - } - else - hlID = HLF_ADD; /* added line */ - } - else - hlID = (hlf_T)0; - prev_lnum = lnum; - changedtick = curbuf->b_changedtick; - fnum = curbuf->b_fnum; - } - - if (hlID == HLF_CHD || hlID == HLF_TXD) - { - col = get_tv_number(&argvars[1]) - 1; /* ignore type error in {col} */ - if (col >= change_start && col <= change_end) - hlID = HLF_TXD; /* changed text */ - else - hlID = HLF_CHD; /* changed line */ - } - rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)hlID; -#endif -} - -/* - * "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_first == NULL; - break; - case VAR_DICT: - n = argvars[0].vval.v_dict == NULL - || argvars[0].vval.v_dict->dv_hashtab.ht_used == 0; - break; - case VAR_SPECIAL: - n = argvars[0].vval.v_number != VVAL_TRUE; - 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: - EMSG2(_(e_intern2), "f_empty(UNKNOWN)"); - n = TRUE; - break; - } - - rettv->vval.v_number = n; -} - -/* - * "escape({string}, {chars})" function - */ - static void -f_escape(typval_T *argvars, typval_T *rettv) -{ - char_u buf[NUMBUFLEN]; - - rettv->vval.v_string = vim_strsave_escaped(get_tv_string(&argvars[0]), - get_tv_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; - - s = get_tv_string_chk(&argvars[0]); - if (s != NULL) - s = skipwhite(s); - - p = s; - if (s == NULL || eval1(&s, rettv, TRUE) == FAIL) - { - if (p != NULL && !aborting()) - EMSG2(_(e_invexpr2), p); - need_clr_eos = FALSE; - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - } - else if (*s != NUL) - EMSG(_(e_trailing)); -} - -/* - * "eventhandler()" function - */ - static void -f_eventhandler(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->vval.v_number = vgetc_busy; -} - -/* - * "executable()" function - */ - static void -f_executable(typval_T *argvars, typval_T *rettv) -{ - char_u *name = get_tv_string(&argvars[0]); - - /* Check in $PATH and also check directly if there is a directory name. */ - rettv->vval.v_number = mch_can_exe(name, NULL, TRUE) - || (gettail(name) != name && mch_can_exe(name, NULL, FALSE)); -} - -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; - } -} - -/* - * Get next line from a list. - * Called by do_cmdline() to get the next line. - * Returns allocated string, or NULL for end of function. - */ - - static char_u * -get_list_line( - int c UNUSED, - void *cookie, - int indent UNUSED) -{ - listitem_T **p = (listitem_T **)cookie; - listitem_T *item = *p; - char_u buf[NUMBUFLEN]; - char_u *s; - - if (item == NULL) - return NULL; - s = get_tv_string_buf_chk(&item->li_tv, buf); - *p = item->li_next; - return s == NULL ? NULL : vim_strsave(s); -} - -/* - * "execute()" function - */ - static void -f_execute(typval_T *argvars, typval_T *rettv) -{ - 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; - garray_T save_ga; - - rettv->vval.v_string = NULL; - rettv->v_type = VAR_STRING; - - if (argvars[0].v_type == VAR_LIST) - { - list = argvars[0].vval.v_list; - if (list == NULL || list->lv_first == NULL) - /* empty list, no commands, empty output */ - return; - ++list->lv_refcount; - } - else - { - cmd = get_tv_string_chk(&argvars[0]); - if (cmd == NULL) - return; - } - - if (argvars[1].v_type != VAR_UNKNOWN) - { - char_u buf[NUMBUFLEN]; - char_u *s = get_tv_string_buf_chk(&argvars[1], buf); - - if (s == NULL) - return; - 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, (int)sizeof(char), 500); - redir_execute = TRUE; - - if (cmd != NULL) - do_cmdline_cmd(cmd); - else - { - listitem_T *item = list->lv_first; - - do_cmdline(NULL, get_list_line, (void *)&item, - DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); - --list->lv_refcount; - } - - rettv->vval.v_string = redir_execute_ga.ga_data; - 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; - - /* "silent reg" or "silent echo x" leaves msg_col somewhere in the - * line. Put it back in the first column. */ - msg_col = 0; -} - -/* - * "exepath()" function - */ - static void -f_exepath(typval_T *argvars, typval_T *rettv) -{ - char_u *p = NULL; - - (void)mch_can_exe(get_tv_string(&argvars[0]), &p, TRUE); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = p; -} - -/* - * "exists()" function - */ - static void -f_exists(typval_T *argvars, typval_T *rettv) -{ - char_u *p; - char_u *name; - int n = FALSE; - int len = 0; - - p = get_tv_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 = (get_option_tv(&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); - } - else if (*p == ':') - { - n = cmd_exists(p + 1); - } - else if (*p == '#') - { -#ifdef FEAT_AUTOCMD - if (p[1] == '#') - n = autocmd_supported(p + 2); - else - n = au_exists(p + 1); -#endif - } - else /* internal variable */ - { - char_u *tofree; - typval_T tv; - - /* get_name_len() takes care of expanding curly braces */ - name = p; - len = get_name_len(&p, &tofree, TRUE, FALSE); - if (len > 0) - { - if (tofree != NULL) - name = tofree; - n = (get_var_tv(name, len, &tv, NULL, FALSE, TRUE) == OK); - if (n) - { - /* handle d.key, l[idx], f(expr) */ - n = (handle_subscript(&p, &tv, TRUE, FALSE) == OK); - if (n) - clear_tv(&tv); - } - } - if (*p != NUL) - n = FALSE; - - vim_free(tofree); - } - - rettv->vval.v_number = n; -} - -#ifdef FEAT_FLOAT -/* - * "exp()" function - */ - static void -f_exp(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = exp(f); - else - rettv->vval.v_float = 0.0; -} -#endif - -/* - * "expand()" function - */ - static void -f_expand(typval_T *argvars, typval_T *rettv) -{ - char_u *s; - int len; - char_u *errormsg; - int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND; - expand_T xpc; - int error = FALSE; - char_u *result; - - rettv->v_type = VAR_STRING; - if (argvars[1].v_type != VAR_UNKNOWN - && argvars[2].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[2], &error) - && !error) - { - rettv->v_type = VAR_LIST; - rettv->vval.v_list = NULL; - } - - s = get_tv_string(&argvars[0]); - if (*s == '%' || *s == '#' || *s == '<') - { - ++emsg_off; - result = eval_vars(s, s, &len, NULL, &errormsg, NULL); - --emsg_off; - if (rettv->v_type == VAR_LIST) - { - if (rettv_list_alloc(rettv) != FAIL && result != NULL) - list_append_string(rettv->vval.v_list, result, -1); - else - 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 - && get_tv_number_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) != FAIL) - { - 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; - } -} - -/* - * "extend(list, list [, idx])" function - * "extend(dict, dict [, action])" function - */ - static void -f_extend(typval_T *argvars, typval_T *rettv) -{ - char_u *arg_errmsg = (char_u *)N_("extend() argument"); - - if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) - { - list_T *l1, *l2; - listitem_T *item; - long before; - int error = FALSE; - - l1 = argvars[0].vval.v_list; - l2 = argvars[1].vval.v_list; - if (l1 != NULL && !tv_check_lock(l1->lv_lock, arg_errmsg, TRUE) - && l2 != NULL) - { - if (argvars[2].v_type != VAR_UNKNOWN) - { - before = (long)get_tv_number_chk(&argvars[2], &error); - if (error) - return; /* type error; errmsg already given */ - - if (before == l1->lv_len) - item = NULL; - else - { - item = list_find(l1, before); - if (item == NULL) - { - EMSGN(_(e_listidx), before); - return; - } - } - } - else - item = NULL; - list_extend(l1, l2, item); - - copy_tv(&argvars[0], rettv); - } - } - else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) - { - dict_T *d1, *d2; - char_u *action; - int i; - - d1 = argvars[0].vval.v_dict; - d2 = argvars[1].vval.v_dict; - if (d1 != NULL && !tv_check_lock(d1->dv_lock, arg_errmsg, TRUE) - && d2 != NULL) - { - /* Check the third argument. */ - if (argvars[2].v_type != VAR_UNKNOWN) - { - static char *(av[]) = {"keep", "force", "error"}; - - action = get_tv_string_chk(&argvars[2]); - if (action == NULL) - return; /* type error; errmsg already given */ - for (i = 0; i < 3; ++i) - if (STRCMP(action, av[i]) == 0) - break; - if (i == 3) - { - EMSG2(_(e_invarg2), action); - return; - } - } - else - action = (char_u *)"force"; - - dict_extend(d1, d2, action); - - copy_tv(&argvars[0], rettv); - } - } - else - EMSG2(_(e_listdictarg), "extend()"); -} - -/* - * "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 dangerous = 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; - - keys = get_tv_string(&argvars[0]); - - if (argvars[1].v_type != VAR_UNKNOWN) - { - flags = get_tv_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 '!': dangerous = 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) - { - ins_typebuf(keys_esc, (remap ? REMAP_YES : REMAP_NONE), - insert ? 0 : typebuf.tb_len, !typed, FALSE); - vim_free(keys_esc); - if (vgetc_busy) - typebuf_was_filled = TRUE; - if (execute) - { - int save_msg_scroll = msg_scroll; - - /* Avoid a 1 second delay when the keys start Insert mode. */ - msg_scroll = FALSE; - - if (!dangerous) - ++ex_normal_busy; - exec_normal(TRUE); - if (!dangerous) - --ex_normal_busy; - msg_scroll |= save_msg_scroll; - } - } - } -} - -/* - * "filereadable()" function - */ - static void -f_filereadable(typval_T *argvars, typval_T *rettv) -{ - int fd; - char_u *p; - int n; - -#ifndef O_NONBLOCK -# define O_NONBLOCK 0 -#endif - p = get_tv_string(&argvars[0]); - if (*p && !mch_isdir(p) && (fd = mch_open((char *)p, - O_RDONLY | O_NONBLOCK, 0)) >= 0) - { - n = TRUE; - close(fd); - } - else - n = FALSE; - - rettv->vval.v_number = n; -} - -/* - * Return 0 for not writable, 1 for writable file, 2 for a dir which we have - * rights to write into. - */ - static void -f_filewritable(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = filewritable(get_tv_string(&argvars[0])); -} - - static void -findfilendir( - typval_T *argvars UNUSED, - typval_T *rettv, - int find_what UNUSED) -{ -#ifdef FEAT_SEARCHPATH - char_u *fname; - char_u *fresult = NULL; - char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; - char_u *p; - char_u pathbuf[NUMBUFLEN]; - int count = 1; - int first = TRUE; - int error = FALSE; -#endif - - rettv->vval.v_string = NULL; - rettv->v_type = VAR_STRING; - -#ifdef FEAT_SEARCHPATH - fname = get_tv_string(&argvars[0]); - - if (argvars[1].v_type != VAR_UNKNOWN) - { - p = get_tv_string_buf_chk(&argvars[1], pathbuf); - if (p == NULL) - error = TRUE; - else - { - if (*p != NUL) - path = p; - - if (argvars[2].v_type != VAR_UNKNOWN) - count = (int)get_tv_number_chk(&argvars[2], &error); - } - } - - if (count < 0 && rettv_list_alloc(rettv) == FAIL) - error = TRUE; - - if (*fname != NUL && !error) - { - do - { - if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) - vim_free(fresult); - fresult = find_file_in_path_option(first ? fname : NULL, - first ? (int)STRLEN(fname) : 0, - 0, first, path, - find_what, - curbuf->b_ffname, - find_what == FINDFILE_DIR - ? (char_u *)"" : curbuf->b_p_sua); - first = FALSE; - - if (fresult != NULL && rettv->v_type == VAR_LIST) - list_append_string(rettv->vval.v_list, fresult, -1); - - } while ((rettv->v_type == VAR_LIST || --count > 0) && fresult != NULL); - } - - if (rettv->v_type == VAR_STRING) - rettv->vval.v_string = fresult; -#endif -} - -static void filter_map(typval_T *argvars, typval_T *rettv, int map); -static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp); - -/* - * Implementation of map() and filter(). - */ - static void -filter_map(typval_T *argvars, typval_T *rettv, int map) -{ - typval_T *expr; - listitem_T *li, *nli; - list_T *l = NULL; - dictitem_T *di; - hashtab_T *ht; - hashitem_T *hi; - dict_T *d = NULL; - typval_T save_val; - typval_T save_key; - int rem; - int todo; - char_u *ermsg = (char_u *)(map ? "map()" : "filter()"); - char_u *arg_errmsg = (char_u *)(map ? N_("map() argument") - : N_("filter() argument")); - int save_did_emsg; - int idx = 0; - - if (argvars[0].v_type == VAR_LIST) - { - if ((l = argvars[0].vval.v_list) == NULL - || (!map && tv_check_lock(l->lv_lock, arg_errmsg, TRUE))) - return; - } - else if (argvars[0].v_type == VAR_DICT) - { - if ((d = argvars[0].vval.v_dict) == NULL - || (!map && tv_check_lock(d->dv_lock, arg_errmsg, TRUE))) - return; - } - else - { - EMSG2(_(e_listdictarg), ermsg); - return; - } - - expr = &argvars[1]; - /* On type errors, the preceding call has already displayed an error - * message. Avoid a misleading error message for an empty string that - * was not passed as argument. */ - if (expr->v_type != VAR_UNKNOWN) - { - prepare_vimvar(VV_VAL, &save_val); - - /* We reset "did_emsg" to be able to detect whether an error - * occurred during evaluation of the expression. */ - save_did_emsg = did_emsg; - did_emsg = FALSE; - - prepare_vimvar(VV_KEY, &save_key); - if (argvars[0].v_type == VAR_DICT) - { - vimvars[VV_KEY].vv_type = VAR_STRING; - - ht = &d->dv_hashtab; - hash_lock(ht); - todo = (int)ht->ht_used; - for (hi = ht->ht_array; todo > 0; ++hi) - { - if (!HASHITEM_EMPTY(hi)) - { - int r; - - --todo; - di = HI2DI(hi); - if (map && - (tv_check_lock(di->di_tv.v_lock, arg_errmsg, TRUE) - || var_check_ro(di->di_flags, arg_errmsg, TRUE))) - break; - vimvars[VV_KEY].vv_str = vim_strsave(di->di_key); - r = filter_map_one(&di->di_tv, expr, map, &rem); - clear_tv(&vimvars[VV_KEY].vv_tv); - if (r == FAIL || did_emsg) - break; - if (!map && rem) - { - if (var_check_fixed(di->di_flags, arg_errmsg, TRUE) - || var_check_ro(di->di_flags, arg_errmsg, TRUE)) - break; - dictitem_remove(d, di); - } - } - } - hash_unlock(ht); - } - else - { - vimvars[VV_KEY].vv_type = VAR_NUMBER; - - for (li = l->lv_first; li != NULL; li = nli) - { - if (map && tv_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE)) - break; - nli = li->li_next; - vimvars[VV_KEY].vv_nr = idx; - if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL - || did_emsg) - break; - if (!map && rem) - listitem_remove(l, li); - ++idx; - } - } - - restore_vimvar(VV_KEY, &save_key); - restore_vimvar(VV_VAL, &save_val); - - did_emsg |= save_did_emsg; - } - - copy_tv(&argvars[0], rettv); -} - - static int -filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) -{ - typval_T rettv; - typval_T argv[3]; - char_u buf[NUMBUFLEN]; - char_u *s; - int retval = FAIL; - int dummy; - - copy_tv(tv, &vimvars[VV_VAL].vv_tv); - argv[0] = vimvars[VV_KEY].vv_tv; - argv[1] = vimvars[VV_VAL].vv_tv; - if (expr->v_type == VAR_FUNC) - { - s = expr->vval.v_string; - if (call_func(s, (int)STRLEN(s), - &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL, NULL) == FAIL) - goto theend; - } - else if (expr->v_type == VAR_PARTIAL) - { - partial_T *partial = expr->vval.v_partial; - - s = partial->pt_name; - if (call_func(s, (int)STRLEN(s), - &rettv, 2, argv, 0L, 0L, &dummy, TRUE, partial, NULL) - == FAIL) - goto theend; - } - else - { - s = get_tv_string_buf_chk(expr, buf); - if (s == NULL) - goto theend; - s = skipwhite(s); - if (eval1(&s, &rettv, TRUE) == FAIL) - goto theend; - if (*s != NUL) /* check for trailing chars after expr */ - { - EMSG2(_(e_invexpr2), s); - goto theend; - } - } - if (map) - { - /* map(): replace the list item value */ - clear_tv(tv); - rettv.v_lock = 0; - *tv = rettv; - } - else - { - int error = FALSE; - - /* filter(): when expr is zero remove the item */ - *remp = (get_tv_number_chk(&rettv, &error) == 0); - clear_tv(&rettv); - /* On type error, nothing has been removed; return FAIL to stop the - * loop. The error message was given by get_tv_number_chk(). */ - if (error) - goto theend; - } - retval = OK; -theend: - clear_tv(&vimvars[VV_VAL].vv_tv); - return retval; -} - -/* - * "filter()" function - */ - static void -f_filter(typval_T *argvars, typval_T *rettv) -{ - filter_map(argvars, rettv, FALSE); -} - -/* - * "finddir({fname}[, {path}[, {count}]])" function - */ - static void -f_finddir(typval_T *argvars, typval_T *rettv) -{ - findfilendir(argvars, rettv, FINDFILE_DIR); -} - -/* - * "findfile({fname}[, {path}[, {count}]])" function - */ - static void -f_findfile(typval_T *argvars, typval_T *rettv) -{ - findfilendir(argvars, rettv, FINDFILE_FILE); -} - -#ifdef FEAT_FLOAT -/* - * "float2nr({float})" function - */ - static void -f_float2nr(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - if (get_float_arg(argvars, &f) == OK) - { -# ifdef FEAT_NUM64 - if (f < -0x7fffffffffffffff) - rettv->vval.v_number = -0x7fffffffffffffff; - else if (f > 0x7fffffffffffffff) - rettv->vval.v_number = 0x7fffffffffffffff; - else - rettv->vval.v_number = (varnumber_T)f; -# else - if (f < -0x7fffffff) - rettv->vval.v_number = -0x7fffffff; - else if (f > 0x7fffffff) - rettv->vval.v_number = 0x7fffffff; - else - rettv->vval.v_number = (varnumber_T)f; -# endif - } -} - -/* - * "floor({float})" function - */ - static void -f_floor(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = floor(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "fmod()" function - */ - static void -f_fmod(typval_T *argvars, typval_T *rettv) -{ - float_T fx = 0.0, fy = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) - rettv->vval.v_float = fmod(fx, fy); - else - rettv->vval.v_float = 0.0; -} -#endif - -/* - * "fnameescape({string})" function - */ - static void -f_fnameescape(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_string = vim_strsave_fnameescape( - get_tv_string(&argvars[0]), FALSE); - rettv->v_type = VAR_STRING; -} - -/* - * "fnamemodify({fname}, {mods})" function - */ - static void -f_fnamemodify(typval_T *argvars, typval_T *rettv) -{ - char_u *fname; - char_u *mods; - int usedlen = 0; - int len; - char_u *fbuf = NULL; - char_u buf[NUMBUFLEN]; - - fname = get_tv_string_chk(&argvars[0]); - mods = get_tv_string_buf_chk(&argvars[1], buf); - if (fname == NULL || mods == NULL) - fname = NULL; - else - { - len = (int)STRLEN(fname); - (void)modify_fname(mods, &usedlen, &fname, &fbuf, &len); - } - - rettv->v_type = VAR_STRING; - if (fname == NULL) - rettv->vval.v_string = NULL; - else - rettv->vval.v_string = vim_strnsave(fname, len); - vim_free(fbuf); -} - -static void foldclosed_both(typval_T *argvars, typval_T *rettv, int end); - -/* - * "foldclosed()" function - */ - static void -foldclosed_both( - typval_T *argvars UNUSED, - typval_T *rettv, - int end UNUSED) -{ -#ifdef FEAT_FOLDING - linenr_T lnum; - linenr_T first, last; - - lnum = get_tv_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) - { - if (hasFoldingWin(curwin, lnum, &first, &last, FALSE, NULL)) - { - if (end) - rettv->vval.v_number = (varnumber_T)last; - else - rettv->vval.v_number = (varnumber_T)first; - return; - } - } -#endif - rettv->vval.v_number = -1; -} - -/* - * "foldclosed()" function - */ - static void -f_foldclosed(typval_T *argvars, typval_T *rettv) -{ - foldclosed_both(argvars, rettv, FALSE); -} - -/* - * "foldclosedend()" function - */ - static void -f_foldclosedend(typval_T *argvars, typval_T *rettv) -{ - foldclosed_both(argvars, rettv, TRUE); -} - -/* - * "foldlevel()" function - */ - static void -f_foldlevel(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_FOLDING - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) - rettv->vval.v_number = foldLevel(lnum); -#endif -} - -/* - * "foldtext()" function - */ - static void -f_foldtext(typval_T *argvars UNUSED, typval_T *rettv) -{ -#ifdef FEAT_FOLDING - linenr_T lnum; - char_u *s; - char_u *r; - int len; - char *txt; -#endif - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; -#ifdef FEAT_FOLDING - if ((linenr_T)vimvars[VV_FOLDSTART].vv_nr > 0 - && (linenr_T)vimvars[VV_FOLDEND].vv_nr - <= curbuf->b_ml.ml_line_count - && vimvars[VV_FOLDDASHES].vv_str != NULL) - { - /* Find first non-empty line in the fold. */ - lnum = (linenr_T)vimvars[VV_FOLDSTART].vv_nr; - while (lnum < (linenr_T)vimvars[VV_FOLDEND].vv_nr) - { - if (!linewhite(lnum)) - break; - ++lnum; - } - - /* Find interesting text in this line. */ - s = skipwhite(ml_get(lnum)); - /* skip C comment-start */ - if (s[0] == '/' && (s[1] == '*' || s[1] == '/')) - { - s = skipwhite(s + 2); - if (*skipwhite(s) == NUL - && lnum + 1 < (linenr_T)vimvars[VV_FOLDEND].vv_nr) - { - s = skipwhite(ml_get(lnum + 1)); - if (*s == '*') - s = skipwhite(s + 1); - } - } - txt = _("+-%s%3ld lines: "); - r = alloc((unsigned)(STRLEN(txt) - + STRLEN(vimvars[VV_FOLDDASHES].vv_str) /* for %s */ - + 20 /* for %3ld */ - + STRLEN(s))); /* concatenated */ - if (r != NULL) - { - sprintf((char *)r, txt, vimvars[VV_FOLDDASHES].vv_str, - (long)((linenr_T)vimvars[VV_FOLDEND].vv_nr - - (linenr_T)vimvars[VV_FOLDSTART].vv_nr + 1)); - len = (int)STRLEN(r); - STRCAT(r, s); - /* remove 'foldmarker' and 'commentstring' */ - foldtext_cleanup(r + len); - rettv->vval.v_string = r; - } - } -#endif -} - -/* - * "foldtextresult(lnum)" function - */ - static void -f_foldtextresult(typval_T *argvars UNUSED, typval_T *rettv) -{ -#ifdef FEAT_FOLDING - linenr_T lnum; - char_u *text; - char_u buf[51]; - foldinfo_T foldinfo; - int fold_count; -#endif - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; -#ifdef FEAT_FOLDING - lnum = get_tv_lnum(argvars); - /* treat illegal types and illegal string values for {lnum} the same */ - if (lnum < 0) - lnum = 0; - fold_count = foldedCount(curwin, lnum, &foldinfo); - if (fold_count > 0) - { - text = get_foldtext(curwin, lnum, lnum + fold_count - 1, - &foldinfo, buf); - if (text == buf) - text = vim_strsave(text); - rettv->vval.v_string = text; - } -#endif -} - -/* - * "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(); -#else -# ifdef WIN32 - win32_set_foreground(); -# endif -#endif -} - -/* - * "function()" function - */ - static void -f_function(typval_T *argvars, typval_T *rettv) -{ - char_u *s; - char_u *name; - int use_string = FALSE; - partial_T *arg_pt = NULL; - - 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 = arg_pt->pt_name; - } - else - { - /* function('MyFunc', [arg], dict) */ - s = get_tv_string(&argvars[0]); - use_string = TRUE; - } - - if (s == NULL || *s == NUL || (use_string && VIM_ISDIGIT(*s))) - EMSG2(_(e_invarg2), s); - /* Don't check an autoload name for existence here. */ - else if (use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL - && !function_exists(s)) - EMSG2(_("E700: Unknown function: %s"), s); - else - { - int dict_idx = 0; - int arg_idx = 0; - list_T *list = NULL; - - if (STRNCMP(s, "s:", 2) == 0 || STRNCMP(s, "", 5) == 0) - { - char sid_buf[25]; - int off = *s == 's' ? 2 : 5; - - /* Expand s: and into 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. */ - sprintf(sid_buf, "%ld_", (long)current_SID); - name = alloc((int)(STRLEN(sid_buf) + STRLEN(s + off) + 1)); - if (name != NULL) - { - STRCPY(name, sid_buf); - STRCAT(name, s + off); - } - } - 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(_("E922: expected a dict")); - vim_free(name); - return; - } - if (argvars[dict_idx].vval.v_dict == NULL) - dict_idx = 0; - } - if (arg_idx > 0) - { - if (argvars[arg_idx].v_type != VAR_LIST) - { - EMSG(_("E923: Second argument of function() must be a list or a dict")); - vim_free(name); - return; - } - list = argvars[arg_idx].vval.v_list; - if (list == NULL || list->lv_len == 0) - arg_idx = 0; - } - } - if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL) - { - partial_T *pt = (partial_T *)alloc_clear(sizeof(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 = (typval_T *)alloc( - sizeof(typval_T) * pt->pt_argc); - if (pt->pt_argv == NULL) - { - vim_free(pt); - vim_free(name); - return; - } - else - { - for (i = 0; i < arg_len; i++) - copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]); - if (lv_len > 0) - for (li = list->lv_first; li != NULL; - li = li->li_next) - 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; - pt->pt_name = name; - func_ref(pt->pt_name); - } - 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); - } - } -} - -/* - * "garbagecollect()" function - */ - static void -f_garbagecollect(typval_T *argvars, typval_T *rettv UNUSED) -{ - /* 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 && get_tv_number(&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; - - if (argvars[0].v_type == VAR_LIST) - { - if ((l = argvars[0].vval.v_list) != NULL) - { - int error = FALSE; - - li = list_find(l, (long)get_tv_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, get_tv_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 - { - vim_memset(&fref_pt, 0, sizeof(fref_pt)); - fref_pt.pt_name = argvars[0].vval.v_string; - pt = &fref_pt; - } - - if (pt != NULL) - { - char_u *what = get_tv_string(&argvars[1]); - - if (STRCMP(what, "func") == 0 || STRCMP(what, "name") == 0) - { - rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING); - if (pt->pt_name == NULL) - rettv->vval.v_string = NULL; - else - rettv->vval.v_string = vim_strsave(pt->pt_name); - } - else if (STRCMP(what, "dict") == 0) - { - rettv->v_type = VAR_DICT; - rettv->vval.v_dict = pt->pt_dict; - if (pt->pt_dict != NULL) - ++pt->pt_dict->dv_refcount; - } - 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 - EMSG2(_(e_invarg2), what); - return; - } - } - else - EMSG2(_(e_listdictarg), "get()"); - - if (tv == NULL) - { - if (argvars[2].v_type != VAR_UNKNOWN) - copy_tv(&argvars[2], rettv); - } - else - copy_tv(tv, rettv); -} - -static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retlist, typval_T *rettv); - -/* - * Get line or list of lines from buffer "buf" into "rettv". - * Return a range (from start to end) of lines in rettv from the specified - * buffer. - * If 'retlist' is TRUE, then the lines are returned as a Vim List. - */ - static void -get_buffer_lines( - buf_T *buf, - linenr_T start, - linenr_T end, - int retlist, - typval_T *rettv) -{ - char_u *p; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - if (retlist && rettv_list_alloc(rettv) == FAIL) - return; - - if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0) - return; - - if (!retlist) - { - if (start >= 1 && start <= buf->b_ml.ml_line_count) - p = ml_get_buf(buf, start, FALSE); - else - p = (char_u *)""; - rettv->vval.v_string = vim_strsave(p); - } - else - { - if (end < start) - return; - - if (start < 1) - start = 1; - if (end > buf->b_ml.ml_line_count) - end = buf->b_ml.ml_line_count; - while (start <= end) - if (list_append_string(rettv->vval.v_list, - ml_get_buf(buf, start++, FALSE), -1) == FAIL) - break; - } -} - -/* - * "getbufline()" function - */ - static void -f_getbufline(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum; - linenr_T end; - buf_T *buf; - - (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ - ++emsg_off; - buf = get_buf_tv(&argvars[0], FALSE); - --emsg_off; - - lnum = get_tv_lnum_buf(&argvars[1], buf); - if (argvars[2].v_type == VAR_UNKNOWN) - end = lnum; - else - end = get_tv_lnum_buf(&argvars[2], buf); - - get_buffer_lines(buf, lnum, end, TRUE, rettv); -} - -/* - * "getbufvar()" function - */ - static void -f_getbufvar(typval_T *argvars, typval_T *rettv) -{ - buf_T *buf; - buf_T *save_curbuf; - char_u *varname; - dictitem_T *v; - int done = FALSE; - - (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ - varname = get_tv_string_chk(&argvars[1]); - ++emsg_off; - buf = get_buf_tv(&argvars[0], FALSE); - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - if (buf != NULL && varname != NULL) - { - /* set curbuf to be our buf, temporarily */ - save_curbuf = curbuf; - curbuf = buf; - - if (*varname == '&') /* buffer-local-option */ - { - if (get_option_tv(&varname, rettv, TRUE) == OK) - done = TRUE; - } - else if (STRCMP(varname, "changedtick") == 0) - { - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = curbuf->b_changedtick; - done = TRUE; - } - else - { - /* Look up the variable. */ - /* Let getbufvar({nr}, "") return the "b:" dictionary. */ - v = find_var_in_ht(&curbuf->b_vars->dv_hashtab, - 'b', varname, FALSE); - if (v != NULL) - { - copy_tv(&v->di_tv, rettv); - done = TRUE; - } - } - - /* restore previous notion of curbuf */ - curbuf = save_curbuf; - } - - if (!done && argvars[2].v_type != VAR_UNKNOWN) - /* use the default value */ - copy_tv(&argvars[2], rettv); - - --emsg_off; -} - -/* - * "getchar()" function - */ - static void -f_getchar(typval_T *argvars, typval_T *rettv) -{ - varnumber_T n; - int error = FALSE; - - /* Position the cursor. Needed after a message that ends in a space. */ - windgoto(msg_row, msg_col); - - ++no_mapping; - ++allow_keys; - for (;;) - { - if (argvars[0].v_type == VAR_UNKNOWN) - /* getchar(): blocking wait. */ - n = safe_vgetc(); - else if (get_tv_number_chk(&argvars[0], &error) == 1) - /* getchar(1): only check if char avail */ - n = vpeekc_any(); - else if (error || vpeekc_any() == NUL) - /* illegal argument or getchar(0) and no char avail: return zero */ - n = 0; - else - /* getchar(0) and char avail: return char */ - n = safe_vgetc(); - - if (n == K_IGNORE) - continue; - break; - } - --no_mapping; - --allow_keys; - - vimvars[VV_MOUSE_WIN].vv_nr = 0; - vimvars[VV_MOUSE_WINID].vv_nr = 0; - vimvars[VV_MOUSE_LNUM].vv_nr = 0; - vimvars[VV_MOUSE_COL].vv_nr = 0; - - rettv->vval.v_number = n; - if (IS_SPECIAL(n) || mod_mask != 0) - { - char_u temp[10]; /* modifier: 3, mbyte-char: 6, NUL: 1 */ - int i = 0; - - /* Turn a special key into three bytes, plus modifier. */ - if (mod_mask != 0) - { - temp[i++] = K_SPECIAL; - temp[i++] = KS_MODIFIER; - temp[i++] = mod_mask; - } - if (IS_SPECIAL(n)) - { - temp[i++] = K_SPECIAL; - temp[i++] = K_SECOND(n); - temp[i++] = K_THIRD(n); - } -#ifdef FEAT_MBYTE - else if (has_mbyte) - i += (*mb_char2bytes)(n, temp + i); -#endif - else - temp[i++] = n; - temp[i++] = NUL; - rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(temp); - -#ifdef FEAT_MOUSE - if (is_mouse_key(n)) - { - int row = mouse_row; - int col = mouse_col; - win_T *win; - linenr_T lnum; -# ifdef FEAT_WINDOWS - win_T *wp; -# endif - int winnr = 1; - - if (row >= 0 && col >= 0) - { - /* Find the window at the mouse coordinates and compute the - * text position. */ - win = mouse_find_win(&row, &col); - (void)mouse_comp_pos(win, &row, &col, &lnum); -# ifdef FEAT_WINDOWS - for (wp = firstwin; wp != win; wp = wp->w_next) - ++winnr; -# endif - vimvars[VV_MOUSE_WIN].vv_nr = winnr; - vimvars[VV_MOUSE_WINID].vv_nr = win->w_id; - vimvars[VV_MOUSE_LNUM].vv_nr = lnum; - vimvars[VV_MOUSE_COL].vv_nr = col + 1; - } - } -#endif - } -} - -/* - * "getcharmod()" function - */ - static void -f_getcharmod(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->vval.v_number = mod_mask; -} - -/* - * "getcharsearch()" function - */ - static void -f_getcharsearch(typval_T *argvars UNUSED, typval_T *rettv) -{ - if (rettv_dict_alloc(rettv) != FAIL) - { - dict_T *dict = rettv->vval.v_dict; - - dict_add_nr_str(dict, "char", 0L, last_csearch()); - dict_add_nr_str(dict, "forward", last_csearch_forward(), NULL); - dict_add_nr_str(dict, "until", last_csearch_until(), NULL); - } -} - -/* - * "getcmdline()" function - */ - static void -f_getcmdline(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = get_cmdline_str(); -} - -/* - * "getcmdpos()" function - */ - static void -f_getcmdpos(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->vval.v_number = get_cmdline_pos() + 1; -} - -/* - * "getcmdtype()" function - */ - static void -f_getcmdtype(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = alloc(2); - if (rettv->vval.v_string != NULL) - { - rettv->vval.v_string[0] = get_cmdline_type(); - rettv->vval.v_string[1] = NUL; - } -} - -/* - * "getcmdwintype()" function - */ - static void -f_getcmdwintype(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; -#ifdef FEAT_CMDWIN - rettv->vval.v_string = alloc(2); - if (rettv->vval.v_string != NULL) - { - rettv->vval.v_string[0] = cmdwin_type; - rettv->vval.v_string[1] = NUL; - } -#endif -} - -#if defined(FEAT_CMDL_COMPL) -/* - * "getcompletion()" function - */ - static void -f_getcompletion(typval_T *argvars, typval_T *rettv) -{ - char_u *pat; - expand_T xpc; - int options = WILD_KEEP_ALL | WILD_SILENT | WILD_USE_NL - | WILD_LIST_NOTFOUND | WILD_NO_BEEP; - - if (p_wic) - options |= WILD_ICASE; - - ExpandInit(&xpc); - xpc.xp_pattern = get_tv_string(&argvars[0]); - xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); - xpc.xp_context = cmdcomplete_str_to_type(get_tv_string(&argvars[1])); - if (xpc.xp_context == EXPAND_NOTHING) - { - if (argvars[1].v_type == VAR_STRING) - EMSG2(_(e_invarg2), argvars[1].vval.v_string); - else - EMSG(_(e_invarg)); - return; - } - -# if defined(FEAT_MENU) - if (xpc.xp_context == EXPAND_MENUS) - { - set_context_in_menu_cmd(&xpc, (char_u *)"menu", xpc.xp_pattern, FALSE); - xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); - } -# endif - - pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context); - if ((rettv_list_alloc(rettv) != FAIL) && (pat != NULL)) - { - int i; - - ExpandOne(&xpc, pat, 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); - } - vim_free(pat); - ExpandCleanup(&xpc); -} -#endif - -/* - * "getcwd()" function - */ - static void -f_getcwd(typval_T *argvars, typval_T *rettv) -{ - win_T *wp = NULL; - char_u *cwd; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - wp = find_tabwin(&argvars[0], &argvars[1]); - if (wp != NULL) - { - if (wp->w_localdir != NULL) - rettv->vval.v_string = vim_strsave(wp->w_localdir); - else if (globaldir != NULL) - rettv->vval.v_string = vim_strsave(globaldir); - else - { - cwd = alloc(MAXPATHL); - if (cwd != NULL) - { - if (mch_dirname(cwd, MAXPATHL) != FAIL) - rettv->vval.v_string = vim_strsave(cwd); - vim_free(cwd); - } - } -#ifdef BACKSLASH_IN_FILENAME - if (rettv->vval.v_string != NULL) - slash_adjust(rettv->vval.v_string); -#endif - } -} - -/* - * "getfontname()" function - */ - static void -f_getfontname(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; -#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 = get_tv_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 -} - -/* - * "getfperm({fname})" function - */ - static void -f_getfperm(typval_T *argvars, typval_T *rettv) -{ - char_u *fname; - stat_T st; - char_u *perm = NULL; - char_u flags[] = "rwx"; - int i; - - fname = get_tv_string(&argvars[0]); - - rettv->v_type = VAR_STRING; - if (mch_stat((char *)fname, &st) >= 0) - { - perm = vim_strsave((char_u *)"---------"); - if (perm != NULL) - { - for (i = 0; i < 9; i++) - { - if (st.st_mode & (1 << (8 - i))) - perm[i] = flags[i % 3]; - } - } - } - rettv->vval.v_string = perm; -} - -/* - * "getfsize({fname})" function - */ - static void -f_getfsize(typval_T *argvars, typval_T *rettv) -{ - char_u *fname; - stat_T st; - - fname = get_tv_string(&argvars[0]); - - rettv->v_type = VAR_NUMBER; - - if (mch_stat((char *)fname, &st) >= 0) - { - if (mch_isdir(fname)) - rettv->vval.v_number = 0; - else - { - rettv->vval.v_number = (varnumber_T)st.st_size; - - /* non-perfect check for overflow */ - if ((off_T)rettv->vval.v_number != (off_T)st.st_size) - rettv->vval.v_number = -2; - } - } - else - rettv->vval.v_number = -1; -} - -/* - * "getftime({fname})" function - */ - static void -f_getftime(typval_T *argvars, typval_T *rettv) -{ - char_u *fname; - stat_T st; - - fname = get_tv_string(&argvars[0]); - - if (mch_stat((char *)fname, &st) >= 0) - rettv->vval.v_number = (varnumber_T)st.st_mtime; - else - rettv->vval.v_number = -1; -} - -/* - * "getftype({fname})" function - */ - static void -f_getftype(typval_T *argvars, typval_T *rettv) -{ - char_u *fname; - stat_T st; - char_u *type = NULL; - char *t; - - fname = get_tv_string(&argvars[0]); - - rettv->v_type = VAR_STRING; - if (mch_lstat((char *)fname, &st) >= 0) - { -#ifdef S_ISREG - if (S_ISREG(st.st_mode)) - t = "file"; - else if (S_ISDIR(st.st_mode)) - t = "dir"; -# ifdef S_ISLNK - else if (S_ISLNK(st.st_mode)) - t = "link"; -# endif -# ifdef S_ISBLK - else if (S_ISBLK(st.st_mode)) - t = "bdev"; -# endif -# ifdef S_ISCHR - else if (S_ISCHR(st.st_mode)) - t = "cdev"; -# endif -# ifdef S_ISFIFO - else if (S_ISFIFO(st.st_mode)) - t = "fifo"; -# endif -# ifdef S_ISSOCK - else if (S_ISSOCK(st.st_mode)) - t = "fifo"; -# endif - else - t = "other"; -#else -# ifdef S_IFMT - switch (st.st_mode & S_IFMT) - { - case S_IFREG: t = "file"; break; - case S_IFDIR: t = "dir"; break; -# ifdef S_IFLNK - case S_IFLNK: t = "link"; break; -# endif -# ifdef S_IFBLK - case S_IFBLK: t = "bdev"; break; -# endif -# ifdef S_IFCHR - case S_IFCHR: t = "cdev"; break; -# endif -# ifdef S_IFIFO - case S_IFIFO: t = "fifo"; break; -# endif -# ifdef S_IFSOCK - case S_IFSOCK: t = "socket"; break; -# endif - default: t = "other"; - } -# else - if (mch_isdir(fname)) - t = "dir"; - else - t = "file"; -# endif -#endif - type = vim_strsave((char_u *)t); - } - rettv->vval.v_string = type; -} - -/* - * "getline(lnum, [end])" function - */ - static void -f_getline(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum; - linenr_T end; - int retlist; - - lnum = get_tv_lnum(argvars); - if (argvars[1].v_type == VAR_UNKNOWN) - { - end = 0; - retlist = FALSE; - } - else - { - end = get_tv_lnum(&argvars[1]); - retlist = TRUE; - } - - get_buffer_lines(curbuf, lnum, end, retlist, rettv); -} - -/* - * "getmatches()" function - */ - static void -f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_SEARCH_EXTRA - dict_T *dict; - matchitem_T *cur = curwin->w_match_head; - int i; - - if (rettv_list_alloc(rettv) == OK) - { - while (cur != NULL) - { - dict = dict_alloc(); - if (dict == NULL) - return; - if (cur->match.regprog == NULL) - { - /* match added with matchaddpos() */ - for (i = 0; i < MAXPOSMATCH; ++i) - { - llpos_T *llpos; - char buf[6]; - list_T *l; - - llpos = &cur->pos.pos[i]; - if (llpos->lnum == 0) - break; - l = list_alloc(); - if (l == NULL) - break; - list_append_number(l, (varnumber_T)llpos->lnum); - if (llpos->col > 0) - { - list_append_number(l, (varnumber_T)llpos->col); - list_append_number(l, (varnumber_T)llpos->len); - } - sprintf(buf, "pos%d", i + 1); - dict_add_list(dict, buf, l); - } - } - else - { - dict_add_nr_str(dict, "pattern", 0L, cur->pattern); - } - dict_add_nr_str(dict, "group", 0L, syn_id2name(cur->hlg_id)); - dict_add_nr_str(dict, "priority", (long)cur->priority, NULL); - dict_add_nr_str(dict, "id", (long)cur->id, NULL); -# if defined(FEAT_CONCEAL) && defined(FEAT_MBYTE) - if (cur->conceal_char) - { - char_u buf[MB_MAXBYTES + 1]; - - buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL; - dict_add_nr_str(dict, "conceal", 0L, (char_u *)&buf); - } -# endif - list_append_dict(rettv->vval.v_list, dict); - cur = cur->next; - } - } -#endif -} - -/* - * "getpid()" function - */ - static void -f_getpid(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->vval.v_number = mch_get_pid(); -} - - static void -getpos_both( - typval_T *argvars, - typval_T *rettv, - int getcurpos) -{ - pos_T *fp; - list_T *l; - int fnum = -1; - - if (rettv_list_alloc(rettv) == OK) - { - l = rettv->vval.v_list; - if (getcurpos) - fp = &curwin->w_cursor; - else - fp = var2fpos(&argvars[0], TRUE, &fnum); - 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, -#ifdef FEAT_VIRTUALEDIT - (fp != NULL) ? (varnumber_T)fp->coladd : -#endif - (varnumber_T)0); - if (getcurpos) - { - update_curswant(); - list_append_number(l, curwin->w_curswant == MAXCOL ? - (varnumber_T)MAXCOL : (varnumber_T)curwin->w_curswant + 1); - } - } - else - rettv->vval.v_number = FALSE; -} - - -/* - * "getcurpos()" function - */ - static void -f_getcurpos(typval_T *argvars, typval_T *rettv) -{ - getpos_both(argvars, rettv, TRUE); -} - -/* - * "getpos(string)" function - */ - static void -f_getpos(typval_T *argvars, typval_T *rettv) -{ - getpos_both(argvars, rettv, FALSE); -} - -/* - * "getqflist()" and "getloclist()" functions - */ - static void -f_getqflist(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_QUICKFIX - win_T *wp; -#endif - -#ifdef FEAT_QUICKFIX - if (rettv_list_alloc(rettv) == OK) - { - wp = NULL; - if (argvars[0].v_type != VAR_UNKNOWN) /* getloclist() */ - { - wp = find_win_by_nr(&argvars[0], NULL); - if (wp == NULL) - return; - } - - (void)get_errorlist(wp, rettv->vval.v_list); - } -#endif -} - -/* - * "getreg()" function - */ - static void -f_getreg(typval_T *argvars, typval_T *rettv) -{ - char_u *strregname; - int regname; - int arg2 = FALSE; - int return_list = FALSE; - int error = FALSE; - - if (argvars[0].v_type != VAR_UNKNOWN) - { - strregname = get_tv_string_chk(&argvars[0]); - error = strregname == NULL; - if (argvars[1].v_type != VAR_UNKNOWN) - { - arg2 = (int)get_tv_number_chk(&argvars[1], &error); - if (!error && argvars[2].v_type != VAR_UNKNOWN) - return_list = (int)get_tv_number_chk(&argvars[2], &error); - } - } - else - strregname = vimvars[VV_REG].vv_str; - - if (error) - return; - - regname = (strregname == NULL ? '"' : *strregname); - if (regname == 0) - regname = '"'; - - 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) -{ - char_u *strregname; - int regname; - char_u buf[NUMBUFLEN + 2]; - long reglen = 0; - - if (argvars[0].v_type != VAR_UNKNOWN) - { - strregname = get_tv_string_chk(&argvars[0]); - if (strregname == NULL) /* type error; errmsg already given */ - { - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - return; - } - } - else - /* Default to v:register */ - strregname = vimvars[VV_REG].vv_str; - - regname = (strregname == NULL ? '"' : *strregname); - if (regname == 0) - regname = '"'; - - buf[0] = NUL; - buf[1] = NUL; - switch (get_reg_type(regname, ®len)) - { - case MLINE: buf[0] = 'V'; break; - case MCHAR: buf[0] = 'v'; break; - case MBLOCK: - buf[0] = Ctrl_V; - sprintf((char *)buf + 1, "%ld", reglen + 1); - break; - } - rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(buf); -} - -/* - * "gettabvar()" function - */ - static void -f_gettabvar(typval_T *argvars, typval_T *rettv) -{ - win_T *oldcurwin; - tabpage_T *tp, *oldtabpage; - dictitem_T *v; - char_u *varname; - int done = FALSE; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - varname = get_tv_string_chk(&argvars[1]); - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - if (tp != NULL && varname != NULL) - { - /* Set tp to be our tabpage, temporarily. Also set the window to the - * first window in the tabpage, otherwise the window is not valid. */ - if (switch_win(&oldcurwin, &oldtabpage, - tp->tp_firstwin == NULL ? firstwin : tp->tp_firstwin, tp, TRUE) - == OK) - { - /* look up the variable */ - /* Let gettabvar({nr}, "") return the "t:" dictionary. */ - v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', varname, FALSE); - if (v != NULL) - { - copy_tv(&v->di_tv, rettv); - done = TRUE; - } - } - - /* restore previous notion of curwin */ - restore_win(oldcurwin, oldtabpage, TRUE); - } - - if (!done && argvars[2].v_type != VAR_UNKNOWN) - copy_tv(&argvars[2], rettv); -} - -/* - * "gettabwinvar()" function - */ - static void -f_gettabwinvar(typval_T *argvars, typval_T *rettv) -{ - getwinvar(argvars, rettv, 1); -} - -/* - * "getwinposx()" function - */ - static void -f_getwinposx(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->vval.v_number = -1; -#ifdef FEAT_GUI - if (gui.in_use) - { - int x, y; - - if (gui_mch_get_winpos(&x, &y) == OK) - rettv->vval.v_number = x; - } -#endif -} - -/* - * "win_findbuf()" function - */ - static void -f_win_findbuf(typval_T *argvars, typval_T *rettv) -{ - if (rettv_list_alloc(rettv) != FAIL) - win_findbuf(argvars, rettv->vval.v_list); -} - -/* - * "win_getid()" function - */ - static void -f_win_getid(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = win_getid(argvars); -} - -/* - * "win_gotoid()" function - */ - static void -f_win_gotoid(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = win_gotoid(argvars); -} - -/* - * "win_id2tabwin()" function - */ - static void -f_win_id2tabwin(typval_T *argvars, typval_T *rettv) -{ - if (rettv_list_alloc(rettv) != FAIL) - win_id2tabwin(argvars, rettv->vval.v_list); -} - -/* - * "win_id2win()" function - */ - static void -f_win_id2win(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = win_id2win(argvars); -} - -/* - * "getwinposy()" function - */ - static void -f_getwinposy(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->vval.v_number = -1; -#ifdef FEAT_GUI - if (gui.in_use) - { - int x, y; - - if (gui_mch_get_winpos(&x, &y) == OK) - rettv->vval.v_number = y; - } -#endif -} - -/* - * Find window specified by "vp" in tabpage "tp". - */ - static win_T * -find_win_by_nr( - typval_T *vp, - tabpage_T *tp UNUSED) /* NULL for current tab page */ -{ -#ifdef FEAT_WINDOWS - win_T *wp; -#endif - int nr; - - nr = (int)get_tv_number_chk(vp, NULL); - -#ifdef FEAT_WINDOWS - if (nr < 0) - return NULL; - if (nr == 0) - return curwin; - - for (wp = (tp == NULL || tp == curtab) ? firstwin : tp->tp_firstwin; - wp != NULL; wp = wp->w_next) - if (nr >= LOWEST_WIN_ID) - { - if (wp->w_id == nr) - return wp; - } - else if (--nr <= 0) - break; - if (nr >= LOWEST_WIN_ID) - return NULL; - return wp; -#else - if (nr == 0 || nr == 1 || nr == curwin->w_id) - return curwin; - return NULL; -#endif -} - -/* - * Find window specified by "wvp" in tabpage "tvp". - */ - static win_T * -find_tabwin( - typval_T *wvp, /* VAR_UNKNOWN for current window */ - typval_T *tvp) /* VAR_UNKNOWN for current tab page */ -{ - win_T *wp = NULL; - tabpage_T *tp = NULL; - long n; - - if (wvp->v_type != VAR_UNKNOWN) - { - if (tvp->v_type != VAR_UNKNOWN) - { - n = (long)get_tv_number(tvp); - if (n >= 0) - tp = find_tabpage(n); - } - else - tp = curtab; - - if (tp != NULL) - wp = find_win_by_nr(wvp, tp); - } - else - wp = curwin; - - return wp; -} - -/* - * "getwinvar()" function - */ - static void -f_getwinvar(typval_T *argvars, typval_T *rettv) -{ - getwinvar(argvars, rettv, 0); -} - -/* - * getwinvar() and gettabwinvar() - */ - static void -getwinvar( - typval_T *argvars, - typval_T *rettv, - int off) /* 1 for gettabwinvar() */ -{ - win_T *win; - char_u *varname; - dictitem_T *v; - tabpage_T *tp = NULL; - int done = FALSE; -#ifdef FEAT_WINDOWS - win_T *oldcurwin; - tabpage_T *oldtabpage; - int need_switch_win; -#endif - -#ifdef FEAT_WINDOWS - if (off == 1) - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - else - tp = curtab; -#endif - win = find_win_by_nr(&argvars[off], tp); - varname = get_tv_string_chk(&argvars[off + 1]); - ++emsg_off; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - if (win != NULL && varname != NULL) - { -#ifdef FEAT_WINDOWS - /* Set curwin to be our win, temporarily. Also set the tabpage, - * otherwise the window is not valid. Only do this when needed, - * autocommands get blocked. */ - need_switch_win = !(tp == curtab && win == curwin); - if (!need_switch_win - || switch_win(&oldcurwin, &oldtabpage, win, tp, TRUE) == OK) -#endif - { - if (*varname == '&') /* window-local-option */ - { - if (get_option_tv(&varname, rettv, 1) == OK) - done = TRUE; - } - else - { - /* Look up the variable. */ - /* Let getwinvar({nr}, "") return the "w:" dictionary. */ - v = find_var_in_ht(&win->w_vars->dv_hashtab, 'w', - varname, FALSE); - if (v != NULL) - { - copy_tv(&v->di_tv, rettv); - done = TRUE; - } - } - } - -#ifdef FEAT_WINDOWS - if (need_switch_win) - /* restore previous notion of curwin */ - restore_win(oldcurwin, oldtabpage, TRUE); -#endif - } - - if (!done && argvars[off + 2].v_type != VAR_UNKNOWN) - /* use the default return value */ - copy_tv(&argvars[off + 2], rettv); - - --emsg_off; -} - -/* - * "glob()" function - */ - static void -f_glob(typval_T *argvars, typval_T *rettv) -{ - int options = WILD_SILENT|WILD_USE_NL; - expand_T xpc; - int error = FALSE; - - /* When the optional second argument is non-zero, don't remove matches - * for 'wildignore' and don't put matches for 'suffixes' at the end. */ - rettv->v_type = VAR_STRING; - if (argvars[1].v_type != VAR_UNKNOWN) - { - if (get_tv_number_chk(&argvars[1], &error)) - options |= WILD_KEEP_ALL; - if (argvars[2].v_type != VAR_UNKNOWN) - { - if (get_tv_number_chk(&argvars[2], &error)) - { - rettv->v_type = VAR_LIST; - rettv->vval.v_list = NULL; - } - if (argvars[3].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[3], &error)) - options |= WILD_ALLLINKS; - } - } - 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, get_tv_string(&argvars[0]), - NULL, options, WILD_ALL); - else if (rettv_list_alloc(rettv) != FAIL) - { - int i; - - ExpandOne(&xpc, get_tv_string(&argvars[0]), - 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; -} - -/* - * "globpath()" function - */ - static void -f_globpath(typval_T *argvars, typval_T *rettv) -{ - int flags = 0; - char_u buf1[NUMBUFLEN]; - char_u *file = get_tv_string_buf_chk(&argvars[1], buf1); - int error = FALSE; - garray_T ga; - int i; - - /* When the optional second argument is non-zero, don't remove matches - * for 'wildignore' and don't put matches for 'suffixes' at the end. */ - rettv->v_type = VAR_STRING; - if (argvars[2].v_type != VAR_UNKNOWN) - { - if (get_tv_number_chk(&argvars[2], &error)) - flags |= WILD_KEEP_ALL; - if (argvars[3].v_type != VAR_UNKNOWN) - { - if (get_tv_number_chk(&argvars[3], &error)) - { - rettv->v_type = VAR_LIST; - rettv->vval.v_list = NULL; - } - if (argvars[4].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[4], &error)) - flags |= WILD_ALLLINKS; - } - } - if (file != NULL && !error) - { - ga_init2(&ga, (int)sizeof(char_u *), 10); - globpath(get_tv_string(&argvars[0]), file, &ga, flags); - if (rettv->v_type == VAR_STRING) - rettv->vval.v_string = ga_concat_strings(&ga, "\n"); - else if (rettv_list_alloc(rettv) != FAIL) - for (i = 0; i < ga.ga_len; ++i) - list_append_string(rettv->vval.v_list, - ((char_u **)(ga.ga_data))[i], -1); - ga_clear_strings(&ga); - } - else - rettv->vval.v_string = NULL; -} - -/* - * "glob2regpat()" function - */ - static void -f_glob2regpat(typval_T *argvars, typval_T *rettv) -{ - char_u *pat = get_tv_string_chk(&argvars[0]); - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = (pat == NULL) - ? NULL : file_pat_to_reg_pat(pat, NULL, NULL, FALSE); -} - -/* - * "has()" function - */ - static void -f_has(typval_T *argvars, typval_T *rettv) -{ - int i; - char_u *name; - int n = FALSE; - static char *(has_list[]) = - { -#ifdef AMIGA - "amiga", -# ifdef FEAT_ARP - "arp", -# endif -#endif -#ifdef __BEOS__ - "beos", -#endif -#ifdef MACOS - "mac", -#endif -#if defined(MACOS_X_UNIX) - "macunix", /* built with 'darwin' enabled */ -#endif -#if defined(__APPLE__) && __APPLE__ == 1 - "osx", /* built with or without 'darwin' enabled */ -#endif -#ifdef __QNX__ - "qnx", -#endif -#ifdef UNIX - "unix", -#endif -#ifdef VMS - "vms", -#endif -#ifdef WIN32 - "win32", -#endif -#if defined(UNIX) && (defined(__CYGWIN32__) || defined(__CYGWIN__)) - "win32unix", -#endif -#if defined(WIN64) || defined(_WIN64) - "win64", -#endif -#ifdef EBCDIC - "ebcdic", -#endif -#ifndef CASE_INSENSITIVE_FILENAME - "fname_case", -#endif -#ifdef HAVE_ACL - "acl", -#endif -#ifdef FEAT_ARABIC - "arabic", -#endif -#ifdef FEAT_AUTOCMD - "autocmd", -#endif -#ifdef FEAT_BEVAL - "balloon_eval", -# ifndef FEAT_GUI_W32 /* other GUIs always have multiline balloons */ - "balloon_multiline", -# endif -#endif -#if defined(SOME_BUILTIN_TCAPS) || defined(ALL_BUILTIN_TCAPS) - "builtin_terms", -# ifdef ALL_BUILTIN_TCAPS - "all_builtin_terms", -# endif -#endif -#if defined(FEAT_BROWSE) && (defined(USE_FILE_CHOOSER) \ - || defined(FEAT_GUI_W32) \ - || defined(FEAT_GUI_MOTIF)) - "browsefilter", -#endif -#ifdef FEAT_BYTEOFF - "byte_offset", -#endif -#ifdef FEAT_JOB_CHANNEL - "channel", -#endif -#ifdef FEAT_CINDENT - "cindent", -#endif -#ifdef FEAT_CLIENTSERVER - "clientserver", -#endif -#ifdef FEAT_CLIPBOARD - "clipboard", -#endif -#ifdef FEAT_CMDL_COMPL - "cmdline_compl", -#endif -#ifdef FEAT_CMDHIST - "cmdline_hist", -#endif -#ifdef FEAT_COMMENTS - "comments", -#endif -#ifdef FEAT_CONCEAL - "conceal", -#endif -#ifdef FEAT_CRYPT - "cryptv", - "crypt-blowfish", - "crypt-blowfish2", -#endif -#ifdef FEAT_CSCOPE - "cscope", -#endif -#ifdef FEAT_CURSORBIND - "cursorbind", -#endif -#ifdef CURSOR_SHAPE - "cursorshape", -#endif -#ifdef DEBUG - "debug", -#endif -#ifdef FEAT_CON_DIALOG - "dialog_con", -#endif -#ifdef FEAT_GUI_DIALOG - "dialog_gui", -#endif -#ifdef FEAT_DIFF - "diff", -#endif -#ifdef FEAT_DIGRAPHS - "digraphs", -#endif -#ifdef FEAT_DIRECTX - "directx", -#endif -#ifdef FEAT_DND - "dnd", -#endif -#ifdef FEAT_EMACS_TAGS - "emacs_tags", -#endif - "eval", /* always present, of course! */ - "ex_extra", /* graduated feature */ -#ifdef FEAT_SEARCH_EXTRA - "extra_search", -#endif -#ifdef FEAT_FKMAP - "farsi", -#endif -#ifdef FEAT_SEARCHPATH - "file_in_path", -#endif -#ifdef FEAT_FILTERPIPE - "filterpipe", -#endif -#ifdef FEAT_FIND_ID - "find_in_path", -#endif -#ifdef FEAT_FLOAT - "float", -#endif -#ifdef FEAT_FOLDING - "folding", -#endif -#ifdef FEAT_FOOTER - "footer", -#endif -#if !defined(USE_SYSTEM) && defined(UNIX) - "fork", -#endif -#ifdef FEAT_GETTEXT - "gettext", -#endif -#ifdef FEAT_GUI - "gui", -#endif -#ifdef FEAT_GUI_ATHENA -# ifdef FEAT_GUI_NEXTAW - "gui_neXtaw", -# else - "gui_athena", -# endif -#endif -#ifdef FEAT_GUI_GTK - "gui_gtk", -# ifdef USE_GTK3 - "gui_gtk3", -# else - "gui_gtk2", -# endif -#endif -#ifdef FEAT_GUI_GNOME - "gui_gnome", -#endif -#ifdef FEAT_GUI_MAC - "gui_mac", -#endif -#ifdef FEAT_GUI_MOTIF - "gui_motif", -#endif -#ifdef FEAT_GUI_PHOTON - "gui_photon", -#endif -#ifdef FEAT_GUI_W32 - "gui_win32", -#endif -#ifdef FEAT_HANGULIN - "hangul_input", -#endif -#if defined(HAVE_ICONV_H) && defined(USE_ICONV) - "iconv", -#endif -#ifdef FEAT_INS_EXPAND - "insert_expand", -#endif -#ifdef FEAT_JOB_CHANNEL - "job", -#endif -#ifdef FEAT_JUMPLIST - "jumplist", -#endif -#ifdef FEAT_KEYMAP - "keymap", -#endif -#ifdef FEAT_LANGMAP - "langmap", -#endif -#ifdef FEAT_LIBCALL - "libcall", -#endif -#ifdef FEAT_LINEBREAK - "linebreak", -#endif -#ifdef FEAT_LISP - "lispindent", -#endif -#ifdef FEAT_LISTCMDS - "listcmds", -#endif -#ifdef FEAT_LOCALMAP - "localmap", -#endif -#ifdef FEAT_LUA -# ifndef DYNAMIC_LUA - "lua", -# endif -#endif -#ifdef FEAT_MENU - "menu", -#endif -#ifdef FEAT_SESSION - "mksession", -#endif -#ifdef FEAT_MODIFY_FNAME - "modify_fname", -#endif -#ifdef FEAT_MOUSE - "mouse", -#endif -#ifdef FEAT_MOUSESHAPE - "mouseshape", -#endif -#if defined(UNIX) || defined(VMS) -# ifdef FEAT_MOUSE_DEC - "mouse_dec", -# endif -# ifdef FEAT_MOUSE_GPM - "mouse_gpm", -# endif -# ifdef FEAT_MOUSE_JSB - "mouse_jsbterm", -# endif -# ifdef FEAT_MOUSE_NET - "mouse_netterm", -# endif -# ifdef FEAT_MOUSE_PTERM - "mouse_pterm", -# endif -# ifdef FEAT_MOUSE_SGR - "mouse_sgr", -# endif -# ifdef FEAT_SYSMOUSE - "mouse_sysmouse", -# endif -# ifdef FEAT_MOUSE_URXVT - "mouse_urxvt", -# endif -# ifdef FEAT_MOUSE_XTERM - "mouse_xterm", -# endif -#endif -#ifdef FEAT_MBYTE - "multi_byte", -#endif -#ifdef FEAT_MBYTE_IME - "multi_byte_ime", -#endif -#ifdef FEAT_MULTI_LANG - "multi_lang", -#endif -#ifdef FEAT_MZSCHEME -#ifndef DYNAMIC_MZSCHEME - "mzscheme", -#endif -#endif -#ifdef FEAT_NUM64 - "num64", -#endif -#ifdef FEAT_OLE - "ole", -#endif - "packages", -#ifdef FEAT_PATH_EXTRA - "path_extra", -#endif -#ifdef FEAT_PERL -#ifndef DYNAMIC_PERL - "perl", -#endif -#endif -#ifdef FEAT_PERSISTENT_UNDO - "persistent_undo", -#endif -#ifdef FEAT_PYTHON -#ifndef DYNAMIC_PYTHON - "python", -#endif -#endif -#ifdef FEAT_PYTHON3 -#ifndef DYNAMIC_PYTHON3 - "python3", -#endif -#endif -#ifdef FEAT_POSTSCRIPT - "postscript", -#endif -#ifdef FEAT_PRINTER - "printer", -#endif -#ifdef FEAT_PROFILE - "profile", -#endif -#ifdef FEAT_RELTIME - "reltime", -#endif -#ifdef FEAT_QUICKFIX - "quickfix", -#endif -#ifdef FEAT_RIGHTLEFT - "rightleft", -#endif -#if defined(FEAT_RUBY) && !defined(DYNAMIC_RUBY) - "ruby", -#endif -#ifdef FEAT_SCROLLBIND - "scrollbind", -#endif -#ifdef FEAT_CMDL_INFO - "showcmd", - "cmdline_info", -#endif -#ifdef FEAT_SIGNS - "signs", -#endif -#ifdef FEAT_SMARTINDENT - "smartindent", -#endif -#ifdef STARTUPTIME - "startuptime", -#endif -#ifdef FEAT_STL_OPT - "statusline", -#endif -#ifdef FEAT_SUN_WORKSHOP - "sun_workshop", -#endif -#ifdef FEAT_NETBEANS_INTG - "netbeans_intg", -#endif -#ifdef FEAT_SPELL - "spell", -#endif -#ifdef FEAT_SYN_HL - "syntax", -#endif -#if defined(USE_SYSTEM) || !defined(UNIX) - "system", -#endif -#ifdef FEAT_TAG_BINS - "tag_binary", -#endif -#ifdef FEAT_TAG_OLDSTATIC - "tag_old_static", -#endif -#ifdef FEAT_TAG_ANYWHITE - "tag_any_white", -#endif -#ifdef FEAT_TCL -# ifndef DYNAMIC_TCL - "tcl", -# endif -#endif -#ifdef FEAT_TERMGUICOLORS - "termguicolors", -#endif -#ifdef TERMINFO - "terminfo", -#endif -#ifdef FEAT_TERMRESPONSE - "termresponse", -#endif -#ifdef FEAT_TEXTOBJ - "textobjects", -#endif -#ifdef HAVE_TGETENT - "tgetent", -#endif -#ifdef FEAT_TIMERS - "timers", -#endif -#ifdef FEAT_TITLE - "title", -#endif -#ifdef FEAT_TOOLBAR - "toolbar", -#endif -#if defined(FEAT_CLIPBOARD) && defined(FEAT_X11) - "unnamedplus", -#endif -#ifdef FEAT_USR_CMDS - "user-commands", /* was accidentally included in 5.4 */ - "user_commands", -#endif -#ifdef FEAT_VIMINFO - "viminfo", -#endif -#ifdef FEAT_WINDOWS - "vertsplit", -#endif -#ifdef FEAT_VIRTUALEDIT - "virtualedit", -#endif - "visual", -#ifdef FEAT_VISUALEXTRA - "visualextra", -#endif -#ifdef FEAT_VREPLACE - "vreplace", -#endif -#ifdef FEAT_WILDIGN - "wildignore", -#endif -#ifdef FEAT_WILDMENU - "wildmenu", -#endif -#ifdef FEAT_WINDOWS - "windows", -#endif -#ifdef FEAT_WAK - "winaltkeys", -#endif -#ifdef FEAT_WRITEBACKUP - "writebackup", -#endif -#ifdef FEAT_XIM - "xim", -#endif -#ifdef FEAT_XFONTSET - "xfontset", -#endif -#ifdef FEAT_XPM_W32 - "xpm", - "xpm_w32", /* for backward compatibility */ -#else -# if defined(HAVE_XPM) - "xpm", -# endif -#endif -#ifdef USE_XSMP - "xsmp", -#endif -#ifdef USE_XSMP_INTERACT - "xsmp_interact", -#endif -#ifdef FEAT_XCLIPBOARD - "xterm_clipboard", -#endif -#ifdef FEAT_XTERM_SAVE - "xterm_save", -#endif -#if defined(UNIX) && defined(FEAT_X11) - "X11", -#endif - NULL - }; - - name = get_tv_string(&argvars[0]); - for (i = 0; has_list[i] != NULL; ++i) - if (STRICMP(name, has_list[i]) == 0) - { - n = TRUE; - break; - } - - if (n == FALSE) - { - if (STRNICMP(name, "patch", 5) == 0) - { - 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) - n = (starting != 0); -#ifdef FEAT_MBYTE - else if (STRICMP(name, "multi_byte_encoding") == 0) - n = has_mbyte; -#endif -#if defined(FEAT_BEVAL) && defined(FEAT_GUI_W32) - else if (STRICMP(name, "balloon_multiline") == 0) - n = multiline_balloon_available(); -#endif -#ifdef DYNAMIC_TCL - else if (STRICMP(name, "tcl") == 0) - n = tcl_enabled(FALSE); -#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_RUBY - else if (STRICMP(name, "ruby") == 0) - n = ruby_enabled(FALSE); -#endif -#ifdef FEAT_PYTHON -#ifdef DYNAMIC_PYTHON - else if (STRICMP(name, "python") == 0) - n = python_enabled(FALSE); -#endif -#endif -#ifdef FEAT_PYTHON3 -#ifdef DYNAMIC_PYTHON3 - else if (STRICMP(name, "python3") == 0) - n = python3_enabled(FALSE); -#endif -#endif -#ifdef DYNAMIC_PERL - else if (STRICMP(name, "perl") == 0) - n = perl_enabled(FALSE); -#endif -#ifdef FEAT_GUI - else if (STRICMP(name, "gui_running") == 0) - n = (gui.in_use || gui.starting); -# ifdef FEAT_GUI_W32 - else if (STRICMP(name, "gui_win32s") == 0) - n = gui_is_win32s(); -# endif -# ifdef FEAT_BROWSE - else if (STRICMP(name, "browse") == 0) - n = gui.in_use; /* gui_mch_browse() works when GUI is running */ -# endif -#endif -#ifdef FEAT_SYN_HL - else if (STRICMP(name, "syntax_items") == 0) - n = syntax_present(curwin); -#endif -#if defined(WIN3264) - else if (STRICMP(name, "win95") == 0) - n = mch_windows95(); -#endif -#ifdef FEAT_NETBEANS_INTG - else if (STRICMP(name, "netbeans_enabled") == 0) - n = netbeans_active(); -#endif - } - - rettv->vval.v_number = n; -} - -/* - * "has_key()" function - */ - static void -f_has_key(typval_T *argvars, typval_T *rettv) -{ - if (argvars[0].v_type != VAR_DICT) - { - EMSG(_(e_dictreq)); - return; - } - if (argvars[0].vval.v_dict == NULL) - return; - - rettv->vval.v_number = dict_find(argvars[0].vval.v_dict, - get_tv_string(&argvars[1]), -1) != NULL; -} - -/* - * "haslocaldir()" function - */ - static void -f_haslocaldir(typval_T *argvars, typval_T *rettv) -{ - win_T *wp = NULL; - - wp = find_tabwin(&argvars[0], &argvars[1]); - rettv->vval.v_number = (wp != NULL && wp->w_localdir != NULL); -} - -/* - * "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; - - name = get_tv_string(&argvars[0]); - if (argvars[1].v_type == VAR_UNKNOWN) - mode = (char_u *)"nvo"; - else - { - mode = get_tv_string_buf(&argvars[1], buf); - if (argvars[2].v_type != VAR_UNKNOWN) - abbr = (int)get_tv_number(&argvars[2]); - } - - if (map_to_exists(name, mode, abbr)) - rettv->vval.v_number = TRUE; - else - rettv->vval.v_number = FALSE; -} - -/* - * "histadd()" function - */ - static void -f_histadd(typval_T *argvars UNUSED, typval_T *rettv) -{ -#ifdef FEAT_CMDHIST - int histype; - char_u *str; - char_u buf[NUMBUFLEN]; -#endif - - rettv->vval.v_number = FALSE; - if (check_restricted() || check_secure()) - return; -#ifdef FEAT_CMDHIST - str = get_tv_string_chk(&argvars[0]); /* NULL on type error */ - histype = str != NULL ? get_histtype(str) : -1; - if (histype >= 0) - { - str = get_tv_string_buf(&argvars[1], buf); - if (*str != NUL) - { - init_history(); - add_to_history(histype, str, FALSE, NUL); - rettv->vval.v_number = TRUE; - return; - } - } -#endif -} - -/* - * "histdel()" function - */ - static void -f_histdel(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_CMDHIST - int n; - char_u buf[NUMBUFLEN]; - char_u *str; - - str = get_tv_string_chk(&argvars[0]); /* NULL on type error */ - if (str == NULL) - n = 0; - else if (argvars[1].v_type == VAR_UNKNOWN) - /* only one argument: clear entire history */ - n = clr_history(get_histtype(str)); - else if (argvars[1].v_type == VAR_NUMBER) - /* index given: remove that entry */ - n = del_history_idx(get_histtype(str), - (int)get_tv_number(&argvars[1])); - else - /* string given: remove all matching entries */ - n = del_history_entry(get_histtype(str), - get_tv_string_buf(&argvars[1], buf)); - rettv->vval.v_number = n; -#endif -} - -/* - * "histget()" function - */ - static void -f_histget(typval_T *argvars UNUSED, typval_T *rettv) -{ -#ifdef FEAT_CMDHIST - int type; - int idx; - char_u *str; - - str = get_tv_string_chk(&argvars[0]); /* NULL on type error */ - if (str == NULL) - rettv->vval.v_string = NULL; - else - { - type = get_histtype(str); - if (argvars[1].v_type == VAR_UNKNOWN) - idx = get_history_idx(type); - else - idx = (int)get_tv_number_chk(&argvars[1], NULL); - /* -1 on type error */ - rettv->vval.v_string = vim_strsave(get_history_entry(type, idx)); - } -#else - rettv->vval.v_string = NULL; -#endif - rettv->v_type = VAR_STRING; -} - -/* - * "histnr()" function - */ - static void -f_histnr(typval_T *argvars UNUSED, typval_T *rettv) -{ - int i; - -#ifdef FEAT_CMDHIST - char_u *history = get_tv_string_chk(&argvars[0]); - - i = history == NULL ? HIST_CMD - 1 : get_histtype(history); - if (i >= HIST_CMD && i < HIST_COUNT) - i = get_history_idx(i); - else -#endif - i = -1; - rettv->vval.v_number = i; -} - -/* - * "highlightID(name)" function - */ - static void -f_hlID(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = syn_name2id(get_tv_string(&argvars[0])); -} - -/* - * "highlight_exists()" function - */ - static void -f_hlexists(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = highlight_exists(get_tv_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); -} - -/* - * iconv() function - */ - static void -f_iconv(typval_T *argvars UNUSED, typval_T *rettv) -{ -#ifdef FEAT_MBYTE - char_u buf1[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - char_u *from, *to, *str; - vimconv_T vimconv; -#endif - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - -#ifdef FEAT_MBYTE - str = get_tv_string(&argvars[0]); - from = enc_canonize(enc_skip(get_tv_string_buf(&argvars[1], buf1))); - to = enc_canonize(enc_skip(get_tv_string_buf(&argvars[2], buf2))); - vimconv.vc_type = CONV_NONE; - convert_setup(&vimconv, from, to); - - /* If the encodings are equal, no conversion needed. */ - if (vimconv.vc_type == CONV_NONE) - rettv->vval.v_string = vim_strsave(str); - else - rettv->vval.v_string = string_convert(&vimconv, str, NULL); - - convert_setup(&vimconv, NULL, NULL); - vim_free(from); - vim_free(to); -#endif -} - -/* - * "indent()" function - */ - static void -f_indent(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) - rettv->vval.v_number = get_indent_lnum(lnum); - else - rettv->vval.v_number = -1; -} - -/* - * "index()" function - */ - static void -f_index(typval_T *argvars, typval_T *rettv) -{ - list_T *l; - listitem_T *item; - long idx = 0; - int ic = FALSE; - - rettv->vval.v_number = -1; - if (argvars[0].v_type != VAR_LIST) - { - EMSG(_(e_listreq)); - return; - } - l = argvars[0].vval.v_list; - if (l != NULL) - { - item = l->lv_first; - if (argvars[2].v_type != VAR_UNKNOWN) - { - int error = FALSE; - - /* Start at specified item. Use the cached index that list_find() - * sets, so that a negative number also works. */ - item = list_find(l, (long)get_tv_number_chk(&argvars[2], &error)); - idx = l->lv_idx; - if (argvars[3].v_type != VAR_UNKNOWN) - ic = (int)get_tv_number_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; - -static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog); - -/* - * This function is used by f_input() and f_inputdialog() functions. The third - * argument to f_input() specifies the type of completion to use at the - * prompt. The third argument to f_inputdialog() specifies the value to return - * when the user cancels the prompt. - */ - static void -get_user_input( - typval_T *argvars, - typval_T *rettv, - int inputdialog) -{ - char_u *prompt = get_tv_string_chk(&argvars[0]); - char_u *p = NULL; - int c; - char_u buf[NUMBUFLEN]; - int cmd_silent_save = cmd_silent; - char_u *defstr = (char_u *)""; - int xp_type = EXPAND_NOTHING; - char_u *xp_arg = NULL; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - -#ifdef NO_CONSOLE_INPUT - /* While starting up, there is no place to enter text. */ - if (no_console_input()) - return; -#endif - - cmd_silent = FALSE; /* Want to see the prompt. */ - if (prompt != NULL) - { - /* Only the part of the message after the last NL is considered as - * prompt for the command line */ - p = vim_strrchr(prompt, '\n'); - if (p == NULL) - p = prompt; - else - { - ++p; - c = *p; - *p = NUL; - msg_start(); - msg_clr_eos(); - msg_puts_attr(prompt, echo_attr); - msg_didout = FALSE; - msg_starthere(); - *p = c; - } - cmdline_row = msg_row; - - if (argvars[1].v_type != VAR_UNKNOWN) - { - defstr = get_tv_string_buf_chk(&argvars[1], buf); - if (defstr != NULL) - stuffReadbuffSpec(defstr); - - if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) - { - char_u *xp_name; - int xp_namelen; - long argt; - - /* input() with a third argument: completion */ - rettv->vval.v_string = NULL; - - xp_name = get_tv_string_buf_chk(&argvars[2], buf); - if (xp_name == NULL) - return; - - xp_namelen = (int)STRLEN(xp_name); - - if (parse_compl_arg(xp_name, xp_namelen, &xp_type, &argt, - &xp_arg) == FAIL) - return; - } - } - - if (defstr != NULL) - { - int save_ex_normal_busy = ex_normal_busy; - ex_normal_busy = 0; - rettv->vval.v_string = - getcmdline_prompt(inputsecret_flag ? NUL : '@', p, echo_attr, - xp_type, xp_arg); - ex_normal_busy = save_ex_normal_busy; - } - if (inputdialog && rettv->vval.v_string == NULL - && argvars[1].v_type != VAR_UNKNOWN - && argvars[2].v_type != VAR_UNKNOWN) - rettv->vval.v_string = vim_strsave(get_tv_string_buf( - &argvars[2], buf)); - - vim_free(xp_arg); - - /* since the user typed this, no need to wait for return */ - need_wait_return = FALSE; - msg_didout = FALSE; - } - cmd_silent = cmd_silent_save; -} - -/* - * "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); -} - -/* - * "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 *)""; - - message = get_tv_string_chk(&argvars[0]); - if (argvars[1].v_type != VAR_UNKNOWN - && (defstr = get_tv_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( - get_tv_string_buf(&argvars[2], buf)); - else - rettv->vval.v_string = NULL; - } - rettv->v_type = VAR_STRING; - } - else -#endif - get_user_input(argvars, rettv, TRUE); -} - -/* - * "inputlist()" function - */ - static void -f_inputlist(typval_T *argvars, typval_T *rettv) -{ - listitem_T *li; - int selected; - int mouse_used; - -#ifdef NO_CONSOLE_INPUT - /* While starting up, there is no place to enter text. */ - if (no_console_input()) - return; -#endif - if (argvars[0].v_type != VAR_LIST || argvars[0].vval.v_list == NULL) - { - EMSG2(_(e_listarg), "inputlist()"); - return; - } - - msg_start(); - msg_row = Rows - 1; /* for when 'cmdheight' > 1 */ - lines_left = Rows; /* avoid more prompt */ - msg_scroll = TRUE; - msg_clr_eos(); - - for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next) - { - msg_puts(get_tv_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); - /* default return is zero == OK */ - } - else if (p_verbose > 1) - { - verb_msg((char_u *)_("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) -{ - ++cmdline_star; - ++inputsecret_flag; - f_input(argvars, rettv); - --cmdline_star; - --inputsecret_flag; -} - -/* - * "insert()" function - */ - static void -f_insert(typval_T *argvars, typval_T *rettv) -{ - long before = 0; - listitem_T *item; - list_T *l; - int error = FALSE; - - if (argvars[0].v_type != VAR_LIST) - EMSG2(_(e_listarg), "insert()"); - else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, (char_u *)N_("insert() argument"), TRUE)) - { - if (argvars[2].v_type != VAR_UNKNOWN) - before = (long)get_tv_number_chk(&argvars[2], &error); - if (error) - return; /* type error; errmsg already given */ - - if (before == l->lv_len) - item = NULL; - else - { - item = list_find(l, before); - if (item == NULL) - { - EMSGN(_(e_listidx), before); - l = NULL; - } - } - if (l != NULL) - { - list_insert_tv(l, &argvars[1], item); - copy_tv(&argvars[0], rettv); - } - } -} - -/* - * "invert(expr)" function - */ - static void -f_invert(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = ~get_tv_number_chk(&argvars[0], NULL); -} - -/* - * "isdirectory()" function - */ - static void -f_isdirectory(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = mch_isdir(get_tv_string(&argvars[0])); -} - -/* - * "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; - end = get_lval(get_tv_string(&argvars[0]), NULL, &lv, FALSE, FALSE, - GLV_NO_AUTOLOAD, FNE_CHECK_START); - if (end != NULL && lv.ll_name != NULL) - { - if (*end != NUL) - EMSG(_(e_trailing)); - else - { - if (lv.ll_tv == NULL) - { - if (check_changedtick(lv.ll_name)) - rettv->vval.v_number = 1; /* always locked */ - else - { - 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(_("E786: Range not allowed")); - else if (lv.ll_newkey != NULL) - EMSG2(_(e_dictkey), 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); -} - -#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H) -/* - * "isnan()" function - */ - static void -f_isnan(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = argvars[0].v_type == VAR_FLOAT - && isnan(argvars[0].vval.v_float); -} -#endif - -/* - * "items(dict)" function - */ - static void -f_items(typval_T *argvars, typval_T *rettv) -{ - dict_list(argvars, rettv, 2); -} - -#if defined(FEAT_JOB_CHANNEL) || defined(PROTO) -/* - * Get the job from the argument. - * Returns NULL if the job is invalid. - */ - static job_T * -get_job_arg(typval_T *tv) -{ - job_T *job; - - if (tv->v_type != VAR_JOB) - { - EMSG2(_(e_invarg2), get_tv_string(tv)); - return NULL; - } - job = tv->vval.v_job; - - if (job == NULL) - EMSG(_("E916: not a valid job")); - return job; -} - -/* - * "job_getchannel()" function - */ - static void -f_job_getchannel(typval_T *argvars, typval_T *rettv) -{ - job_T *job = get_job_arg(&argvars[0]); - - if (job != NULL) - { - rettv->v_type = VAR_CHANNEL; - rettv->vval.v_channel = job->jv_channel; - if (job->jv_channel != NULL) - ++job->jv_channel->ch_refcount; - } -} - -/* - * "job_info()" function - */ - static void -f_job_info(typval_T *argvars, typval_T *rettv) -{ - job_T *job = get_job_arg(&argvars[0]); - - if (job != NULL && rettv_dict_alloc(rettv) != FAIL) - job_info(job, rettv->vval.v_dict); -} - -/* - * "job_setoptions()" function - */ - static void -f_job_setoptions(typval_T *argvars, typval_T *rettv UNUSED) -{ - job_T *job = get_job_arg(&argvars[0]); - jobopt_T opt; - - if (job == NULL) - return; - clear_job_options(&opt); - if (get_job_options(&argvars[1], &opt, JO_STOPONEXIT + JO_EXIT_CB) == OK) - job_set_options(job, &opt); - free_job_options(&opt); -} - -/* - * "job_start()" function - */ - static void -f_job_start(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_JOB; - if (check_restricted() || check_secure()) - return; - rettv->vval.v_job = job_start(argvars); -} - -/* - * "job_status()" function - */ - static void -f_job_status(typval_T *argvars, typval_T *rettv) -{ - job_T *job = get_job_arg(&argvars[0]); - - if (job != NULL) - { - rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave((char_u *)job_status(job)); - } -} - -/* - * "job_stop()" function - */ - static void -f_job_stop(typval_T *argvars, typval_T *rettv) -{ - job_T *job = get_job_arg(&argvars[0]); - - if (job != NULL) - rettv->vval.v_number = job_stop(job, argvars); -} -#endif - -/* - * "join()" function - */ - static void -f_join(typval_T *argvars, typval_T *rettv) -{ - garray_T ga; - char_u *sep; - - if (argvars[0].v_type != VAR_LIST) - { - EMSG(_(e_listreq)); - return; - } - if (argvars[0].vval.v_list == NULL) - return; - if (argvars[1].v_type == VAR_UNKNOWN) - sep = (char_u *)" "; - else - sep = get_tv_string_chk(&argvars[1]); - - rettv->v_type = VAR_STRING; - - if (sep != NULL) - { - ga_init2(&ga, (int)sizeof(char), 80); - list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0); - ga_append(&ga, NUL); - rettv->vval.v_string = (char_u *)ga.ga_data; - } - else - rettv->vval.v_string = NULL; -} - -/* - * "js_decode()" function - */ - static void -f_js_decode(typval_T *argvars, typval_T *rettv) -{ - js_read_T reader; - - reader.js_buf = get_tv_string(&argvars[0]); - reader.js_fill = NULL; - reader.js_used = 0; - if (json_decode_all(&reader, rettv, JSON_JS) != OK) - EMSG(_(e_invarg)); -} - -/* - * "js_encode()" function - */ - static void -f_js_encode(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = json_encode(&argvars[0], JSON_JS); -} - -/* - * "json_decode()" function - */ - static void -f_json_decode(typval_T *argvars, typval_T *rettv) -{ - js_read_T reader; - - reader.js_buf = get_tv_string(&argvars[0]); - reader.js_fill = NULL; - reader.js_used = 0; - if (json_decode_all(&reader, rettv, 0) != OK) - EMSG(_(e_invarg)); -} - -/* - * "json_encode()" function - */ - static void -f_json_encode(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = json_encode(&argvars[0], 0); -} - -/* - * "keys()" function - */ - static void -f_keys(typval_T *argvars, typval_T *rettv) -{ - dict_list(argvars, rettv, 0); -} - -/* - * "last_buffer_nr()" function. - */ - static void -f_last_buffer_nr(typval_T *argvars UNUSED, typval_T *rettv) -{ - int n = 0; - buf_T *buf; - - for (buf = firstbuf; buf != NULL; buf = buf->b_next) - if (n < buf->b_fnum) - n = buf->b_fnum; - - rettv->vval.v_number = n; -} - -/* - * "len()" function - */ - static 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( - get_tv_string(&argvars[0])); - 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_SPECIAL: - case VAR_FLOAT: - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_JOB: - case VAR_CHANNEL: - EMSG(_("E701: Invalid type for len()")); - break; - } -} - -static void libcall_common(typval_T *argvars, typval_T *rettv, int type); - - static void -libcall_common(typval_T *argvars, 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; - -#ifdef FEAT_LIBCALL - /* The first two args must be strings, otherwise its 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 - 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)" function - */ - static void -f_line(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum = 0; - pos_T *fp; - int fnum; - - fp = var2fpos(&argvars[0], TRUE, &fnum); - 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; - - lnum = get_tv_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 -} - -/* - * "lispindent(lnum)" function - */ - static void -f_lispindent(typval_T *argvars UNUSED, typval_T *rettv) -{ -#ifdef FEAT_LISP - pos_T pos; - linenr_T lnum; - - pos = curwin->w_cursor; - lnum = get_tv_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) - { - curwin->w_cursor.lnum = lnum; - rettv->vval.v_number = get_lisp_indent(); - curwin->w_cursor = pos; - } - else -#endif - rettv->vval.v_number = -1; -} - -/* - * "localtime()" function - */ - static void -f_localtime(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->vval.v_number = (varnumber_T)time(NULL); -} - -static void get_maparg(typval_T *argvars, typval_T *rettv, int exact); - - static void -get_maparg(typval_T *argvars, typval_T *rettv, int exact) -{ - char_u *keys; - char_u *which; - char_u buf[NUMBUFLEN]; - char_u *keys_buf = NULL; - char_u *rhs; - int mode; - int abbr = FALSE; - int get_dict = FALSE; - mapblock_T *mp; - int buffer_local; - - /* return empty string for failure */ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - keys = get_tv_string(&argvars[0]); - if (*keys == NUL) - return; - - if (argvars[1].v_type != VAR_UNKNOWN) - { - which = get_tv_string_buf_chk(&argvars[1], buf); - if (argvars[2].v_type != VAR_UNKNOWN) - { - abbr = (int)get_tv_number(&argvars[2]); - if (argvars[3].v_type != VAR_UNKNOWN) - get_dict = (int)get_tv_number(&argvars[3]); - } - } - else - which = (char_u *)""; - if (which == NULL) - return; - - mode = get_map_mode(&which, 0); - - keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, FALSE); - rhs = check_map(keys, mode, exact, FALSE, abbr, &mp, &buffer_local); - vim_free(keys_buf); - - if (!get_dict) - { - /* Return a string. */ - if (rhs != NULL) - rettv->vval.v_string = str2special_save(rhs, FALSE); - - } - else if (rettv_dict_alloc(rettv) != FAIL && rhs != NULL) - { - /* Return a dictionary. */ - char_u *lhs = str2special_save(mp->m_keys, TRUE); - char_u *mapmode = map_mode_to_chars(mp->m_mode); - dict_T *dict = rettv->vval.v_dict; - - dict_add_nr_str(dict, "lhs", 0L, lhs); - dict_add_nr_str(dict, "rhs", 0L, mp->m_orig_str); - dict_add_nr_str(dict, "noremap", mp->m_noremap ? 1L : 0L , NULL); - dict_add_nr_str(dict, "expr", mp->m_expr ? 1L : 0L, NULL); - dict_add_nr_str(dict, "silent", mp->m_silent ? 1L : 0L, NULL); - dict_add_nr_str(dict, "sid", (long)mp->m_script_ID, NULL); - dict_add_nr_str(dict, "buffer", (long)buffer_local, NULL); - dict_add_nr_str(dict, "nowait", mp->m_nowait ? 1L : 0L, NULL); - dict_add_nr_str(dict, "mode", 0L, mapmode); - - vim_free(lhs); - vim_free(mapmode); - } -} - -#ifdef FEAT_FLOAT -/* - * "log()" function - */ - static void -f_log(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = log(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "log10()" function - */ - static void -f_log10(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = log10(f); - else - rettv->vval.v_float = 0.0; -} -#endif - -#ifdef FEAT_LUA -/* - * "luaeval()" function - */ - static void -f_luaeval(typval_T *argvars, typval_T *rettv) -{ - char_u *str; - char_u buf[NUMBUFLEN]; - - str = get_tv_string_buf(&argvars[0], buf); - do_luaeval(str, argvars + 1, rettv); -} -#endif - -/* - * "map()" function - */ - static void -f_map(typval_T *argvars, typval_T *rettv) -{ - filter_map(argvars, rettv, TRUE); -} - -/* - * "maparg()" function - */ - static void -f_maparg(typval_T *argvars, typval_T *rettv) -{ - get_maparg(argvars, rettv, TRUE); -} - -/* - * "mapcheck()" function - */ - static void -f_mapcheck(typval_T *argvars, typval_T *rettv) -{ - get_maparg(argvars, rettv, FALSE); -} - -static void find_some_match(typval_T *argvars, typval_T *rettv, int start); - - static void -find_some_match(typval_T *argvars, typval_T *rettv, int 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 = (char_u *)""; - - rettv->vval.v_number = -1; - if (type == 3 || type == 4) - { - /* type 3: return empty list when there are no matches. - * type 4: return ["", -1, -1, -1] */ - if (rettv_list_alloc(rettv) == FAIL) - goto theend; - if (type == 4 - && (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 == 2) - { - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - } - - if (argvars[0].v_type == VAR_LIST) - { - if ((l = argvars[0].vval.v_list) == NULL) - goto theend; - li = l->lv_first; - } - else - { - expr = str = get_tv_string(&argvars[0]); - len = (long)STRLEN(str); - } - - pat = get_tv_string_buf_chk(&argvars[1], patbuf); - if (pat == NULL) - goto theend; - - if (argvars[2].v_type != VAR_UNKNOWN) - { - int error = FALSE; - - start = (long)get_tv_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_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)get_tv_number_chk(&argvars[3], &error); - if (error) - goto theend; - } - - regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); - if (regmatch.regprog != NULL) - { - regmatch.rm_ic = p_ic; - - for (;;) - { - if (l != NULL) - { - if (li == NULL) - { - match = FALSE; - break; - } - vim_free(tofree); - expr = str = echo_string(&li->li_tv, &tofree, strbuf, 0); - if (str == NULL) - break; - } - - match = vim_regexec_nl(®match, str, (colnr_T)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 - { -#ifdef FEAT_MBYTE - startcol = (colnr_T)(regmatch.startp[0] - + (*mb_ptr2len)(regmatch.startp[0]) - str); -#else - startcol = (colnr_T)(regmatch.startp[0] + 1 - str); -#endif - if (startcol > (colnr_T)len - || str + startcol <= regmatch.startp[0]) - { - match = FALSE; - break; - } - } - } - - if (match) - { - if (type == 4) - { - 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], - (int)(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 == 3) - { - 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 == 2) - { - /* return matched string */ - if (l != NULL) - copy_tv(&li->li_tv, rettv); - else - rettv->vval.v_string = vim_strnsave(regmatch.startp[0], - (int)(regmatch.endp[0] - regmatch.startp[0])); - } - else if (l != NULL) - rettv->vval.v_number = idx; - else - { - if (type != 0) - 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); - } - - if (type == 4 && l == NULL) - /* matchstrpos() without a list: drop the second item. */ - listitem_remove(rettv->vval.v_list, - rettv->vval.v_list->lv_first->li_next); - -theend: - vim_free(tofree); - p_cpo = save_cpo; -} - -/* - * "match()" function - */ - static void -f_match(typval_T *argvars, typval_T *rettv) -{ - find_some_match(argvars, rettv, 1); -} - -/* - * "matchadd()" function - */ - static void -f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_SEARCH_EXTRA - char_u buf[NUMBUFLEN]; - char_u *grp = get_tv_string_buf_chk(&argvars[0], buf); /* group */ - char_u *pat = get_tv_string_buf_chk(&argvars[1], buf); /* pattern */ - int prio = 10; /* default priority */ - int id = -1; - int error = FALSE; - char_u *conceal_char = NULL; - - rettv->vval.v_number = -1; - - if (grp == NULL || pat == NULL) - return; - if (argvars[2].v_type != VAR_UNKNOWN) - { - prio = (int)get_tv_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) - { - id = (int)get_tv_number_chk(&argvars[3], &error); - if (argvars[4].v_type != VAR_UNKNOWN) - { - if (argvars[4].v_type != VAR_DICT) - { - EMSG(_(e_dictreq)); - return; - } - if (dict_find(argvars[4].vval.v_dict, - (char_u *)"conceal", -1) != NULL) - conceal_char = get_dict_string(argvars[4].vval.v_dict, - (char_u *)"conceal", FALSE); - } - } - } - if (error == TRUE) - return; - if (id >= 1 && id <= 3) - { - EMSGN("E798: ID is reserved for \":match\": %ld", id); - return; - } - - rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL, - conceal_char); -#endif -} - -/* - * "matchaddpos()" function - */ - static void -f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_SEARCH_EXTRA - char_u buf[NUMBUFLEN]; - char_u *group; - int prio = 10; - int id = -1; - int error = FALSE; - list_T *l; - char_u *conceal_char = NULL; - - rettv->vval.v_number = -1; - - group = get_tv_string_buf_chk(&argvars[0], buf); - if (group == NULL) - return; - - if (argvars[1].v_type != VAR_LIST) - { - EMSG2(_(e_listarg), "matchaddpos()"); - return; - } - l = argvars[1].vval.v_list; - if (l == NULL) - return; - - if (argvars[2].v_type != VAR_UNKNOWN) - { - prio = (int)get_tv_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) - { - id = (int)get_tv_number_chk(&argvars[3], &error); - if (argvars[4].v_type != VAR_UNKNOWN) - { - if (argvars[4].v_type != VAR_DICT) - { - EMSG(_(e_dictreq)); - return; - } - if (dict_find(argvars[4].vval.v_dict, - (char_u *)"conceal", -1) != NULL) - conceal_char = get_dict_string(argvars[4].vval.v_dict, - (char_u *)"conceal", FALSE); - } - } - } - if (error == TRUE) - return; - - /* id == 3 is ok because matchaddpos() is supposed to substitute :3match */ - if (id == 1 || id == 2) - { - EMSGN("E798: ID is reserved for \":match\": %ld", id); - return; - } - - rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l, - conceal_char); -#endif -} - -/* - * "matcharg()" function - */ - static void -f_matcharg(typval_T *argvars UNUSED, typval_T *rettv) -{ - if (rettv_list_alloc(rettv) == OK) - { -#ifdef FEAT_SEARCH_EXTRA - int id = (int)get_tv_number(&argvars[0]); - matchitem_T *m; - - if (id >= 1 && id <= 3) - { - if ((m = (matchitem_T *)get_match(curwin, id)) != NULL) - { - list_append_string(rettv->vval.v_list, - syn_id2name(m->hlg_id), -1); - list_append_string(rettv->vval.v_list, m->pattern, -1); - } - else - { - list_append_string(rettv->vval.v_list, NULL, -1); - list_append_string(rettv->vval.v_list, NULL, -1); - } - } -#endif - } -} - -/* - * "matchdelete()" function - */ - static void -f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_SEARCH_EXTRA - rettv->vval.v_number = match_delete(curwin, - (int)get_tv_number(&argvars[0]), TRUE); -#endif -} - -/* - * "matchend()" function - */ - static void -f_matchend(typval_T *argvars, typval_T *rettv) -{ - find_some_match(argvars, rettv, 0); -} - -/* - * "matchlist()" function - */ - static void -f_matchlist(typval_T *argvars, typval_T *rettv) -{ - find_some_match(argvars, rettv, 3); -} - -/* - * "matchstr()" function - */ - static void -f_matchstr(typval_T *argvars, typval_T *rettv) -{ - find_some_match(argvars, rettv, 2); -} - -/* - * "matchstrpos()" function - */ - static void -f_matchstrpos(typval_T *argvars, typval_T *rettv) -{ - find_some_match(argvars, rettv, 4); -} - -static void max_min(typval_T *argvars, typval_T *rettv, int domax); - - static void -max_min(typval_T *argvars, typval_T *rettv, int domax) -{ - varnumber_T n = 0; - varnumber_T i; - int error = FALSE; - - if (argvars[0].v_type == VAR_LIST) - { - list_T *l; - listitem_T *li; - - l = argvars[0].vval.v_list; - if (l != NULL) - { - li = l->lv_first; - if (li != NULL) - { - n = get_tv_number_chk(&li->li_tv, &error); - for (;;) - { - li = li->li_next; - if (li == NULL) - break; - i = get_tv_number_chk(&li->li_tv, &error); - 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 = get_tv_number_chk(&HI2DI(hi)->di_tv, &error); - if (first) - { - n = i; - first = FALSE; - } - else if (domax ? i > n : i < n) - n = i; - } - } - } - } - else - EMSG(_(e_listdictarg)); - rettv->vval.v_number = error ? 0 : 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); -} - -static int mkdir_recurse(char_u *dir, int prot); - -/* - * Create the directory in which "dir" is located, and higher levels when - * needed. - */ - static int -mkdir_recurse(char_u *dir, int prot) -{ - char_u *p; - char_u *updir; - int r = FAIL; - - /* Get end of directory name in "dir". - * We're done when it's "/" or "c:/". */ - p = gettail_sep(dir); - if (p <= get_past_head(dir)) - return OK; - - /* If the directory exists we're done. Otherwise: create it.*/ - updir = vim_strnsave(dir, (int)(p - dir)); - if (updir == NULL) - return FAIL; - if (mch_isdir(updir)) - r = OK; - else if (mkdir_recurse(updir, prot) == OK) - r = vim_mkdir_emsg(updir, prot); - vim_free(updir); - return r; -} - -#ifdef vim_mkdir -/* - * "mkdir()" function - */ - static void -f_mkdir(typval_T *argvars, typval_T *rettv) -{ - char_u *dir; - char_u buf[NUMBUFLEN]; - int prot = 0755; - - rettv->vval.v_number = FAIL; - if (check_restricted() || check_secure()) - return; - - dir = get_tv_string_buf(&argvars[0], buf); - if (*dir == NUL) - rettv->vval.v_number = FAIL; - else - { - if (*gettail(dir) == NUL) - /* remove trailing slashes */ - *gettail_sep(dir) = NUL; - - if (argvars[1].v_type != VAR_UNKNOWN) - { - if (argvars[2].v_type != VAR_UNKNOWN) - prot = (int)get_tv_number_chk(&argvars[2], NULL); - if (prot != -1 && STRCMP(get_tv_string(&argvars[1]), "p") == 0) - mkdir_recurse(dir, prot); - } - rettv->vval.v_number = prot == -1 ? FAIL : vim_mkdir_emsg(dir, prot); - } -} -#endif - -/* - * "mode()" function - */ - static void -f_mode(typval_T *argvars, typval_T *rettv) -{ - char_u buf[3]; - - buf[1] = NUL; - buf[2] = NUL; - - if (time_for_testing == 93784) - { - /* Testing the two-character code. */ - buf[0] = 'x'; - buf[1] = '!'; - } - else if (VIsual_active) - { - if (VIsual_select) - buf[0] = VIsual_mode + 's' - 'v'; - else - buf[0] = VIsual_mode; - } - else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE - || State == CONFIRM) - { - buf[0] = 'r'; - if (State == ASKMORE) - buf[1] = 'm'; - else if (State == CONFIRM) - buf[1] = '?'; - } - else if (State == EXTERNCMD) - buf[0] = '!'; - else if (State & INSERT) - { -#ifdef FEAT_VREPLACE - if (State & VREPLACE_FLAG) - { - buf[0] = 'R'; - buf[1] = 'v'; - } - else -#endif - if (State & REPLACE_FLAG) - buf[0] = 'R'; - else - buf[0] = 'i'; - } - else if (State & CMDLINE) - { - buf[0] = 'c'; - if (exmode_active) - buf[1] = 'v'; - } - else if (exmode_active) - { - buf[0] = 'c'; - buf[1] = 'e'; - } - else - { - buf[0] = 'n'; - if (finish_op) - buf[1] = 'o'; - } - - /* Clear out the minor mode when the argument is not a non-zero number or - * non-empty string. */ - if (!non_zero_arg(&argvars[0])) - buf[1] = NUL; - - rettv->vval.v_string = vim_strsave(buf); - rettv->v_type = VAR_STRING; -} - -#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]; - - str = get_tv_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; - - for (lnum = get_tv_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]; - -#ifdef FEAT_MBYTE - if (has_mbyte) - { - int utf8 = 0; - - if (argvars[1].v_type != VAR_UNKNOWN) - utf8 = (int)get_tv_number_chk(&argvars[1], NULL); - if (utf8) - buf[(*utf_char2bytes)((int)get_tv_number(&argvars[0]), buf)] = NUL; - else - buf[(*mb_char2bytes)((int)get_tv_number(&argvars[0]), buf)] = NUL; - } - else -#endif - { - buf[0] = (char_u)get_tv_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) -{ - rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) - | get_tv_number_chk(&argvars[1], NULL); -} - -/* - * "pathshorten()" function - */ - static void -f_pathshorten(typval_T *argvars, typval_T *rettv) -{ - char_u *p; - - rettv->v_type = VAR_STRING; - p = get_tv_string_chk(&argvars[0]); - if (p == NULL) - rettv->vval.v_string = NULL; - else - { - p = vim_strsave(p); - rettv->vval.v_string = p; - if (p != NULL) - shorten_dir(p); - } -} - -#ifdef FEAT_PERL -/* - * "perleval()" function - */ - static void -f_perleval(typval_T *argvars, typval_T *rettv) -{ - char_u *str; - char_u buf[NUMBUFLEN]; - - str = get_tv_string_buf(&argvars[0], buf); - do_perleval(str, rettv); -} -#endif - -#ifdef FEAT_FLOAT -/* - * "pow()" function - */ - static void -f_pow(typval_T *argvars, typval_T *rettv) -{ - float_T fx = 0.0, fy = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) - rettv->vval.v_float = pow(fx, fy); - else - rettv->vval.v_float = 0.0; -} -#endif - -/* - * "prevnonblank()" function - */ - static void -f_prevnonblank(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum; - - lnum = get_tv_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; - - /* Get the required length, allocate the buffer and do it for real. */ - did_emsg = FALSE; - fmt = (char *)get_tv_string_buf(&argvars[0], buf); - len = vim_vsnprintf(NULL, 0, fmt, ap, argvars + 1); - if (!did_emsg) - { - s = alloc(len + 1); - if (s != NULL) - { - rettv->vval.v_string = s; - (void)vim_vsnprintf((char *)s, len + 1, fmt, ap, argvars + 1); - } - } - did_emsg |= saved_did_emsg; -} - -/* - * "pumvisible()" function - */ - static void -f_pumvisible(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_INS_EXPAND - if (pum_visible()) - rettv->vval.v_number = 1; -#endif -} - -#ifdef FEAT_PYTHON3 -/* - * "py3eval()" function - */ - static void -f_py3eval(typval_T *argvars, typval_T *rettv) -{ - char_u *str; - char_u buf[NUMBUFLEN]; - - str = get_tv_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]; - - str = get_tv_string_buf(&argvars[0], buf); - do_pyeval(str, rettv); -} -#endif - -/* - * "range()" function - */ - static void -f_range(typval_T *argvars, typval_T *rettv) -{ - varnumber_T start; - varnumber_T end; - varnumber_T stride = 1; - varnumber_T i; - int error = FALSE; - - start = get_tv_number_chk(&argvars[0], &error); - if (argvars[1].v_type == VAR_UNKNOWN) - { - end = start - 1; - start = 0; - } - else - { - end = get_tv_number_chk(&argvars[1], &error); - if (argvars[2].v_type != VAR_UNKNOWN) - stride = get_tv_number_chk(&argvars[2], &error); - } - - if (error) - return; /* type error; errmsg already given */ - if (stride == 0) - EMSG(_("E726: Stride is zero")); - else if (stride > 0 ? end + 1 < start : end - 1 > start) - EMSG(_("E727: Start past end")); - else - { - if (rettv_list_alloc(rettv) == OK) - for (i = start; stride > 0 ? i <= end : i >= end; i += stride) - if (list_append_number(rettv->vval.v_list, - (varnumber_T)i) == FAIL) - break; - } -} - -/* - * "readfile()" function - */ - static void -f_readfile(typval_T *argvars, typval_T *rettv) -{ - int binary = FALSE; - int failed = FALSE; - char_u *fname; - FILE *fd; - char_u buf[(IOSIZE/256)*256]; /* rounded to avoid odd + 1 */ - int io_size = sizeof(buf); - int readlen; /* size of last fread() */ - char_u *prev = NULL; /* previously read bytes, if any */ - long prevlen = 0; /* length of data in prev */ - long prevsize = 0; /* size of prev buffer */ - long maxline = MAXLNUM; - long cnt = 0; - char_u *p; /* position in buf */ - char_u *start; /* start of current line */ - - if (argvars[1].v_type != VAR_UNKNOWN) - { - if (STRCMP(get_tv_string(&argvars[1]), "b") == 0) - binary = TRUE; - if (argvars[2].v_type != VAR_UNKNOWN) - maxline = (long)get_tv_number(&argvars[2]); - } - - if (rettv_list_alloc(rettv) == FAIL) - return; - - /* Always open the file in binary mode, library functions have a mind of - * their own about CR-LF conversion. */ - fname = get_tv_string(&argvars[0]); - if (*fname == NUL || (fd = mch_fopen((char *)fname, READBIN)) == NULL) - { - EMSG2(_(e_notopen), *fname == NUL ? (char_u *)_("") : fname); - return; - } - - while (cnt < maxline || maxline < 0) - { - readlen = (int)fread(buf, 1, io_size, fd); - - /* This for loop processes what was read, but is also entered at end - * of file so that either: - * - an incomplete line gets written - * - a "binary" file gets an empty line at the end if it ends in a - * newline. */ - for (p = buf, start = buf; - p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary)); - ++p) - { - if (*p == '\n' || readlen <= 0) - { - listitem_T *li; - char_u *s = NULL; - long_u len = p - start; - - /* Finished a line. Remove CRs before NL. */ - if (readlen > 0 && !binary) - { - while (len > 0 && start[len - 1] == '\r') - --len; - /* removal may cross back to the "prev" string */ - if (len == 0) - while (prevlen > 0 && prev[prevlen - 1] == '\r') - --prevlen; - } - if (prevlen == 0) - s = vim_strnsave(start, (int)len); - else - { - /* Change "prev" buffer to be the right size. This way - * the bytes are only copied once, and very long lines are - * allocated only once. */ - if ((s = vim_realloc(prev, prevlen + len + 1)) != NULL) - { - mch_memmove(s + prevlen, start, len); - s[prevlen + len] = NUL; - prev = NULL; /* the list will own the string */ - prevlen = prevsize = 0; - } - } - if (s == NULL) - { - do_outofmem_msg((long_u) prevlen + len + 1); - failed = TRUE; - break; - } - - if ((li = listitem_alloc()) == NULL) - { - vim_free(s); - failed = TRUE; - break; - } - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = s; - list_append(rettv->vval.v_list, li); - - start = p + 1; /* step over newline */ - if ((++cnt >= maxline && maxline >= 0) || readlen <= 0) - break; - } - else if (*p == NUL) - *p = '\n'; -#ifdef FEAT_MBYTE - /* Check for utf8 "bom"; U+FEFF is encoded as EF BB BF. Do this - * when finding the BF and check the previous two bytes. */ - else if (*p == 0xbf && enc_utf8 && !binary) - { - /* Find the two bytes before the 0xbf. If p is at buf, or buf - * + 1, these may be in the "prev" string. */ - char_u back1 = p >= buf + 1 ? p[-1] - : prevlen >= 1 ? prev[prevlen - 1] : NUL; - char_u back2 = p >= buf + 2 ? p[-2] - : p == buf + 1 && prevlen >= 1 ? prev[prevlen - 1] - : prevlen >= 2 ? prev[prevlen - 2] : NUL; - - if (back2 == 0xef && back1 == 0xbb) - { - char_u *dest = p - 2; - - /* Usually a BOM is at the beginning of a file, and so at - * the beginning of a line; then we can just step over it. - */ - if (start == dest) - start = p + 1; - else - { - /* have to shuffle buf to close gap */ - int adjust_prevlen = 0; - - if (dest < buf) - { - adjust_prevlen = (int)(buf - dest); /* must be 1 or 2 */ - dest = buf; - } - if (readlen > p - buf + 1) - mch_memmove(dest, p + 1, readlen - (p - buf) - 1); - readlen -= 3 - adjust_prevlen; - prevlen -= adjust_prevlen; - p = dest - 1; - } - } - } -#endif - } /* for */ - - if (failed || (cnt >= maxline && maxline >= 0) || readlen <= 0) - break; - if (start < p) - { - /* There's part of a line in buf, store it in "prev". */ - if (p - start + prevlen >= prevsize) - { - /* need bigger "prev" buffer */ - char_u *newprev; - - /* A common use case is ordinary text files and "prev" gets a - * fragment of a line, so the first allocation is made - * small, to avoid repeatedly 'allocing' large and - * 'reallocing' small. */ - if (prevsize == 0) - prevsize = (long)(p - start); - else - { - long grow50pc = (prevsize * 3) / 2; - long growmin = (long)((p - start) * 2 + prevlen); - prevsize = grow50pc > growmin ? grow50pc : growmin; - } - newprev = prev == NULL ? alloc(prevsize) - : vim_realloc(prev, prevsize); - if (newprev == NULL) - { - do_outofmem_msg((long_u)prevsize); - failed = TRUE; - break; - } - prev = newprev; - } - /* Add the line part to end of "prev". */ - mch_memmove(prev + prevlen, start, p - start); - prevlen += (long)(p - start); - } - } /* while */ - - /* - * For a negative line count use only the lines at the end of the file, - * free the rest. - */ - if (!failed && maxline < 0) - while (cnt > -maxline) - { - listitem_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); - --cnt; - } - - if (failed) - { - list_free(rettv->vval.v_list); - /* readfile doc says an empty list is returned on error */ - rettv->vval.v_list = list_alloc(); - } - - vim_free(prev); - fclose(fd); -} - -#if defined(FEAT_RELTIME) -static int list2proftime(typval_T *arg, proftime_T *tm); - -/* - * Convert a List to proftime_T. - * Return FAIL when there is something wrong. - */ - static int -list2proftime(typval_T *arg, proftime_T *tm) -{ - long n1, n2; - int error = FALSE; - - if (arg->v_type != VAR_LIST || arg->vval.v_list == NULL - || arg->vval.v_list->lv_len != 2) - return FAIL; - n1 = list_find_nr(arg->vval.v_list, 0L, &error); - n2 = list_find_nr(arg->vval.v_list, 1L, &error); -# ifdef WIN3264 - tm->HighPart = n1; - tm->LowPart = n2; -# else - tm->tv_sec = n1; - tm->tv_usec = n2; -# endif - return error ? FAIL : OK; -} -#endif /* FEAT_RELTIME */ - -/* - * "reltime()" function - */ - static void -f_reltime(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_RELTIME - proftime_T res; - proftime_T start; - - if (argvars[0].v_type == VAR_UNKNOWN) - { - /* No arguments: get current time. */ - profile_start(&res); - } - else if (argvars[1].v_type == VAR_UNKNOWN) - { - if (list2proftime(&argvars[0], &res) == FAIL) - return; - profile_end(&res); - } - else - { - /* Two arguments: compute the difference. */ - if (list2proftime(&argvars[0], &start) == FAIL - || list2proftime(&argvars[1], &res) == FAIL) - return; - profile_sub(&res, &start); - } - - if (rettv_list_alloc(rettv) == OK) - { - long n1, n2; - -# ifdef WIN3264 - n1 = res.HighPart; - n2 = res.LowPart; -# else - n1 = res.tv_sec; - n2 = res.tv_usec; -# endif - list_append_number(rettv->vval.v_list, (varnumber_T)n1); - list_append_number(rettv->vval.v_list, (varnumber_T)n2); - } -#endif -} - -#ifdef FEAT_FLOAT -/* - * "reltimefloat()" function - */ - static void -f_reltimefloat(typval_T *argvars UNUSED, typval_T *rettv) -{ -# ifdef FEAT_RELTIME - proftime_T tm; -# endif - - rettv->v_type = VAR_FLOAT; - rettv->vval.v_float = 0; -# ifdef FEAT_RELTIME - if (list2proftime(&argvars[0], &tm) == OK) - rettv->vval.v_float = profile_float(&tm); -# endif -} -#endif - -/* - * "reltimestr()" function - */ - static void -f_reltimestr(typval_T *argvars UNUSED, typval_T *rettv) -{ -#ifdef FEAT_RELTIME - proftime_T tm; -#endif - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; -#ifdef FEAT_RELTIME - if (list2proftime(&argvars[0], &tm) == OK) - rettv->vval.v_string = vim_strsave((char_u *)profile_msg(&tm)); -#endif -} - -#if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11) -static void make_connection(void); -static int check_connection(void); - - static void -make_connection(void) -{ - if (X_DISPLAY == NULL -# ifdef FEAT_GUI - && !gui.in_use -# endif - ) - { - x_force_connect = TRUE; - setup_term_clip(); - x_force_connect = FALSE; - } -} - - static int -check_connection(void) -{ - make_connection(); - if (X_DISPLAY == NULL) - { - EMSG(_("E240: No connection to Vim server")); - return FAIL; - } - return OK; -} -#endif - -#ifdef FEAT_CLIENTSERVER - static void -remote_common(typval_T *argvars, typval_T *rettv, int expr) -{ - char_u *server_name; - char_u *keys; - char_u *r = NULL; - char_u buf[NUMBUFLEN]; -# ifdef WIN32 - HWND w; -# else - Window w; -# endif - - if (check_restricted() || check_secure()) - return; - -# ifdef FEAT_X11 - if (check_connection() == FAIL) - return; -# endif - - server_name = get_tv_string_chk(&argvars[0]); - if (server_name == NULL) - return; /* type error; errmsg already given */ - keys = get_tv_string_buf(&argvars[1], buf); -# ifdef WIN32 - if (serverSendToVim(server_name, keys, &r, &w, expr, TRUE) < 0) -# else - if (serverSendToVim(X_DISPLAY, server_name, keys, &r, &w, expr, 0, TRUE) - < 0) -# endif - { - if (r != NULL) - EMSG(r); /* sending worked but evaluation failed */ - else - EMSG2(_("E241: Unable to send to %s"), server_name); - return; - } - - rettv->vval.v_string = r; - - if (argvars[2].v_type != VAR_UNKNOWN) - { - dictitem_T v; - char_u str[30]; - char_u *idvar; - - sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w); - v.di_tv.v_type = VAR_STRING; - v.di_tv.vval.v_string = vim_strsave(str); - idvar = get_tv_string_chk(&argvars[2]); - if (idvar != NULL) - set_var(idvar, &v.di_tv, FALSE); - vim_free(v.di_tv.vval.v_string); - } -} -#endif - -/* - * "remote_expr()" function - */ - static void -f_remote_expr(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; -#ifdef FEAT_CLIENTSERVER - remote_common(argvars, rettv, TRUE); -#endif -} - -/* - * "remote_foreground()" function - */ - static void -f_remote_foreground(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_CLIENTSERVER -# ifdef WIN32 - /* On Win32 it's done in this application. */ - { - char_u *server_name = get_tv_string_chk(&argvars[0]); - - if (server_name != NULL) - serverForeground(server_name); - } -# else - /* Send a foreground() expression to the server. */ - argvars[1].v_type = VAR_STRING; - argvars[1].vval.v_string = vim_strsave((char_u *)"foreground()"); - argvars[2].v_type = VAR_UNKNOWN; - remote_common(argvars, rettv, TRUE); - vim_free(argvars[1].vval.v_string); -# endif -#endif -} - - static void -f_remote_peek(typval_T *argvars UNUSED, typval_T *rettv) -{ -#ifdef FEAT_CLIENTSERVER - dictitem_T v; - char_u *s = NULL; -# ifdef WIN32 - long_u n = 0; -# endif - char_u *serverid; - - if (check_restricted() || check_secure()) - { - rettv->vval.v_number = -1; - return; - } - serverid = get_tv_string_chk(&argvars[0]); - if (serverid == NULL) - { - rettv->vval.v_number = -1; - return; /* type error; errmsg already given */ - } -# ifdef WIN32 - sscanf((const char *)serverid, SCANF_HEX_LONG_U, &n); - if (n == 0) - rettv->vval.v_number = -1; - else - { - s = serverGetReply((HWND)n, FALSE, FALSE, FALSE); - rettv->vval.v_number = (s != NULL); - } -# else - if (check_connection() == FAIL) - return; - - rettv->vval.v_number = serverPeekReply(X_DISPLAY, - serverStrToWin(serverid), &s); -# endif - - if (argvars[1].v_type != VAR_UNKNOWN && rettv->vval.v_number > 0) - { - char_u *retvar; - - v.di_tv.v_type = VAR_STRING; - v.di_tv.vval.v_string = vim_strsave(s); - retvar = get_tv_string_chk(&argvars[1]); - if (retvar != NULL) - set_var(retvar, &v.di_tv, FALSE); - vim_free(v.di_tv.vval.v_string); - } -#else - rettv->vval.v_number = -1; -#endif -} - - static void -f_remote_read(typval_T *argvars UNUSED, typval_T *rettv) -{ - char_u *r = NULL; - -#ifdef FEAT_CLIENTSERVER - char_u *serverid = get_tv_string_chk(&argvars[0]); - - if (serverid != NULL && !check_restricted() && !check_secure()) - { -# ifdef WIN32 - /* The server's HWND is encoded in the 'id' parameter */ - long_u n = 0; - - sscanf((char *)serverid, SCANF_HEX_LONG_U, &n); - if (n != 0) - r = serverGetReply((HWND)n, FALSE, TRUE, TRUE); - if (r == NULL) -# else - if (check_connection() == FAIL || serverReadReply(X_DISPLAY, - serverStrToWin(serverid), &r, FALSE) < 0) -# endif - EMSG(_("E277: Unable to read a server reply")); - } -#endif - rettv->v_type = VAR_STRING; - rettv->vval.v_string = r; -} - -/* - * "remote_send()" function - */ - static void -f_remote_send(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; -#ifdef FEAT_CLIENTSERVER - remote_common(argvars, rettv, FALSE); -#endif -} - -/* - * "remove()" function - */ - static void -f_remove(typval_T *argvars, typval_T *rettv) -{ - list_T *l; - listitem_T *item, *item2; - listitem_T *li; - long idx; - long end; - char_u *key; - dict_T *d; - dictitem_T *di; - char_u *arg_errmsg = (char_u *)N_("remove() argument"); - - if (argvars[0].v_type == VAR_DICT) - { - if (argvars[2].v_type != VAR_UNKNOWN) - EMSG2(_(e_toomanyarg), "remove()"); - else if ((d = argvars[0].vval.v_dict) != NULL - && !tv_check_lock(d->dv_lock, arg_errmsg, TRUE)) - { - key = get_tv_string_chk(&argvars[1]); - if (key != NULL) - { - di = dict_find(d, key, -1); - if (di == NULL) - EMSG2(_(e_dictkey), key); - else if (!var_check_fixed(di->di_flags, arg_errmsg, TRUE) - && !var_check_ro(di->di_flags, arg_errmsg, TRUE)) - { - *rettv = di->di_tv; - init_tv(&di->di_tv); - dictitem_remove(d, di); - } - } - } - } - else if (argvars[0].v_type != VAR_LIST) - EMSG2(_(e_listdictarg), "remove()"); - else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, arg_errmsg, TRUE)) - { - int error = FALSE; - - idx = (long)get_tv_number_chk(&argvars[1], &error); - if (error) - ; /* type error: do nothing, errmsg already given */ - else if ((item = list_find(l, idx)) == NULL) - EMSGN(_(e_listidx), idx); - else - { - if (argvars[2].v_type == VAR_UNKNOWN) - { - /* Remove one item, return its value. */ - vimlist_remove(l, item, item); - *rettv = item->li_tv; - vim_free(item); - } - else - { - /* Remove range of items, return list with values. */ - end = (long)get_tv_number_chk(&argvars[2], &error); - if (error) - ; /* type error: do nothing */ - else if ((item2 = list_find(l, end)) == NULL) - EMSGN(_(e_listidx), end); - else - { - int cnt = 0; - - for (li = item; li != NULL; li = li->li_next) - { - ++cnt; - if (li == item2) - break; - } - if (li == NULL) /* didn't find "item2" after "item" */ - EMSG(_(e_invrange)); - else - { - vimlist_remove(l, item, item2); - if (rettv_list_alloc(rettv) == OK) - { - l = rettv->vval.v_list; - l->lv_first = item; - l->lv_last = item2; - item->li_prev = NULL; - item2->li_next = NULL; - l->lv_len = cnt; - } - } - } - } - } - } -} - -/* - * "rename({from}, {to})" function - */ - static void -f_rename(typval_T *argvars, typval_T *rettv) -{ - char_u buf[NUMBUFLEN]; - - if (check_restricted() || check_secure()) - rettv->vval.v_number = -1; - else - rettv->vval.v_number = vim_rename(get_tv_string(&argvars[0]), - get_tv_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; - - n = (int)get_tv_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 = get_tv_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; - } -} - -/* - * "resolve()" function - */ - static void -f_resolve(typval_T *argvars, typval_T *rettv) -{ - char_u *p; -#ifdef HAVE_READLINK - char_u *buf = NULL; -#endif - - p = get_tv_string(&argvars[0]); -#ifdef FEAT_SHORTCUT - { - char_u *v = NULL; - - v = mch_resolve_shortcut(p); - if (v != NULL) - rettv->vval.v_string = v; - else - rettv->vval.v_string = vim_strsave(p); - } -#else -# ifdef HAVE_READLINK - { - char_u *cpy; - int len; - char_u *remain = NULL; - char_u *q; - int is_relative_to_current = FALSE; - int has_trailing_pathsep = FALSE; - int limit = 100; - - p = vim_strsave(p); - - if (p[0] == '.' && (vim_ispathsep(p[1]) - || (p[1] == '.' && (vim_ispathsep(p[2]))))) - is_relative_to_current = TRUE; - - len = STRLEN(p); - if (len > 0 && after_pathsep(p, p + len)) - { - has_trailing_pathsep = TRUE; - p[len - 1] = NUL; /* the trailing slash breaks readlink() */ - } - - q = getnextcomp(p); - if (*q != NUL) - { - /* Separate the first path component in "p", and keep the - * remainder (beginning with the path separator). */ - remain = vim_strsave(q - 1); - q[-1] = NUL; - } - - buf = alloc(MAXPATHL + 1); - if (buf == NULL) - goto fail; - - for (;;) - { - for (;;) - { - len = readlink((char *)p, (char *)buf, MAXPATHL); - if (len <= 0) - break; - buf[len] = NUL; - - if (limit-- == 0) - { - vim_free(p); - vim_free(remain); - EMSG(_("E655: Too many symbolic links (cycle?)")); - rettv->vval.v_string = NULL; - goto fail; - } - - /* Ensure that the result will have a trailing path separator - * if the argument has one. */ - if (remain == NULL && has_trailing_pathsep) - add_pathsep(buf); - - /* Separate the first path component in the link value and - * concatenate the remainders. */ - q = getnextcomp(vim_ispathsep(*buf) ? buf + 1 : buf); - if (*q != NUL) - { - if (remain == NULL) - remain = vim_strsave(q - 1); - else - { - cpy = concat_str(q - 1, remain); - if (cpy != NULL) - { - vim_free(remain); - remain = cpy; - } - } - q[-1] = NUL; - } - - q = gettail(p); - if (q > p && *q == NUL) - { - /* Ignore trailing path separator. */ - q[-1] = NUL; - q = gettail(p); - } - if (q > p && !mch_isFullName(buf)) - { - /* symlink is relative to directory of argument */ - cpy = alloc((unsigned)(STRLEN(p) + STRLEN(buf) + 1)); - if (cpy != NULL) - { - STRCPY(cpy, p); - STRCPY(gettail(cpy), buf); - vim_free(p); - p = cpy; - } - } - else - { - vim_free(p); - p = vim_strsave(buf); - } - } - - if (remain == NULL) - break; - - /* Append the first path component of "remain" to "p". */ - q = getnextcomp(remain + 1); - len = q - remain - (*q != NUL); - cpy = vim_strnsave(p, STRLEN(p) + len); - if (cpy != NULL) - { - STRNCAT(cpy, remain, len); - vim_free(p); - p = cpy; - } - /* Shorten "remain". */ - if (*q != NUL) - STRMOVE(remain, q - 1); - else - { - vim_free(remain); - remain = NULL; - } - } - - /* If the result is a relative path name, make it explicitly relative to - * the current directory if and only if the argument had this form. */ - if (!vim_ispathsep(*p)) - { - if (is_relative_to_current - && *p != NUL - && !(p[0] == '.' - && (p[1] == NUL - || vim_ispathsep(p[1]) - || (p[1] == '.' - && (p[2] == NUL - || vim_ispathsep(p[2])))))) - { - /* Prepend "./". */ - cpy = concat_str((char_u *)"./", p); - if (cpy != NULL) - { - vim_free(p); - p = cpy; - } - } - else if (!is_relative_to_current) - { - /* Strip leading "./". */ - q = p; - while (q[0] == '.' && vim_ispathsep(q[1])) - q += 2; - if (q > p) - STRMOVE(p, p + 2); - } - } - - /* Ensure that the result will have no trailing path separator - * if the argument had none. But keep "/" or "//". */ - if (!has_trailing_pathsep) - { - q = p + STRLEN(p); - if (after_pathsep(p, q)) - *gettail_sep(p) = NUL; - } - - rettv->vval.v_string = p; - } -# else - rettv->vval.v_string = vim_strsave(p); -# endif -#endif - - simplify_filename(rettv->vval.v_string); - -#ifdef HAVE_READLINK -fail: - vim_free(buf); -#endif - rettv->v_type = VAR_STRING; -} - -/* - * "reverse({list})" function - */ - static void -f_reverse(typval_T *argvars, typval_T *rettv) -{ - list_T *l; - listitem_T *li, *ni; - - if (argvars[0].v_type != VAR_LIST) - EMSG2(_(e_listarg), "reverse()"); - else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, - (char_u *)N_("reverse() argument"), TRUE)) - { - li = l->lv_last; - l->lv_first = l->lv_last = NULL; - l->lv_len = 0; - while (li != NULL) - { - ni = li->li_prev; - list_append(l, li); - li = ni; - } - rettv->vval.v_list = l; - rettv->v_type = VAR_LIST; - ++l->lv_refcount; - l->lv_idx = l->lv_len - l->lv_idx - 1; - } -} - -#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 */ - -static int get_search_arg(typval_T *varp, int *flagsp); - -/* - * 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 = get_tv_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) - { - EMSG2(_(e_invarg2), 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; - proftime_T tm; -#ifdef FEAT_RELTIME - long time_limit = 0; -#endif - int options = SEARCH_KEEP; - int subpatnum; - - pat = get_tv_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 and timeout. */ - if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) - { - lnum_stop = (long)get_tv_number_chk(&argvars[2], NULL); - if (lnum_stop < 0) - goto theend; -#ifdef FEAT_RELTIME - if (argvars[3].v_type != VAR_UNKNOWN) - { - time_limit = (long)get_tv_number_chk(&argvars[3], NULL); - if (time_limit < 0) - goto theend; - } -#endif - } - -#ifdef FEAT_RELTIME - /* Set the time limit, if there is one. */ - profile_setlimit(time_limit, &tm); -#endif - - /* - * 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))) - { - EMSG2(_(e_invarg2), get_tv_string(&argvars[1])); - goto theend; - } - - pos = save_cursor = curwin->w_cursor; - subpatnum = searchit(curwin, curbuf, &pos, dir, pat, 1L, - options, RE_SEARCH, (linenr_T)lnum_stop, &tm); - 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_FLOAT - -/* - * round() is not in C90, use ceil() or floor() instead. - */ - float_T -vim_round(float_T f) -{ - return f > 0 ? floor(f + 0.5) : ceil(f - 0.5); -} - -/* - * "round({float})" function - */ - static void -f_round(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = vim_round(f); - else - rettv->vval.v_float = 0.0; -} -#endif - -/* - * "screenattr()" function - */ - static void -f_screenattr(typval_T *argvars, typval_T *rettv) -{ - int row; - int col; - int c; - - row = (int)get_tv_number_chk(&argvars[0], NULL) - 1; - col = (int)get_tv_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; - - row = (int)get_tv_number_chk(&argvars[0], NULL) - 1; - col = (int)get_tv_number_chk(&argvars[1], NULL) - 1; - if (row < 0 || row >= screen_Rows - || col < 0 || col >= screen_Columns) - c = -1; - else - { - off = LineOffset[row] + col; -#ifdef FEAT_MBYTE - if (enc_utf8 && ScreenLinesUC[off] != 0) - c = ScreenLinesUC[off]; - else -#endif - c = ScreenLines[off]; - } - rettv->vval.v_number = c; -} - -/* - * "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; -} - -/* - * "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 = 1; - int thisblock = 0; - int error = FALSE; - char_u *name; - - rettv->vval.v_number = 1; /* default: FAIL */ - - name = get_tv_string_chk(&argvars[0]); - if (argvars[1].v_type != VAR_UNKNOWN) - { - locally = (int)get_tv_number_chk(&argvars[1], &error) == 0; - if (!error && argvars[2].v_type != VAR_UNKNOWN) - thisblock = (int)get_tv_number_chk(&argvars[2], &error) != 0; - } - 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; - char_u *skip; - int save_p_ws = p_ws; - int dir; - int flags = 0; - char_u nbuf1[NUMBUFLEN]; - char_u nbuf2[NUMBUFLEN]; - char_u nbuf3[NUMBUFLEN]; - int retval = 0; /* default: FAIL */ - long lnum_stop = 0; - long time_limit = 0; - - /* Get the three pattern arguments: start, middle, end. */ - spat = get_tv_string_chk(&argvars[0]); - mpat = get_tv_string_buf_chk(&argvars[1], nbuf1); - epat = get_tv_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))) - { - EMSG2(_(e_invarg2), get_tv_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 = (char_u *)""; - else - { - skip = get_tv_string_buf_chk(&argvars[4], nbuf3); - if (argvars[5].v_type != VAR_UNKNOWN) - { - lnum_stop = (long)get_tv_number_chk(&argvars[5], NULL); - if (lnum_stop < 0) - goto theend; -#ifdef FEAT_RELTIME - if (argvars[6].v_type != VAR_UNKNOWN) - { - time_limit = (long)get_tv_number_chk(&argvars[6], NULL); - if (time_limit < 0) - goto theend; - } -#endif - } - } - if (skip == NULL) - goto theend; /* type error */ - - 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 */ - char_u *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 err; - int options = SEARCH_KEEP; - proftime_T tm; - - /* Make 'cpoptions' empty, the 'l' flag should not be used here. */ - save_cpo = p_cpo; - p_cpo = empty_option; - -#ifdef FEAT_RELTIME - /* Set the time limit, if there is one. */ - profile_setlimit(time_limit, &tm); -#endif - - /* Make two search patterns: start/end (pat2, for in nested pairs) and - * start/middle/end (pat3, for the top pair). */ - pat2 = alloc((unsigned)(STRLEN(spat) + STRLEN(epat) + 15)); - pat3 = alloc((unsigned)(STRLEN(spat) + STRLEN(mpat) + STRLEN(epat) + 23)); - if (pat2 == NULL || pat3 == NULL) - goto theend; - sprintf((char *)pat2, "\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat); - if (*mpat == NUL) - STRCPY(pat3, pat2); - else - sprintf((char *)pat3, "\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)", - spat, epat, mpat); - if (flags & SP_START) - options |= SEARCH_START; - - save_cursor = curwin->w_cursor; - pos = curwin->w_cursor; - clearpos(&firstpos); - clearpos(&foundpos); - pat = pat3; - for (;;) - { - n = searchit(curwin, curbuf, &pos, dir, pat, 1L, - options, RE_SEARCH, lnum_stop, &tm); - if (n == FAIL || (firstpos.lnum != 0 && equalpos(pos, firstpos))) - /* didn't find it or found the first match again: FAIL */ - break; - - if (firstpos.lnum == 0) - firstpos = pos; - if (equalpos(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 (*skip != NUL) - { - save_pos = curwin->w_cursor; - curwin->w_cursor = pos; - r = eval_to_bool(skip, &err, NULL, FALSE); - 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: - vim_free(pat2); - vim_free(pat3); - if (p_cpo == empty_option) - p_cpo = save_cpo; - else - /* Darn, evaluating the {skip} expression changed the value. */ - 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); -} - - static void -f_server2client(typval_T *argvars UNUSED, typval_T *rettv) -{ -#ifdef FEAT_CLIENTSERVER - char_u buf[NUMBUFLEN]; - char_u *server = get_tv_string_chk(&argvars[0]); - char_u *reply = get_tv_string_buf_chk(&argvars[1], buf); - - rettv->vval.v_number = -1; - if (server == NULL || reply == NULL) - return; - if (check_restricted() || check_secure()) - return; -# ifdef FEAT_X11 - if (check_connection() == FAIL) - return; -# endif - - if (serverSendReply(server, reply) < 0) - { - EMSG(_("E258: Unable to send to client")); - return; - } - rettv->vval.v_number = 0; -#else - rettv->vval.v_number = -1; -#endif -} - - static void -f_serverlist(typval_T *argvars UNUSED, typval_T *rettv) -{ - char_u *r = NULL; - -#ifdef FEAT_CLIENTSERVER -# ifdef WIN32 - r = serverGetVimNames(); -# else - make_connection(); - if (X_DISPLAY != NULL) - r = serverGetVimNames(X_DISPLAY); -# endif -#endif - rettv->v_type = VAR_STRING; - rettv->vval.v_string = r; -} - -/* - * "setbufvar()" function - */ - static void -f_setbufvar(typval_T *argvars, typval_T *rettv UNUSED) -{ - buf_T *buf; - char_u *varname, *bufvarname; - typval_T *varp; - char_u nbuf[NUMBUFLEN]; - - if (check_restricted() || check_secure()) - return; - (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ - varname = get_tv_string_chk(&argvars[1]); - buf = get_buf_tv(&argvars[0], FALSE); - varp = &argvars[2]; - - if (buf != NULL && varname != NULL && varp != NULL) - { - if (*varname == '&') - { - long numval; - char_u *strval; - int error = FALSE; - aco_save_T aco; - - /* set curbuf to be our buf, temporarily */ - aucmd_prepbuf(&aco, buf); - - ++varname; - numval = (long)get_tv_number_chk(varp, &error); - strval = get_tv_string_buf_chk(varp, nbuf); - if (!error && strval != NULL) - set_option_value(varname, numval, strval, OPT_LOCAL); - - /* reset notion of buffer */ - aucmd_restbuf(&aco); - } - else - { - buf_T *save_curbuf = curbuf; - - bufvarname = alloc((unsigned)STRLEN(varname) + 3); - if (bufvarname != NULL) - { - curbuf = buf; - STRCPY(bufvarname, "b:"); - STRCPY(bufvarname + 2, varname); - set_var(bufvarname, varp, TRUE); - vim_free(bufvarname); - curbuf = save_curbuf; - } - } - } -} - - static void -f_setcharsearch(typval_T *argvars, typval_T *rettv UNUSED) -{ - dict_T *d; - dictitem_T *di; - char_u *csearch; - - if (argvars[0].v_type != VAR_DICT) - { - EMSG(_(e_dictreq)); - return; - } - - if ((d = argvars[0].vval.v_dict) != NULL) - { - csearch = get_dict_string(d, (char_u *)"char", FALSE); - if (csearch != NULL) - { -#ifdef FEAT_MBYTE - if (enc_utf8) - { - int pcc[MAX_MCO]; - int c = utfc_ptr2char(csearch, pcc); - - set_last_csearch(c, csearch, utfc_ptr2len(csearch)); - } - else -#endif - set_last_csearch(PTR2CHAR(csearch), - csearch, MB_PTR2LEN(csearch)); - } - - di = dict_find(d, (char_u *)"forward", -1); - if (di != NULL) - set_csearch_direction((int)get_tv_number(&di->di_tv) - ? FORWARD : BACKWARD); - - di = dict_find(d, (char_u *)"until", -1); - if (di != NULL) - set_csearch_until(!!get_tv_number(&di->di_tv)); - } -} - -/* - * "setcmdpos()" function - */ - static void -f_setcmdpos(typval_T *argvars, typval_T *rettv) -{ - int pos = (int)get_tv_number(&argvars[0]) - 1; - - if (pos >= 0) - rettv->vval.v_number = set_cmdline_pos(pos); -} - -/* - * "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; - fname = get_tv_string_chk(&argvars[0]); - if (fname == NULL) - return; - mode_str = get_tv_string_buf_chk(&argvars[1], modebuf); - if (mode_str == NULL) - return; - if (STRLEN(mode_str) != 9) - { - EMSG2(_(e_invarg2), 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; -} - -/* - * "setline()" function - */ - static void -f_setline(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum; - char_u *line = NULL; - list_T *l = NULL; - listitem_T *li = NULL; - long added = 0; - linenr_T lcount = curbuf->b_ml.ml_line_count; - - lnum = get_tv_lnum(&argvars[0]); - if (argvars[1].v_type == VAR_LIST) - { - l = argvars[1].vval.v_list; - li = l->lv_first; - } - else - line = get_tv_string_chk(&argvars[1]); - - /* default result is zero == OK */ - for (;;) - { - if (l != NULL) - { - /* list argument, get next string */ - if (li == NULL) - break; - line = get_tv_string_chk(&li->li_tv); - li = li->li_next; - } - - rettv->vval.v_number = 1; /* FAIL */ - if (line == NULL || lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) - break; - - /* When coming here from Insert mode, sync undo, so that this can be - * undone separately from what was previously inserted. */ - if (u_sync_once == 2) - { - u_sync_once = 1; /* notify that u_sync() was called */ - u_sync(TRUE); - } - - if (lnum <= curbuf->b_ml.ml_line_count) - { - /* existing line, replace it */ - if (u_savesub(lnum) == OK && ml_replace(lnum, line, TRUE) == OK) - { - changed_bytes(lnum, 0); - if (lnum == curwin->w_cursor.lnum) - check_cursor_col(); - rettv->vval.v_number = 0; /* OK */ - } - } - else if (added > 0 || u_save(lnum - 1, lnum) == OK) - { - /* lnum is one past the last line, append the line */ - ++added; - if (ml_append(lnum - 1, line, (colnr_T)0, FALSE) == OK) - rettv->vval.v_number = 0; /* OK */ - } - - if (l == NULL) /* only one string argument */ - break; - ++lnum; - } - - if (added > 0) - appended_lines_mark(lcount, added); -} - -static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, typval_T *rettv); - -/* - * Used by "setqflist()" and "setloclist()" functions - */ - static void -set_qf_ll_list( - win_T *wp UNUSED, - typval_T *list_arg UNUSED, - typval_T *action_arg UNUSED, - typval_T *rettv) -{ -#ifdef FEAT_QUICKFIX - static char *e_invact = N_("E927: Invalid action: '%s'"); - char_u *act; - int action = 0; -#endif - - rettv->vval.v_number = -1; - -#ifdef FEAT_QUICKFIX - if (list_arg->v_type != VAR_LIST) - EMSG(_(e_listreq)); - else - { - list_T *l = list_arg->vval.v_list; - - if (action_arg->v_type == VAR_STRING) - { - act = get_tv_string_chk(action_arg); - if (act == NULL) - return; /* type error; errmsg already given */ - if ((*act == 'a' || *act == 'r' || *act == ' ') && act[1] == NUL) - action = *act; - else - EMSG2(_(e_invact), act); - } - else if (action_arg->v_type == VAR_UNKNOWN) - action = ' '; - else - EMSG(_(e_stringreq)); - - if (l != NULL && action && set_errorlist(wp, l, action, - (char_u *)(wp == NULL ? "setqflist()" : "setloclist()")) == OK) - rettv->vval.v_number = 0; - } -#endif -} - -/* - * "setloclist()" function - */ - static void -f_setloclist(typval_T *argvars, typval_T *rettv) -{ - win_T *win; - - rettv->vval.v_number = -1; - - win = find_win_by_nr(&argvars[0], NULL); - if (win != NULL) - set_qf_ll_list(win, &argvars[1], &argvars[2], rettv); -} - -/* - * "setmatches()" function - */ - static void -f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_SEARCH_EXTRA - list_T *l; - listitem_T *li; - dict_T *d; - list_T *s = NULL; - - rettv->vval.v_number = -1; - if (argvars[0].v_type != VAR_LIST) - { - EMSG(_(e_listreq)); - return; - } - if ((l = argvars[0].vval.v_list) != NULL) - { - - /* To some extent make sure that we are dealing with a list from - * "getmatches()". */ - li = l->lv_first; - while (li != NULL) - { - if (li->li_tv.v_type != VAR_DICT - || (d = li->li_tv.vval.v_dict) == NULL) - { - EMSG(_(e_invarg)); - return; - } - if (!(dict_find(d, (char_u *)"group", -1) != NULL - && (dict_find(d, (char_u *)"pattern", -1) != NULL - || dict_find(d, (char_u *)"pos1", -1) != NULL) - && dict_find(d, (char_u *)"priority", -1) != NULL - && dict_find(d, (char_u *)"id", -1) != NULL)) - { - EMSG(_(e_invarg)); - return; - } - li = li->li_next; - } - - clear_matches(curwin); - li = l->lv_first; - while (li != NULL) - { - int i = 0; - char_u buf[5]; - dictitem_T *di; - char_u *group; - int priority; - int id; - char_u *conceal; - - d = li->li_tv.vval.v_dict; - if (dict_find(d, (char_u *)"pattern", -1) == NULL) - { - if (s == NULL) - { - s = list_alloc(); - if (s == NULL) - return; - } - - /* match from matchaddpos() */ - for (i = 1; i < 9; i++) - { - sprintf((char *)buf, (char *)"pos%d", i); - if ((di = dict_find(d, (char_u *)buf, -1)) != NULL) - { - if (di->di_tv.v_type != VAR_LIST) - return; - - list_append_tv(s, &di->di_tv); - s->lv_refcount++; - } - else - break; - } - } - - group = get_dict_string(d, (char_u *)"group", FALSE); - priority = (int)get_dict_number(d, (char_u *)"priority"); - id = (int)get_dict_number(d, (char_u *)"id"); - conceal = dict_find(d, (char_u *)"conceal", -1) != NULL - ? get_dict_string(d, (char_u *)"conceal", FALSE) - : NULL; - if (i == 0) - { - match_add(curwin, group, - get_dict_string(d, (char_u *)"pattern", FALSE), - priority, id, NULL, conceal); - } - else - { - match_add(curwin, group, NULL, priority, id, s, conceal); - list_unref(s); - s = NULL; - } - - li = li->li_next; - } - rettv->vval.v_number = 0; - } -#endif -} - -/* - * "setpos()" function - */ - static void -f_setpos(typval_T *argvars, typval_T *rettv) -{ - pos_T pos; - int fnum; - char_u *name; - colnr_T curswant = -1; - - rettv->vval.v_number = -1; - name = get_tv_string_chk(argvars); - if (name != NULL) - { - if (list2fpos(&argvars[1], &pos, &fnum, &curswant) == OK) - { - if (--pos.col < 0) - pos.col = 0; - if (name[0] == '.' && name[1] == NUL) - { - /* set cursor */ - if (fnum == curbuf->b_fnum) - { - 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 - EMSG(_(e_invarg)); - } - 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_invarg)); - } - } -} - -/* - * "setqflist()" function - */ - static void -f_setqflist(typval_T *argvars, typval_T *rettv) -{ - set_qf_ll_list(NULL, &argvars[0], &argvars[1], rettv); -} - -/* - * "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; - - block_len = -1; - yank_type = MAUTO; - append = FALSE; - - strregname = get_tv_string_chk(argvars); - rettv->vval.v_number = 1; /* FAIL is default */ - - if (strregname == NULL) - return; /* type error; errmsg already given */ - regname = *strregname; - if (regname == 0 || regname == '@') - regname = '"'; - - if (argvars[2].v_type != VAR_UNKNOWN) - { - stropt = get_tv_string_chk(&argvars[2]); - if (stropt == NULL) - return; /* type error */ - for (; *stropt != NUL; ++stropt) - switch (*stropt) - { - case 'a': case 'A': /* append */ - append = TRUE; - break; - 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; - } - } - - if (argvars[1].v_type == VAR_LIST) - { - char_u **lstval; - char_u **allocval; - char_u buf[NUMBUFLEN]; - char_u **curval; - char_u **curallocval; - list_T *ll = argvars[1].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 = (char_u **)alloc(sizeof(char_u *) * ((len + 1) * 2)); - if (lstval == NULL) - return; - curval = lstval; - allocval = lstval + len + 2; - curallocval = allocval; - - for (li = ll == NULL ? NULL : ll->lv_first; li != NULL; - li = li->li_next) - { - strval = get_tv_string_buf_chk(&li->li_tv, buf); - if (strval == NULL) - goto free_lstval; - if (strval == buf) - { - /* Need to make a copy, next get_tv_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 - { - strval = get_tv_string_chk(&argvars[1]); - if (strval == NULL) - return; - write_reg_contents_ex(regname, strval, -1, - append, yank_type, block_len); - } - rettv->vval.v_number = 0; -} - -/* - * "settabvar()" function - */ - static void -f_settabvar(typval_T *argvars, typval_T *rettv) -{ -#ifdef FEAT_WINDOWS - tabpage_T *save_curtab; - tabpage_T *tp; -#endif - char_u *varname, *tabvarname; - typval_T *varp; - - rettv->vval.v_number = 0; - - if (check_restricted() || check_secure()) - return; - -#ifdef FEAT_WINDOWS - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); -#endif - varname = get_tv_string_chk(&argvars[1]); - varp = &argvars[2]; - - if (varname != NULL && varp != NULL -#ifdef FEAT_WINDOWS - && tp != NULL -#endif - ) - { -#ifdef FEAT_WINDOWS - save_curtab = curtab; - goto_tabpage_tp(tp, FALSE, FALSE); -#endif - - tabvarname = alloc((unsigned)STRLEN(varname) + 3); - if (tabvarname != NULL) - { - STRCPY(tabvarname, "t:"); - STRCPY(tabvarname + 2, varname); - set_var(tabvarname, varp, TRUE); - vim_free(tabvarname); - } - -#ifdef FEAT_WINDOWS - /* Restore current tabpage */ - if (valid_tabpage(save_curtab)) - goto_tabpage_tp(save_curtab, FALSE, FALSE); -#endif - } -} - -/* - * "settabwinvar()" function - */ - static void -f_settabwinvar(typval_T *argvars, typval_T *rettv) -{ - setwinvar(argvars, rettv, 1); -} - -/* - * "setwinvar()" function - */ - static void -f_setwinvar(typval_T *argvars, typval_T *rettv) -{ - setwinvar(argvars, rettv, 0); -} - -/* - * "setwinvar()" and "settabwinvar()" functions - */ - - static void -setwinvar(typval_T *argvars, typval_T *rettv UNUSED, int off) -{ - win_T *win; -#ifdef FEAT_WINDOWS - win_T *save_curwin; - tabpage_T *save_curtab; - int need_switch_win; -#endif - char_u *varname, *winvarname; - typval_T *varp; - char_u nbuf[NUMBUFLEN]; - tabpage_T *tp = NULL; - - if (check_restricted() || check_secure()) - return; - -#ifdef FEAT_WINDOWS - if (off == 1) - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - else - tp = curtab; -#endif - win = find_win_by_nr(&argvars[off], tp); - varname = get_tv_string_chk(&argvars[off + 1]); - varp = &argvars[off + 2]; - - if (win != NULL && varname != NULL && varp != NULL) - { -#ifdef FEAT_WINDOWS - need_switch_win = !(tp == curtab && win == curwin); - if (!need_switch_win - || switch_win(&save_curwin, &save_curtab, win, tp, TRUE) == OK) -#endif - { - if (*varname == '&') - { - long numval; - char_u *strval; - int error = FALSE; - - ++varname; - numval = (long)get_tv_number_chk(varp, &error); - strval = get_tv_string_buf_chk(varp, nbuf); - if (!error && strval != NULL) - set_option_value(varname, numval, strval, OPT_LOCAL); - } - else - { - winvarname = alloc((unsigned)STRLEN(varname) + 3); - if (winvarname != NULL) - { - STRCPY(winvarname, "w:"); - STRCPY(winvarname + 2, varname); - set_var(winvarname, varp, TRUE); - vim_free(winvarname); - } - } - } -#ifdef FEAT_WINDOWS - if (need_switch_win) - restore_win(save_curwin, save_curtab, TRUE); -#endif - } -} - -#ifdef FEAT_CRYPT -/* - * "sha256({string})" function - */ - static void -f_sha256(typval_T *argvars, typval_T *rettv) -{ - char_u *p; - - p = get_tv_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) -{ - rettv->vval.v_string = vim_strsave_shellescape( - get_tv_string(&argvars[0]), non_zero_arg(&argvars[1]), TRUE); - rettv->v_type = VAR_STRING; -} - -/* - * shiftwidth() function - */ - static void -f_shiftwidth(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->vval.v_number = get_sw_value(curbuf); -} - -/* - * "simplify()" function - */ - static void -f_simplify(typval_T *argvars, typval_T *rettv) -{ - char_u *p; - - p = get_tv_string(&argvars[0]); - rettv->vval.v_string = vim_strsave(p); - simplify_filename(rettv->vval.v_string); /* simplify in place */ - rettv->v_type = VAR_STRING; -} - -#ifdef FEAT_FLOAT -/* - * "sin()" function - */ - static void -f_sin(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = sin(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "sinh()" function - */ - static void -f_sinh(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = sinh(f); - else - rettv->vval.v_float = 0.0; -} -#endif - -static int -#ifdef __BORLANDC__ - _RTLENTRYF -#endif - item_compare(const void *s1, const void *s2); -static int -#ifdef __BORLANDC__ - _RTLENTRYF -#endif - item_compare2(const void *s1, const void *s2); - -/* struct used in the array that's given to qsort() */ -typedef struct -{ - listitem_T *item; - int idx; -} sortItem_T; - -/* struct storing information about current sort */ -typedef struct -{ - int item_compare_ic; - int item_compare_numeric; - int item_compare_numbers; -#ifdef FEAT_FLOAT - int item_compare_float; -#endif - char_u *item_compare_func; - partial_T *item_compare_partial; - dict_T *item_compare_selfdict; - int item_compare_func_err; - int item_compare_keep_zero; -} sortinfo_T; -static sortinfo_T *sortinfo = NULL; -static void do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort); -#define ITEM_COMPARE_FAIL 999 - -/* - * Compare functions for f_sort() and f_uniq() below. - */ - static int -#ifdef __BORLANDC__ -_RTLENTRYF -#endif -item_compare(const void *s1, const void *s2) -{ - sortItem_T *si1, *si2; - typval_T *tv1, *tv2; - char_u *p1, *p2; - char_u *tofree1 = NULL, *tofree2 = NULL; - int res; - char_u numbuf1[NUMBUFLEN]; - char_u numbuf2[NUMBUFLEN]; - - si1 = (sortItem_T *)s1; - si2 = (sortItem_T *)s2; - tv1 = &si1->item->li_tv; - tv2 = &si2->item->li_tv; - - if (sortinfo->item_compare_numbers) - { - varnumber_T v1 = get_tv_number(tv1); - varnumber_T v2 = get_tv_number(tv2); - - return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; - } - -#ifdef FEAT_FLOAT - if (sortinfo->item_compare_float) - { - float_T v1 = get_tv_float(tv1); - float_T v2 = get_tv_float(tv2); - - return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; - } -#endif - - /* tv2string() puts quotes around a string and allocates memory. Don't do - * that for string variables. Use a single quote when comparing with a - * non-string to do what the docs promise. */ - if (tv1->v_type == VAR_STRING) - { - if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) - p1 = (char_u *)"'"; - else - p1 = tv1->vval.v_string; - } - else - p1 = tv2string(tv1, &tofree1, numbuf1, 0); - if (tv2->v_type == VAR_STRING) - { - if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) - p2 = (char_u *)"'"; - else - p2 = tv2->vval.v_string; - } - else - p2 = tv2string(tv2, &tofree2, numbuf2, 0); - if (p1 == NULL) - p1 = (char_u *)""; - if (p2 == NULL) - p2 = (char_u *)""; - if (!sortinfo->item_compare_numeric) - { - if (sortinfo->item_compare_ic) - res = STRICMP(p1, p2); - else - res = STRCMP(p1, p2); - } - else - { - double n1, n2; - n1 = strtod((char *)p1, (char **)&p1); - n2 = strtod((char *)p2, (char **)&p2); - res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; - } - - /* When the result would be zero, compare the item indexes. Makes the - * sort stable. */ - if (res == 0 && !sortinfo->item_compare_keep_zero) - res = si1->idx > si2->idx ? 1 : -1; - - vim_free(tofree1); - vim_free(tofree2); - return res; -} - - static int -#ifdef __BORLANDC__ -_RTLENTRYF -#endif -item_compare2(const void *s1, const void *s2) -{ - sortItem_T *si1, *si2; - int res; - typval_T rettv; - typval_T argv[3]; - int dummy; - char_u *func_name; - partial_T *partial = sortinfo->item_compare_partial; - - /* shortcut after failure in previous call; compare all items equal */ - if (sortinfo->item_compare_func_err) - return 0; - - si1 = (sortItem_T *)s1; - si2 = (sortItem_T *)s2; - - if (partial == NULL) - func_name = sortinfo->item_compare_func; - else - func_name = partial->pt_name; - - /* Copy the values. This is needed to be able to set v_lock to VAR_FIXED - * in the copy without changing the original list items. */ - copy_tv(&si1->item->li_tv, &argv[0]); - copy_tv(&si2->item->li_tv, &argv[1]); - - rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ - res = call_func(func_name, (int)STRLEN(func_name), - &rettv, 2, argv, 0L, 0L, &dummy, TRUE, - partial, sortinfo->item_compare_selfdict); - clear_tv(&argv[0]); - clear_tv(&argv[1]); - - if (res == FAIL) - res = ITEM_COMPARE_FAIL; - else - res = (int)get_tv_number_chk(&rettv, &sortinfo->item_compare_func_err); - if (sortinfo->item_compare_func_err) - res = ITEM_COMPARE_FAIL; /* return value has wrong type */ - clear_tv(&rettv); - - /* When the result would be zero, compare the pointers themselves. Makes - * the sort stable. */ - if (res == 0 && !sortinfo->item_compare_keep_zero) - res = si1->idx > si2->idx ? 1 : -1; - - return res; -} - -/* - * "sort({list})" function - */ - static void -do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort) -{ - list_T *l; - listitem_T *li; - sortItem_T *ptrs; - sortinfo_T *old_sortinfo; - sortinfo_T info; - long len; - long i; - - /* Pointer to current info struct used in compare function. Save and - * restore the current one for nested calls. */ - old_sortinfo = sortinfo; - sortinfo = &info; - - if (argvars[0].v_type != VAR_LIST) - EMSG2(_(e_listarg), sort ? "sort()" : "uniq()"); - else - { - l = argvars[0].vval.v_list; - if (l == NULL || tv_check_lock(l->lv_lock, - (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")), - TRUE)) - goto theend; - rettv->vval.v_list = l; - rettv->v_type = VAR_LIST; - ++l->lv_refcount; - - len = list_len(l); - if (len <= 1) - goto theend; /* short list sorts pretty quickly */ - - info.item_compare_ic = FALSE; - info.item_compare_numeric = FALSE; - info.item_compare_numbers = FALSE; -#ifdef FEAT_FLOAT - info.item_compare_float = FALSE; -#endif - info.item_compare_func = NULL; - info.item_compare_partial = NULL; - info.item_compare_selfdict = NULL; - if (argvars[1].v_type != VAR_UNKNOWN) - { - /* optional second argument: {func} */ - if (argvars[1].v_type == VAR_FUNC) - info.item_compare_func = argvars[1].vval.v_string; - else if (argvars[1].v_type == VAR_PARTIAL) - info.item_compare_partial = argvars[1].vval.v_partial; - else - { - int error = FALSE; - - i = (long)get_tv_number_chk(&argvars[1], &error); - if (error) - goto theend; /* type error; errmsg already given */ - if (i == 1) - info.item_compare_ic = TRUE; - else if (argvars[1].v_type != VAR_NUMBER) - info.item_compare_func = get_tv_string(&argvars[1]); - else if (i != 0) - { - EMSG(_(e_invarg)); - goto theend; - } - if (info.item_compare_func != NULL) - { - if (*info.item_compare_func == NUL) - { - /* empty string means default sort */ - info.item_compare_func = NULL; - } - else if (STRCMP(info.item_compare_func, "n") == 0) - { - info.item_compare_func = NULL; - info.item_compare_numeric = TRUE; - } - else if (STRCMP(info.item_compare_func, "N") == 0) - { - info.item_compare_func = NULL; - info.item_compare_numbers = TRUE; - } -#ifdef FEAT_FLOAT - else if (STRCMP(info.item_compare_func, "f") == 0) - { - info.item_compare_func = NULL; - info.item_compare_float = TRUE; - } -#endif - else if (STRCMP(info.item_compare_func, "i") == 0) - { - info.item_compare_func = NULL; - info.item_compare_ic = TRUE; - } - } - } - - if (argvars[2].v_type != VAR_UNKNOWN) - { - /* optional third argument: {dict} */ - if (argvars[2].v_type != VAR_DICT) - { - EMSG(_(e_dictreq)); - goto theend; - } - info.item_compare_selfdict = argvars[2].vval.v_dict; - } - } - - /* Make an array with each entry pointing to an item in the List. */ - ptrs = (sortItem_T *)alloc((int)(len * sizeof(sortItem_T))); - if (ptrs == NULL) - goto theend; - - i = 0; - if (sort) - { - /* sort(): ptrs will be the list to sort */ - for (li = l->lv_first; li != NULL; li = li->li_next) - { - ptrs[i].item = li; - ptrs[i].idx = i; - ++i; - } - - info.item_compare_func_err = FALSE; - info.item_compare_keep_zero = FALSE; - /* test the compare function */ - if ((info.item_compare_func != NULL - || info.item_compare_partial != NULL) - && item_compare2((void *)&ptrs[0], (void *)&ptrs[1]) - == ITEM_COMPARE_FAIL) - EMSG(_("E702: Sort compare function failed")); - else - { - /* Sort the array with item pointers. */ - qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T), - info.item_compare_func == NULL - && info.item_compare_partial == NULL - ? item_compare : item_compare2); - - if (!info.item_compare_func_err) - { - /* Clear the List and append the items in sorted order. */ - l->lv_first = l->lv_last = l->lv_idx_item = NULL; - l->lv_len = 0; - for (i = 0; i < len; ++i) - list_append(l, ptrs[i].item); - } - } - } - else - { - int (*item_compare_func_ptr)(const void *, const void *); - - /* f_uniq(): ptrs will be a stack of items to remove */ - info.item_compare_func_err = FALSE; - info.item_compare_keep_zero = TRUE; - item_compare_func_ptr = info.item_compare_func != NULL - || info.item_compare_partial != NULL - ? item_compare2 : item_compare; - - for (li = l->lv_first; li != NULL && li->li_next != NULL; - li = li->li_next) - { - if (item_compare_func_ptr((void *)&li, (void *)&li->li_next) - == 0) - ptrs[i++].item = li; - if (info.item_compare_func_err) - { - EMSG(_("E882: Uniq compare function failed")); - break; - } - } - - if (!info.item_compare_func_err) - { - while (--i >= 0) - { - li = ptrs[i].item->li_next; - ptrs[i].item->li_next = li->li_next; - if (li->li_next != NULL) - li->li_next->li_prev = ptrs[i].item; - else - l->lv_last = ptrs[i].item; - list_fix_watch(l, li); - listitem_free(li); - l->lv_len--; - } - } - } - - vim_free(ptrs); - } -theend: - sortinfo = old_sortinfo; -} - -/* - * "sort({list})" function - */ - static void -f_sort(typval_T *argvars, typval_T *rettv) -{ - do_sort_uniq(argvars, rettv, TRUE); -} - -/* - * "uniq({list})" function - */ - static void -f_uniq(typval_T *argvars, typval_T *rettv) -{ - do_sort_uniq(argvars, rettv, FALSE); -} - -/* - * "soundfold({word})" function - */ - static void -f_soundfold(typval_T *argvars, typval_T *rettv) -{ - char_u *s; - - rettv->v_type = VAR_STRING; - s = get_tv_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; - - if (rettv_list_alloc(rettv) == FAIL) - 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(); - } - else if (curwin->w_p_spell && *curbuf->b_s.b_p_spl != NUL) - { - char_u *str = get_tv_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; - } - } - } -#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; -#endif - - if (rettv_list_alloc(rettv) == FAIL) - return; - -#ifdef FEAT_SPELL - if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) - { - str = get_tv_string(&argvars[0]); - if (argvars[1].v_type != VAR_UNKNOWN) - { - maxcount = (int)get_tv_number_chk(&argvars[1], &typeerr); - if (maxcount <= 0) - return; - if (argvars[2].v_type != VAR_UNKNOWN) - { - need_capital = (int)get_tv_number_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); - } -#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; - - /* Make 'cpoptions' empty, the 'l' flag should not be used here. */ - save_cpo = p_cpo; - p_cpo = (char_u *)""; - - str = get_tv_string(&argvars[0]); - if (argvars[1].v_type != VAR_UNKNOWN) - { - pat = get_tv_string_buf_chk(&argvars[1], patbuf); - if (pat == NULL) - typeerr = TRUE; - if (argvars[2].v_type != VAR_UNKNOWN) - keepempty = (int)get_tv_number_chk(&argvars[2], &typeerr); - } - if (pat == NULL || *pat == NUL) - pat = (char_u *)"[\\x01- ]\\+"; - - if (rettv_list_alloc(rettv) == FAIL) - return; - if (typeerr) - return; - - regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); - if (regmatch.regprog != NULL) - { - regmatch.rm_ic = FALSE; - while (*str != NUL || keepempty) - { - if (*str == NUL) - match = FALSE; /* empty item at the end */ - else - match = vim_regexec_nl(®match, str, col); - if (match) - end = regmatch.startp[0]; - else - end = str + STRLEN(str); - if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0 - && *str != NUL && match && end < regmatch.endp[0])) - { - if (list_append_string(rettv->vval.v_list, str, - (int)(end - str)) == FAIL) - break; - } - if (!match) - break; - /* Advance to just after the match. */ - if (regmatch.endp[0] > str) - col = 0; - else - { - /* Don't get stuck at the same match. */ -#ifdef FEAT_MBYTE - col = (*mb_ptr2len)(regmatch.endp[0]); -#else - col = 1; -#endif - } - str = regmatch.endp[0]; - } - - vim_regfree(regmatch.regprog); - } - - p_cpo = save_cpo; -} - -#ifdef FEAT_FLOAT -/* - * "sqrt()" function - */ - static void -f_sqrt(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = sqrt(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "str2float()" function - */ - static void -f_str2float(typval_T *argvars, typval_T *rettv) -{ - char_u *p = skipwhite(get_tv_string(&argvars[0])); - - if (*p == '+') - p = skipwhite(p + 1); - (void)string2float(p, &rettv->vval.v_float); - rettv->v_type = VAR_FLOAT; -} -#endif - -/* - * "str2nr()" function - */ - static void -f_str2nr(typval_T *argvars, typval_T *rettv) -{ - int base = 10; - char_u *p; - varnumber_T n; - int what; - - if (argvars[1].v_type != VAR_UNKNOWN) - { - base = (int)get_tv_number(&argvars[1]); - if (base != 2 && base != 8 && base != 10 && base != 16) - { - EMSG(_(e_invarg)); - return; - } - } - - p = skipwhite(get_tv_string(&argvars[0])); - if (*p == '+') - p = skipwhite(p + 1); - switch (base) - { - case 2: what = STR2NR_BIN + STR2NR_FORCE; break; - case 8: what = STR2NR_OCT + STR2NR_FORCE; break; - case 16: what = STR2NR_HEX + STR2NR_FORCE; break; - default: what = 0; - } - vim_str2nr(p, NULL, NULL, what, &n, NULL, 0); - rettv->vval.v_number = n; -} - -#ifdef HAVE_STRFTIME -/* - * "strftime({format}[, {time}])" function - */ - static void -f_strftime(typval_T *argvars, typval_T *rettv) -{ - char_u result_buf[256]; - struct tm *curtime; - time_t seconds; - char_u *p; - - rettv->v_type = VAR_STRING; - - p = get_tv_string(&argvars[0]); - if (argvars[1].v_type == VAR_UNKNOWN) - seconds = time(NULL); - else - seconds = (time_t)get_tv_number(&argvars[1]); - curtime = localtime(&seconds); - /* MSVC returns NULL for an invalid value of seconds. */ - if (curtime == NULL) - rettv->vval.v_string = vim_strsave((char_u *)_("(Invalid)")); - else - { -# ifdef FEAT_MBYTE - vimconv_T conv; - char_u *enc; - - conv.vc_type = CONV_NONE; - enc = enc_locale(); - convert_setup(&conv, p_enc, enc); - if (conv.vc_type != CONV_NONE) - p = string_convert(&conv, p, NULL); -# endif - if (p != NULL) - (void)strftime((char *)result_buf, sizeof(result_buf), - (char *)p, curtime); - else - result_buf[0] = NUL; - -# ifdef FEAT_MBYTE - if (conv.vc_type != CONV_NONE) - vim_free(p); - convert_setup(&conv, enc, p_enc); - if (conv.vc_type != CONV_NONE) - rettv->vval.v_string = string_convert(&conv, result_buf, NULL); - else -# endif - rettv->vval.v_string = vim_strsave(result_buf); - -# ifdef FEAT_MBYTE - /* Release conversion descriptors */ - convert_setup(&conv, NULL, NULL); - vim_free(enc); -# endif - } -} -#endif - -/* - * "strgetchar()" function - */ - static void -f_strgetchar(typval_T *argvars, typval_T *rettv) -{ - char_u *str; - int len; - int error = FALSE; - int charidx; - - rettv->vval.v_number = -1; - str = get_tv_string_chk(&argvars[0]); - if (str == NULL) - return; - len = (int)STRLEN(str); - charidx = (int)get_tv_number_chk(&argvars[1], &error); - if (error) - return; -#ifdef FEAT_MBYTE - { - int byteidx = 0; - - while (charidx >= 0 && byteidx < len) - { - if (charidx == 0) - { - rettv->vval.v_number = mb_ptr2char(str + byteidx); - break; - } - --charidx; - byteidx += mb_cptr2len(str + byteidx); - } - } -#else - if (charidx < len) - rettv->vval.v_number = str[charidx]; -#endif -} - -/* - * "stridx()" function - */ - static void -f_stridx(typval_T *argvars, typval_T *rettv) -{ - char_u buf[NUMBUFLEN]; - char_u *needle; - char_u *haystack; - char_u *save_haystack; - char_u *pos; - int start_idx; - - needle = get_tv_string_chk(&argvars[1]); - save_haystack = haystack = get_tv_string_buf_chk(&argvars[0], buf); - rettv->vval.v_number = -1; - if (needle == NULL || haystack == NULL) - return; /* type error; errmsg already given */ - - if (argvars[2].v_type != VAR_UNKNOWN) - { - int error = FALSE; - - start_idx = (int)get_tv_number_chk(&argvars[2], &error); - if (error || start_idx >= (int)STRLEN(haystack)) - return; - if (start_idx >= 0) - haystack += start_idx; - } - - pos = (char_u *)strstr((char *)haystack, (char *)needle); - if (pos != NULL) - rettv->vval.v_number = (varnumber_T)(pos - save_haystack); -} - -/* - * "string()" function - */ - static void -f_string(typval_T *argvars, typval_T *rettv) -{ - char_u *tofree; - char_u numbuf[NUMBUFLEN]; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = tv2string(&argvars[0], &tofree, numbuf, - get_copyID()); - /* Make a copy if we have a value but it's not in allocated memory. */ - if (rettv->vval.v_string != NULL && tofree == NULL) - rettv->vval.v_string = vim_strsave(rettv->vval.v_string); -} - -/* - * "strlen()" function - */ - static void -f_strlen(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = (varnumber_T)(STRLEN( - get_tv_string(&argvars[0]))); -} - -/* - * "strchars()" function - */ - static void -f_strchars(typval_T *argvars, typval_T *rettv) -{ - char_u *s = get_tv_string(&argvars[0]); - int skipcc = 0; -#ifdef FEAT_MBYTE - varnumber_T len = 0; - int (*func_mb_ptr2char_adv)(char_u **pp); -#endif - - if (argvars[1].v_type != VAR_UNKNOWN) - skipcc = (int)get_tv_number_chk(&argvars[1], NULL); - if (skipcc < 0 || skipcc > 1) - EMSG(_(e_invarg)); - else - { -#ifdef FEAT_MBYTE - func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv; - while (*s != NUL) - { - func_mb_ptr2char_adv(&s); - ++len; - } - rettv->vval.v_number = len; -#else - rettv->vval.v_number = (varnumber_T)(STRLEN(s)); -#endif - } -} - -/* - * "strdisplaywidth()" function - */ - static void -f_strdisplaywidth(typval_T *argvars, typval_T *rettv) -{ - char_u *s = get_tv_string(&argvars[0]); - int col = 0; - - if (argvars[1].v_type != VAR_UNKNOWN) - col = (int)get_tv_number(&argvars[1]); - - rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, s) - col); -} - -/* - * "strwidth()" function - */ - static void -f_strwidth(typval_T *argvars, typval_T *rettv) -{ - char_u *s = get_tv_string(&argvars[0]); - - rettv->vval.v_number = (varnumber_T)( -#ifdef FEAT_MBYTE - mb_string2cells(s, -1) -#else - STRLEN(s) -#endif - ); -} - -/* - * "strcharpart()" function - */ - static void -f_strcharpart(typval_T *argvars, typval_T *rettv) -{ -#ifdef FEAT_MBYTE - char_u *p; - int nchar; - int nbyte = 0; - int charlen; - int len = 0; - int slen; - int error = FALSE; - - p = get_tv_string(&argvars[0]); - slen = (int)STRLEN(p); - - nchar = (int)get_tv_number_chk(&argvars[1], &error); - if (!error) - { - if (nchar > 0) - while (nchar > 0 && nbyte < slen) - { - nbyte += mb_cptr2len(p + nbyte); - --nchar; - } - else - nbyte = nchar; - if (argvars[2].v_type != VAR_UNKNOWN) - { - charlen = (int)get_tv_number(&argvars[2]); - while (charlen > 0 && nbyte + len < slen) - { - int off = nbyte + len; - - if (off < 0) - len += 1; - else - len += mb_cptr2len(p + off); - --charlen; - } - } - else - len = slen - nbyte; /* default: all bytes that are available. */ - } - - /* - * Only return the overlap between the specified part and the actual - * string. - */ - if (nbyte < 0) - { - len += nbyte; - nbyte = 0; - } - else if (nbyte > slen) - nbyte = slen; - if (len < 0) - len = 0; - else if (nbyte + len > slen) - len = slen - nbyte; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strnsave(p + nbyte, len); -#else - f_strpart(argvars, rettv); -#endif -} - -/* - * "strpart()" function - */ - static void -f_strpart(typval_T *argvars, typval_T *rettv) -{ - char_u *p; - int n; - int len; - int slen; - int error = FALSE; - - p = get_tv_string(&argvars[0]); - slen = (int)STRLEN(p); - - n = (int)get_tv_number_chk(&argvars[1], &error); - if (error) - len = 0; - else if (argvars[2].v_type != VAR_UNKNOWN) - len = (int)get_tv_number(&argvars[2]); - else - len = slen - n; /* default len: all bytes that are available. */ - - /* - * Only return the overlap between the specified part and the actual - * string. - */ - if (n < 0) - { - len += n; - n = 0; - } - else if (n > slen) - n = slen; - if (len < 0) - len = 0; - else if (n + len > slen) - len = slen - n; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strnsave(p + n, len); -} - -/* - * "strridx()" function - */ - static void -f_strridx(typval_T *argvars, typval_T *rettv) -{ - char_u buf[NUMBUFLEN]; - char_u *needle; - char_u *haystack; - char_u *rest; - char_u *lastmatch = NULL; - int haystack_len, end_idx; - - needle = get_tv_string_chk(&argvars[1]); - haystack = get_tv_string_buf_chk(&argvars[0], buf); - - rettv->vval.v_number = -1; - if (needle == NULL || haystack == NULL) - return; /* type error; errmsg already given */ - - haystack_len = (int)STRLEN(haystack); - if (argvars[2].v_type != VAR_UNKNOWN) - { - /* Third argument: upper limit for index */ - end_idx = (int)get_tv_number_chk(&argvars[2], NULL); - if (end_idx < 0) - return; /* can never find a match */ - } - else - end_idx = haystack_len; - - if (*needle == NUL) - { - /* Empty string matches past the end. */ - lastmatch = haystack + end_idx; - } - else - { - for (rest = haystack; *rest != '\0'; ++rest) - { - rest = (char_u *)strstr((char *)rest, (char *)needle); - if (rest == NULL || rest > haystack + end_idx) - break; - lastmatch = rest; - } - } - - if (lastmatch == NULL) - rettv->vval.v_number = -1; - else - rettv->vval.v_number = (varnumber_T)(lastmatch - haystack); -} - -/* - * "strtrans()" function - */ - static void -f_strtrans(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = transstr(get_tv_string(&argvars[0])); -} - -/* - * "submatch()" function - */ - static void -f_submatch(typval_T *argvars, typval_T *rettv) -{ - int error = FALSE; - int no; - int retList = 0; - - no = (int)get_tv_number_chk(&argvars[0], &error); - if (error) - return; - error = FALSE; - if (argvars[1].v_type != VAR_UNKNOWN) - retList = (int)get_tv_number_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 = get_tv_string_chk(&argvars[0]); - char_u *pat = get_tv_string_buf_chk(&argvars[1], patbuf); - char_u *sub = get_tv_string_buf_chk(&argvars[2], subbuf); - char_u *flg = get_tv_string_buf_chk(&argvars[3], flagsbuf); - - rettv->v_type = VAR_STRING; - if (str == NULL || pat == NULL || sub == NULL || flg == NULL) - rettv->vval.v_string = NULL; - else - rettv->vval.v_string = do_string_sub(str, pat, sub, flg); -} - -/* - * "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; - - lnum = get_tv_lnum(argvars); /* -1 on type error */ - col = (linenr_T)get_tv_number(&argvars[1]) - 1; /* -1 on type error */ - trans = (int)get_tv_number_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, (colnr_T)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; - - id = (int)get_tv_number(&argvars[0]); - what = get_tv_string(&argvars[1]); - if (argvars[2].v_type != VAR_UNKNOWN) - { - mode = get_tv_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(NULL, id - 1); - 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); - else /* standout */ - p = highlight_has_attr(id, HL_STANDOUT, modec); - break; - - case 'u': - 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 - id = (int)get_tv_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->v_type = VAR_LIST; - rettv->vval.v_list = NULL; - -#if defined(FEAT_SYN_HL) && defined(FEAT_CONCEAL) - lnum = get_tv_lnum(argvars); /* -1 on type error */ - col = (colnr_T)get_tv_number(&argvars[1]) - 1; /* -1 on type error */ - - vim_memset(str, NUL, sizeof(str)); - - if (rettv_list_alloc(rettv) != FAIL) - { - 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 && lcs_conceal != NUL) - cchar = lcs_conceal; - if (cchar != NUL) - { -# ifdef FEAT_MBYTE - if (has_mbyte) - (*mb_char2bytes)(cchar, str); - else -# endif - 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->v_type = VAR_LIST; - rettv->vval.v_list = NULL; - -#ifdef FEAT_SYN_HL - lnum = get_tv_lnum(argvars); /* -1 on type error */ - col = (colnr_T)get_tv_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) != FAIL) - { - (void)syn_get_id(curwin, lnum, (colnr_T)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 -} - - static void -get_cmd_output_as_rettv( - typval_T *argvars, - typval_T *rettv, - int retlist) -{ - char_u *res = NULL; - char_u *p; - char_u *infile = NULL; - char_u buf[NUMBUFLEN]; - int err = FALSE; - FILE *fd; - list_T *list = NULL; - int flags = SHELL_SILENT; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - if (check_restricted() || check_secure()) - goto errret; - - if (argvars[1].v_type != VAR_UNKNOWN) - { - /* - * Write the string to a temp file, to be used for input of the shell - * command. - */ - if ((infile = vim_tempname('i', TRUE)) == NULL) - { - EMSG(_(e_notmp)); - goto errret; - } - - fd = mch_fopen((char *)infile, WRITEBIN); - if (fd == NULL) - { - EMSG2(_(e_notopen), infile); - goto errret; - } - if (argvars[1].v_type == VAR_LIST) - { - if (write_list(fd, argvars[1].vval.v_list, TRUE) == FAIL) - err = TRUE; - } - else - { - size_t len; - - p = get_tv_string_buf_chk(&argvars[1], buf); - if (p == NULL) - { - fclose(fd); - goto errret; /* type error; errmsg already given */ - } - len = STRLEN(p); - if (len > 0 && fwrite(p, len, 1, fd) != 1) - err = TRUE; - } - if (fclose(fd) != 0) - err = TRUE; - if (err) - { - EMSG(_("E677: Error writing temp file")); - goto errret; - } - } - - /* Omit SHELL_COOKED when invoked with ":silent". Avoids that the shell - * echoes typeahead, that messes up the display. */ - if (!msg_silent) - flags += SHELL_COOKED; - - if (retlist) - { - int len; - listitem_T *li; - char_u *s = NULL; - char_u *start; - char_u *end; - int i; - - res = get_cmd_output(get_tv_string(&argvars[0]), infile, flags, &len); - if (res == NULL) - goto errret; - - list = list_alloc(); - if (list == NULL) - goto errret; - - for (i = 0; i < len; ++i) - { - start = res + i; - while (i < len && res[i] != NL) - ++i; - end = res + i; - - s = alloc((unsigned)(end - start + 1)); - if (s == NULL) - goto errret; - - for (p = s; start < end; ++p, ++start) - *p = *start == NUL ? NL : *start; - *p = NUL; - - li = listitem_alloc(); - if (li == NULL) - { - vim_free(s); - goto errret; - } - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = s; - list_append(list, li); - } - - ++list->lv_refcount; - rettv->v_type = VAR_LIST; - rettv->vval.v_list = list; - list = NULL; - } - else - { - res = get_cmd_output(get_tv_string(&argvars[0]), infile, flags, NULL); -#ifdef USE_CR - /* translate into */ - if (res != NULL) - { - char_u *s; - - for (s = res; *s; ++s) - { - if (*s == CAR) - *s = NL; - } - } -#else -# ifdef USE_CRNL - /* translate into */ - if (res != NULL) - { - char_u *s, *d; - - d = res; - for (s = res; *s; ++s) - { - if (s[0] == CAR && s[1] == NL) - ++s; - *d++ = *s; - } - *d = NUL; - } -# endif -#endif - rettv->vval.v_string = res; - res = NULL; - } - -errret: - if (infile != NULL) - { - mch_remove(infile); - vim_free(infile); - } - if (res != NULL) - vim_free(res); - if (list != NULL) - list_free(list); -} - -/* - * "system()" function - */ - static void -f_system(typval_T *argvars, typval_T *rettv) -{ - get_cmd_output_as_rettv(argvars, rettv, FALSE); -} - -/* - * "systemlist()" function - */ - static void -f_systemlist(typval_T *argvars, typval_T *rettv) -{ - get_cmd_output_as_rettv(argvars, rettv, TRUE); -} - -/* - * "tabpagebuflist()" function - */ - static void -f_tabpagebuflist(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_WINDOWS - tabpage_T *tp; - win_T *wp = NULL; - - if (argvars[0].v_type == VAR_UNKNOWN) - wp = firstwin; - else - { - tp = find_tabpage((int)get_tv_number(&argvars[0])); - if (tp != NULL) - wp = (tp == curtab) ? firstwin : tp->tp_firstwin; - } - if (wp != NULL && rettv_list_alloc(rettv) != FAIL) - { - for (; wp != NULL; wp = wp->w_next) - if (list_append_number(rettv->vval.v_list, - wp->w_buffer->b_fnum) == FAIL) - break; - } -#endif -} - - -/* - * "tabpagenr()" function - */ - static void -f_tabpagenr(typval_T *argvars UNUSED, typval_T *rettv) -{ - int nr = 1; -#ifdef FEAT_WINDOWS - char_u *arg; - - if (argvars[0].v_type != VAR_UNKNOWN) - { - arg = get_tv_string_chk(&argvars[0]); - nr = 0; - if (arg != NULL) - { - if (STRCMP(arg, "$") == 0) - nr = tabpage_index(NULL) - 1; - else - EMSG2(_(e_invexpr2), arg); - } - } - else - nr = tabpage_index(curtab); -#endif - rettv->vval.v_number = nr; -} - - -#ifdef FEAT_WINDOWS -static int get_winnr(tabpage_T *tp, typval_T *argvar); - -/* - * Common code for tabpagewinnr() and winnr(). - */ - static int -get_winnr(tabpage_T *tp, typval_T *argvar) -{ - win_T *twin; - int nr = 1; - win_T *wp; - char_u *arg; - - twin = (tp == curtab) ? curwin : tp->tp_curwin; - if (argvar->v_type != VAR_UNKNOWN) - { - arg = get_tv_string_chk(argvar); - if (arg == NULL) - nr = 0; /* type error; errmsg already given */ - else if (STRCMP(arg, "$") == 0) - twin = (tp == curtab) ? lastwin : tp->tp_lastwin; - else if (STRCMP(arg, "#") == 0) - { - twin = (tp == curtab) ? prevwin : tp->tp_prevwin; - if (twin == NULL) - nr = 0; - } - else - { - EMSG2(_(e_invexpr2), arg); - nr = 0; - } - } - - if (nr > 0) - for (wp = (tp == curtab) ? firstwin : tp->tp_firstwin; - wp != twin; wp = wp->w_next) - { - if (wp == NULL) - { - /* didn't find it in this tabpage */ - nr = 0; - break; - } - ++nr; - } - return nr; -} -#endif - -/* - * "tabpagewinnr()" function - */ - static void -f_tabpagewinnr(typval_T *argvars UNUSED, typval_T *rettv) -{ - int nr = 1; -#ifdef FEAT_WINDOWS - tabpage_T *tp; - - tp = find_tabpage((int)get_tv_number(&argvars[0])); - if (tp == NULL) - nr = 0; - else - nr = get_winnr(tp, &argvars[1]); -#endif - rettv->vval.v_number = nr; -} - - -/* - * "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 *tag_pattern; - - tag_pattern = get_tv_string(&argvars[0]); - - rettv->vval.v_number = FALSE; - if (*tag_pattern == NUL) - return; - - if (rettv_list_alloc(rettv) == OK) - (void)get_tags(rettv->vval.v_list, tag_pattern); -} - -/* - * "tempname()" function - */ - static void -f_tempname(typval_T *argvars UNUSED, typval_T *rettv) -{ - static int x = 'A'; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_tempname(x, FALSE); - - /* Advance 'x' to use A-Z and 0-9, so that there are at least 34 different - * names. Skip 'I' and 'O', they are used for shell redirection. */ - do - { - if (x == 'Z') - x = '0'; - else if (x == '9') - x = 'A'; - else - { -#ifdef EBCDIC - if (x == 'I') - x = 'J'; - else if (x == 'R') - x = 'S'; - else -#endif - ++x; - } - } while (x == 'I' || x == 'O'); -} - -#ifdef FEAT_FLOAT -/* - * "tan()" function - */ - static void -f_tan(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = tan(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "tanh()" function - */ - static void -f_tanh(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = tanh(f); - else - rettv->vval.v_float = 0.0; -} -#endif - -/* - * "test_alloc_fail(id, countdown, repeat)" function - */ - static void -f_test_alloc_fail(typval_T *argvars, typval_T *rettv UNUSED) -{ - if (argvars[0].v_type != VAR_NUMBER - || argvars[0].vval.v_number <= 0 - || argvars[1].v_type != VAR_NUMBER - || argvars[1].vval.v_number < 0 - || argvars[2].v_type != VAR_NUMBER) - EMSG(_(e_invarg)); - else - { - alloc_fail_id = argvars[0].vval.v_number; - if (alloc_fail_id >= aid_last) - EMSG(_(e_invarg)); - alloc_fail_countdown = argvars[1].vval.v_number; - alloc_fail_repeat = argvars[2].vval.v_number; - did_outofmem_msg = FALSE; - } -} - -/* - * "test_autochdir()" - */ - static void -f_test_autochdir(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#if defined(FEAT_AUTOCHDIR) - test_autochdir = TRUE; -#endif -} - -/* - * "test_disable_char_avail({expr})" function - */ - static void -f_test_disable_char_avail(typval_T *argvars, typval_T *rettv UNUSED) -{ - disable_char_avail_for_testing = (int)get_tv_number(&argvars[0]); -} - -/* - * "test_garbagecollect_now()" function - */ - static void -f_test_garbagecollect_now(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ - /* This is dangerous, any Lists and Dicts used internally may be freed - * while still in use. */ - garbage_collect(TRUE); -} - -#ifdef FEAT_JOB_CHANNEL - static void -f_test_null_channel(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->v_type = VAR_CHANNEL; - rettv->vval.v_channel = NULL; -} -#endif - - static void -f_test_null_dict(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->v_type = VAR_DICT; - rettv->vval.v_dict = NULL; -} - -#ifdef FEAT_JOB_CHANNEL - static void -f_test_null_job(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->v_type = VAR_JOB; - rettv->vval.v_job = NULL; -} -#endif - - static void -f_test_null_list(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->v_type = VAR_LIST; - rettv->vval.v_list = NULL; -} - - static void -f_test_null_partial(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->v_type = VAR_PARTIAL; - rettv->vval.v_partial = NULL; -} - - static void -f_test_null_string(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; -} - - static void -f_test_settime(typval_T *argvars, typval_T *rettv UNUSED) -{ - time_for_testing = (time_t)get_tv_number(&argvars[0]); -} - -#if defined(FEAT_JOB_CHANNEL) || defined(FEAT_TIMERS) || defined(PROTO) -/* - * Get a callback from "arg". It can be a Funcref or a function name. - * When "arg" is zero return an empty string. - * Return NULL for an invalid argument. - */ - char_u * -get_callback(typval_T *arg, partial_T **pp) -{ - if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL) - { - *pp = arg->vval.v_partial; - ++(*pp)->pt_refcount; - return (*pp)->pt_name; - } - *pp = NULL; - if (arg->v_type == VAR_FUNC) - { - func_ref(arg->vval.v_string); - return arg->vval.v_string; - } - if (arg->v_type == VAR_STRING) - return arg->vval.v_string; - if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0) - return (char_u *)""; - EMSG(_("E921: Invalid callback argument")); - return NULL; -} - -/* - * Unref/free "callback" and "partial" retured by get_callback(). - */ - void -free_callback(char_u *callback, partial_T *partial) -{ - if (partial != NULL) - partial_unref(partial); - else if (callback != NULL) - { - func_unref(callback); - vim_free(callback); - } -} -#endif - -#ifdef FEAT_TIMERS -/* - * "timer_start(time, callback [, options])" function - */ - static void -f_timer_start(typval_T *argvars, typval_T *rettv) -{ - long msec = (long)get_tv_number(&argvars[0]); - timer_T *timer; - int repeat = 0; - char_u *callback; - dict_T *dict; - - if (check_secure()) - return; - if (argvars[2].v_type != VAR_UNKNOWN) - { - if (argvars[2].v_type != VAR_DICT - || (dict = argvars[2].vval.v_dict) == NULL) - { - EMSG2(_(e_invarg2), get_tv_string(&argvars[2])); - return; - } - if (dict_find(dict, (char_u *)"repeat", -1) != NULL) - repeat = get_dict_number(dict, (char_u *)"repeat"); - } - - timer = create_timer(msec, repeat); - callback = get_callback(&argvars[1], &timer->tr_partial); - if (callback == NULL) - { - stop_timer(timer); - rettv->vval.v_number = -1; - } - else - { - timer->tr_callback = vim_strsave(callback); - rettv->vval.v_number = timer->tr_id; - } -} - -/* - * "timer_stop(timer)" function - */ - static void -f_timer_stop(typval_T *argvars, typval_T *rettv UNUSED) -{ - timer_T *timer; - - if (argvars[0].v_type != VAR_NUMBER) - { - EMSG(_(e_number_exp)); - return; - } - timer = find_timer((int)get_tv_number(&argvars[0])); - if (timer != NULL) - stop_timer(timer); -} -#endif - -/* - * "tolower(string)" function - */ - static void -f_tolower(typval_T *argvars, typval_T *rettv) -{ - char_u *p; - - p = vim_strsave(get_tv_string(&argvars[0])); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = p; - - if (p != NULL) - while (*p != NUL) - { -#ifdef FEAT_MBYTE - int l; - - if (enc_utf8) - { - int c, lc; - - c = utf_ptr2char(p); - lc = utf_tolower(c); - l = utf_ptr2len(p); - /* TODO: reallocate string when byte count changes. */ - if (utf_char2len(lc) == l) - utf_char2bytes(lc, p); - p += l; - } - else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1) - p += l; /* skip multi-byte character */ - else -#endif - { - *p = TOLOWER_LOC(*p); /* note that tolower() can be a macro */ - ++p; - } - } -} - -/* - * "toupper(string)" function - */ - static void -f_toupper(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = strup_save(get_tv_string(&argvars[0])); -} - -/* - * "tr(string, fromstr, tostr)" function - */ - static void -f_tr(typval_T *argvars, typval_T *rettv) -{ - char_u *in_str; - char_u *fromstr; - char_u *tostr; - char_u *p; -#ifdef FEAT_MBYTE - int inlen; - int fromlen; - int tolen; - int idx; - char_u *cpstr; - int cplen; - int first = TRUE; -#endif - char_u buf[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - garray_T ga; - - in_str = get_tv_string(&argvars[0]); - fromstr = get_tv_string_buf_chk(&argvars[1], buf); - tostr = get_tv_string_buf_chk(&argvars[2], buf2); - - /* Default return value: empty string. */ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - if (fromstr == NULL || tostr == NULL) - return; /* type error; errmsg already given */ - ga_init2(&ga, (int)sizeof(char), 80); - -#ifdef FEAT_MBYTE - if (!has_mbyte) -#endif - /* not multi-byte: fromstr and tostr must be the same length */ - if (STRLEN(fromstr) != STRLEN(tostr)) - { -#ifdef FEAT_MBYTE -error: -#endif - EMSG2(_(e_invarg2), fromstr); - ga_clear(&ga); - return; - } - - /* fromstr and tostr have to contain the same number of chars */ - while (*in_str != NUL) - { -#ifdef FEAT_MBYTE - if (has_mbyte) - { - inlen = (*mb_ptr2len)(in_str); - cpstr = in_str; - cplen = inlen; - idx = 0; - for (p = fromstr; *p != NUL; p += fromlen) - { - fromlen = (*mb_ptr2len)(p); - if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0) - { - for (p = tostr; *p != NUL; p += tolen) - { - tolen = (*mb_ptr2len)(p); - if (idx-- == 0) - { - cplen = tolen; - cpstr = p; - break; - } - } - if (*p == NUL) /* tostr is shorter than fromstr */ - goto error; - break; - } - ++idx; - } - - if (first && cpstr == in_str) - { - /* Check that fromstr and tostr have the same number of - * (multi-byte) characters. Done only once when a character - * of in_str doesn't appear in fromstr. */ - first = FALSE; - for (p = tostr; *p != NUL; p += tolen) - { - tolen = (*mb_ptr2len)(p); - --idx; - } - if (idx != 0) - goto error; - } - - (void)ga_grow(&ga, cplen); - mch_memmove((char *)ga.ga_data + ga.ga_len, cpstr, (size_t)cplen); - ga.ga_len += cplen; - - in_str += inlen; - } - else -#endif - { - /* When not using multi-byte chars we can do it faster. */ - p = vim_strchr(fromstr, *in_str); - if (p != NULL) - ga_append(&ga, tostr[p - fromstr]); - else - ga_append(&ga, *in_str); - ++in_str; - } - } - - /* add a terminating NUL */ - (void)ga_grow(&ga, 1); - ga_append(&ga, NUL); - - rettv->vval.v_string = ga.ga_data; -} - -#ifdef FEAT_FLOAT -/* - * "trunc({float})" function - */ - static void -f_trunc(typval_T *argvars, typval_T *rettv) -{ - float_T f = 0.0; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - /* trunc() is not in C90, use floor() or ceil() instead. */ - rettv->vval.v_float = f > 0 ? floor(f) : ceil(f); - else - rettv->vval.v_float = 0.0; -} -#endif - -/* - * "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 = 0; break; - case VAR_STRING: n = 1; break; - case VAR_PARTIAL: - case VAR_FUNC: n = 2; break; - case VAR_LIST: n = 3; break; - case VAR_DICT: n = 4; break; - case VAR_FLOAT: n = 5; break; - case VAR_SPECIAL: - if (argvars[0].vval.v_number == VVAL_FALSE - || argvars[0].vval.v_number == VVAL_TRUE) - n = 6; - else - n = 7; - break; - case VAR_JOB: n = 8; break; - case VAR_CHANNEL: n = 9; break; - case VAR_UNKNOWN: - EMSG2(_(e_intern2), "f_type(UNKNOWN)"); - n = -1; - break; - } - rettv->vval.v_number = n; -} - -/* - * "undofile(name)" function - */ - static void -f_undofile(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; -#ifdef FEAT_PERSISTENT_UNDO - { - char_u *fname = get_tv_string(&argvars[0]); - - if (*fname == NUL) - { - /* If there is no file name there will be no undo file. */ - rettv->vval.v_string = NULL; - } - else - { - char_u *ffname = FullName_save(fname, FALSE); - - if (ffname != NULL) - rettv->vval.v_string = u_get_undo_file_name(ffname, FALSE); - vim_free(ffname); - } - } -#else - rettv->vval.v_string = NULL; -#endif -} - -/* - * "undotree()" function - */ - static void -f_undotree(typval_T *argvars UNUSED, typval_T *rettv) -{ - if (rettv_dict_alloc(rettv) == OK) - { - dict_T *dict = rettv->vval.v_dict; - list_T *list; - - dict_add_nr_str(dict, "synced", (long)curbuf->b_u_synced, NULL); - dict_add_nr_str(dict, "seq_last", curbuf->b_u_seq_last, NULL); - dict_add_nr_str(dict, "save_last", - (long)curbuf->b_u_save_nr_last, NULL); - dict_add_nr_str(dict, "seq_cur", curbuf->b_u_seq_cur, NULL); - dict_add_nr_str(dict, "time_cur", (long)curbuf->b_u_time_cur, NULL); - dict_add_nr_str(dict, "save_cur", (long)curbuf->b_u_save_nr_cur, NULL); - - list = list_alloc(); - if (list != NULL) - { - u_eval_tree(curbuf->b_u_oldhead, list); - dict_add_list(dict, "entries", list); - } - } -} - -/* - * "values(dict)" function - */ - static void -f_values(typval_T *argvars, typval_T *rettv) -{ - dict_list(argvars, rettv, 1); -} - -/* - * "virtcol(string)" function - */ - static void -f_virtcol(typval_T *argvars, typval_T *rettv) -{ - colnr_T vcol = 0; - pos_T *fp; - int fnum = curbuf->b_fnum; - - fp = var2fpos(&argvars[0], FALSE, &fnum); - if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count - && fnum == curbuf->b_fnum) - { - getvvcol(curwin, fp, NULL, NULL, &vcol); - ++vcol; - } - - rettv->vval.v_number = vcol; -} - -/* - * "visualmode()" function - */ - static void -f_visualmode(typval_T *argvars, typval_T *rettv) -{ - char_u str[2]; - - 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) - rettv->vval.v_number = 1; -#endif -} - -/* - * "winbufnr(nr)" function - */ - static void -f_winbufnr(typval_T *argvars, typval_T *rettv) -{ - win_T *wp; - - wp = find_win_by_nr(&argvars[0], NULL); - if (wp == NULL) - rettv->vval.v_number = -1; - else - rettv->vval.v_number = wp->w_buffer->b_fnum; -} - -/* - * "wincol()" function - */ - static void -f_wincol(typval_T *argvars UNUSED, typval_T *rettv) -{ - validate_cursor(); - rettv->vval.v_number = curwin->w_wcol + 1; -} - -/* - * "winheight(nr)" function - */ - static void -f_winheight(typval_T *argvars, typval_T *rettv) -{ - win_T *wp; - - wp = find_win_by_nr(&argvars[0], NULL); - if (wp == NULL) - rettv->vval.v_number = -1; - else - rettv->vval.v_number = wp->w_height; -} - -/* - * "winline()" function - */ - static void -f_winline(typval_T *argvars UNUSED, typval_T *rettv) -{ - validate_cursor(); - rettv->vval.v_number = curwin->w_wrow + 1; -} - -/* - * "winnr()" function - */ - static void -f_winnr(typval_T *argvars UNUSED, typval_T *rettv) -{ - int nr = 1; - -#ifdef FEAT_WINDOWS - nr = get_winnr(curtab, &argvars[0]); -#endif - rettv->vval.v_number = nr; -} - -/* - * "winrestcmd()" function - */ - static void -f_winrestcmd(typval_T *argvars UNUSED, typval_T *rettv) -{ -#ifdef FEAT_WINDOWS - win_T *wp; - int winnr = 1; - garray_T ga; - char_u buf[50]; - - ga_init2(&ga, (int)sizeof(char), 70); - for (wp = firstwin; wp != NULL; wp = wp->w_next) - { - sprintf((char *)buf, "%dresize %d|", winnr, wp->w_height); - ga_concat(&ga, buf); - sprintf((char *)buf, "vert %dresize %d|", winnr, wp->w_width); - ga_concat(&ga, buf); - ++winnr; - } - ga_append(&ga, NUL); - - rettv->vval.v_string = ga.ga_data; -#else - rettv->vval.v_string = NULL; -#endif - rettv->v_type = VAR_STRING; -} - -/* - * "winrestview()" function - */ - static void -f_winrestview(typval_T *argvars, typval_T *rettv UNUSED) -{ - dict_T *dict; - - if (argvars[0].v_type != VAR_DICT - || (dict = argvars[0].vval.v_dict) == NULL) - EMSG(_(e_invarg)); - else - { - if (dict_find(dict, (char_u *)"lnum", -1) != NULL) - curwin->w_cursor.lnum = (linenr_T)get_dict_number(dict, (char_u *)"lnum"); - if (dict_find(dict, (char_u *)"col", -1) != NULL) - curwin->w_cursor.col = (colnr_T)get_dict_number(dict, (char_u *)"col"); -#ifdef FEAT_VIRTUALEDIT - if (dict_find(dict, (char_u *)"coladd", -1) != NULL) - curwin->w_cursor.coladd = (colnr_T)get_dict_number(dict, (char_u *)"coladd"); -#endif - if (dict_find(dict, (char_u *)"curswant", -1) != NULL) - { - curwin->w_curswant = (colnr_T)get_dict_number(dict, (char_u *)"curswant"); - curwin->w_set_curswant = FALSE; - } - - if (dict_find(dict, (char_u *)"topline", -1) != NULL) - set_topline(curwin, (linenr_T)get_dict_number(dict, (char_u *)"topline")); -#ifdef FEAT_DIFF - if (dict_find(dict, (char_u *)"topfill", -1) != NULL) - curwin->w_topfill = (int)get_dict_number(dict, (char_u *)"topfill"); -#endif - if (dict_find(dict, (char_u *)"leftcol", -1) != NULL) - curwin->w_leftcol = (colnr_T)get_dict_number(dict, (char_u *)"leftcol"); - if (dict_find(dict, (char_u *)"skipcol", -1) != NULL) - curwin->w_skipcol = (colnr_T)get_dict_number(dict, (char_u *)"skipcol"); - - check_cursor(); - win_new_height(curwin, curwin->w_height); -# ifdef FEAT_WINDOWS - win_new_width(curwin, W_WIDTH(curwin)); -# endif - changed_window_setting(); - - if (curwin->w_topline <= 0) - curwin->w_topline = 1; - if (curwin->w_topline > curbuf->b_ml.ml_line_count) - curwin->w_topline = curbuf->b_ml.ml_line_count; -#ifdef FEAT_DIFF - check_topfill(curwin, TRUE); -#endif - } -} - -/* - * "winsaveview()" function - */ - static void -f_winsaveview(typval_T *argvars UNUSED, typval_T *rettv) -{ - dict_T *dict; - - if (rettv_dict_alloc(rettv) == FAIL) - return; - dict = rettv->vval.v_dict; - - dict_add_nr_str(dict, "lnum", (long)curwin->w_cursor.lnum, NULL); - dict_add_nr_str(dict, "col", (long)curwin->w_cursor.col, NULL); -#ifdef FEAT_VIRTUALEDIT - dict_add_nr_str(dict, "coladd", (long)curwin->w_cursor.coladd, NULL); -#endif - update_curswant(); - dict_add_nr_str(dict, "curswant", (long)curwin->w_curswant, NULL); - - dict_add_nr_str(dict, "topline", (long)curwin->w_topline, NULL); -#ifdef FEAT_DIFF - dict_add_nr_str(dict, "topfill", (long)curwin->w_topfill, NULL); -#endif - dict_add_nr_str(dict, "leftcol", (long)curwin->w_leftcol, NULL); - dict_add_nr_str(dict, "skipcol", (long)curwin->w_skipcol, NULL); -} - -/* - * "winwidth(nr)" function - */ - static void -f_winwidth(typval_T *argvars, typval_T *rettv) -{ - win_T *wp; - - wp = find_win_by_nr(&argvars[0], NULL); - if (wp == NULL) - rettv->vval.v_number = -1; - else -#ifdef FEAT_WINDOWS - rettv->vval.v_number = wp->w_width; -#else - rettv->vval.v_number = Columns; -#endif -} - -/* - * "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); -} - -/* - * Write list of strings to file - */ - static int -write_list(FILE *fd, list_T *list, int binary) -{ - listitem_T *li; - int c; - int ret = OK; - char_u *s; - - for (li = list->lv_first; li != NULL; li = li->li_next) - { - for (s = get_tv_string(&li->li_tv); *s != NUL; ++s) - { - if (*s == '\n') - c = putc(NUL, fd); - else - c = putc(*s, fd); - if (c == EOF) - { - ret = FAIL; - break; - } - } - if (!binary || li->li_next != NULL) - if (putc('\n', fd) == EOF) - { - ret = FAIL; - break; - } - if (ret == FAIL) - { - EMSG(_(e_write)); - break; - } - } - return ret; -} - -/* - * "writefile()" function - */ - static void -f_writefile(typval_T *argvars, typval_T *rettv) -{ - int binary = FALSE; - int append = FALSE; - char_u *fname; - FILE *fd; - int ret = 0; - - if (check_restricted() || check_secure()) - return; - - if (argvars[0].v_type != VAR_LIST) - { - EMSG2(_(e_listarg), "writefile()"); - return; - } - if (argvars[0].vval.v_list == NULL) - return; - - if (argvars[2].v_type != VAR_UNKNOWN) - { - if (vim_strchr(get_tv_string(&argvars[2]), 'b') != NULL) - binary = TRUE; - if (vim_strchr(get_tv_string(&argvars[2]), 'a') != NULL) - append = TRUE; - } - - /* Always open the file in binary mode, library functions have a mind of - * their own about CR-LF conversion. */ - fname = get_tv_string(&argvars[1]); - if (*fname == NUL || (fd = mch_fopen((char *)fname, - append ? APPENDBIN : WRITEBIN)) == NULL) - { - EMSG2(_(e_notcreate), *fname == NUL ? (char_u *)_("") : fname); - ret = -1; - } - else - { - if (write_list(fd, argvars[0].vval.v_list, binary) == FAIL) - ret = -1; - fclose(fd); - } - - rettv->vval.v_number = ret; -} - -/* - * "xor(expr, expr)" function - */ - static void -f_xor(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) - ^ get_tv_number_chk(&argvars[1], NULL); -} /* * Translate a String variable into a position. * Returns NULL when there is an error. */ - static pos_T * + pos_T * var2fpos( typval_T *varp, int dollar_lnum, /* TRUE when $ is last line */ @@ -19415,7 +6095,7 @@ var2fpos( * Return FAIL when conversion is not possible, doesn't check the position for * validity. */ - static int + int list2fpos( typval_T *arg, pos_T *posp, @@ -19531,7 +6211,7 @@ get_id_len(char_u **arg) * If the name contains 'magic' {}'s, expand them and return the * expanded name in an allocated string via 'alias' - caller must free. */ - static int + int get_name_len( char_u **arg, char_u **alias, @@ -20032,7 +6712,7 @@ set_cmdarg(exarg_T *eap, char_u *oldarg) * Get the value of internal variable "name". * Return OK or FAIL. */ - static int + int get_var_tv( char_u *name, int len, /* length of "name" */ @@ -20324,7 +7004,7 @@ clear_tv(typval_T *varp) /* * Set the value of a variable to NULL without freeing items. */ - static void + void init_tv(typval_T *varp) { if (varp != NULL) @@ -20401,7 +7081,7 @@ get_tv_number_chk(typval_T *varp, int *denote) } #ifdef FEAT_FLOAT - static float_T + float_T get_tv_float(typval_T *varp) { switch (varp->v_type) @@ -20444,44 +7124,6 @@ get_tv_float(typval_T *varp) } #endif -/* - * Get the lnum from the first argument. - * Also accepts ".", "$", etc., but that only works for the current buffer. - * Returns -1 on error. - */ - static linenr_T -get_tv_lnum(typval_T *argvars) -{ - typval_T rettv; - linenr_T lnum; - - lnum = (linenr_T)get_tv_number_chk(&argvars[0], NULL); - if (lnum == 0) /* no valid number, try using line() */ - { - rettv.v_type = VAR_NUMBER; - f_line(argvars, &rettv); - lnum = (linenr_T)rettv.vval.v_number; - clear_tv(&rettv); - } - return lnum; -} - -/* - * Get the lnum from the first argument. - * Also accepts "$", then "buf" is used. - * Returns 0 on error. - */ - static linenr_T -get_tv_lnum_buf(typval_T *argvars, buf_T *buf) -{ - if (argvars[0].v_type == VAR_STRING - && argvars[0].vval.v_string != NULL - && argvars[0].vval.v_string[0] == '$' - && buf != NULL) - return buf->b_ml.ml_line_count; - return (linenr_T)get_tv_number_chk(&argvars[0], NULL); -} - /* * Get the string value of a variable. * If it is a Number variable, the number is converted into a string. @@ -20624,7 +7266,7 @@ find_var(char_u *name, hashtab_T **htp, int no_autoload) * Find variable "varname" in hashtab "ht" with name "htname". * Returns NULL if not found. */ - static dictitem_T * + dictitem_T * find_var_in_ht( hashtab_T *ht, int htname, @@ -20936,7 +7578,7 @@ list_one_var_a( * If the variable already exists, the value is updated. * Otherwise the variable is created. */ - static void + void set_var( char_u *name, typval_T *tv, @@ -21064,7 +7706,7 @@ var_check_ro(int flags, char_u *name, int use_gettext) * Return TRUE if di_flags "flags" indicates variable "name" is fixed. * Also give an error message. */ - static int + int var_check_fixed(int flags, char_u *name, int use_gettext) { if (flags & DI_FLAGS_FIX) @@ -21309,6 +7951,110 @@ item_copy( return ret; } +/* + * This function is used by f_input() and f_inputdialog() functions. The third + * argument to f_input() specifies the type of completion to use at the + * prompt. The third argument to f_inputdialog() specifies the value to return + * when the user cancels the prompt. + */ + void +get_user_input( + typval_T *argvars, + typval_T *rettv, + int inputdialog, + int secret) +{ + char_u *prompt = get_tv_string_chk(&argvars[0]); + char_u *p = NULL; + int c; + char_u buf[NUMBUFLEN]; + int cmd_silent_save = cmd_silent; + char_u *defstr = (char_u *)""; + int xp_type = EXPAND_NOTHING; + char_u *xp_arg = NULL; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + +#ifdef NO_CONSOLE_INPUT + /* While starting up, there is no place to enter text. */ + if (no_console_input()) + return; +#endif + + cmd_silent = FALSE; /* Want to see the prompt. */ + if (prompt != NULL) + { + /* Only the part of the message after the last NL is considered as + * prompt for the command line */ + p = vim_strrchr(prompt, '\n'); + if (p == NULL) + p = prompt; + else + { + ++p; + c = *p; + *p = NUL; + msg_start(); + msg_clr_eos(); + msg_puts_attr(prompt, echo_attr); + msg_didout = FALSE; + msg_starthere(); + *p = c; + } + cmdline_row = msg_row; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + defstr = get_tv_string_buf_chk(&argvars[1], buf); + if (defstr != NULL) + stuffReadbuffSpec(defstr); + + if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) + { + char_u *xp_name; + int xp_namelen; + long argt; + + /* input() with a third argument: completion */ + rettv->vval.v_string = NULL; + + xp_name = get_tv_string_buf_chk(&argvars[2], buf); + if (xp_name == NULL) + return; + + xp_namelen = (int)STRLEN(xp_name); + + if (parse_compl_arg(xp_name, xp_namelen, &xp_type, &argt, + &xp_arg) == FAIL) + return; + } + } + + if (defstr != NULL) + { + int save_ex_normal_busy = ex_normal_busy; + ex_normal_busy = 0; + rettv->vval.v_string = + getcmdline_prompt(secret ? NUL : '@', p, echo_attr, + xp_type, xp_arg); + ex_normal_busy = save_ex_normal_busy; + } + if (inputdialog && rettv->vval.v_string == NULL + && argvars[1].v_type != VAR_UNKNOWN + && argvars[2].v_type != VAR_UNKNOWN) + rettv->vval.v_string = vim_strsave(get_tv_string_buf( + &argvars[2], buf)); + + vim_free(xp_arg); + + /* since the user typed this, no need to wait for return */ + need_wait_return = FALSE; + msg_didout = FALSE; + } + cmd_silent = cmd_silent_save; +} + /* * ":echo expr1 ..." print each argument separated with a space, add a * newline at the end. @@ -21515,6 +8261,224 @@ ex_execute(exarg_T *eap) eap->nextcmd = check_nextcmd(arg); } +/* + * Find window specified by "vp" in tabpage "tp". + */ + win_T * +find_win_by_nr( + typval_T *vp, + tabpage_T *tp UNUSED) /* NULL for current tab page */ +{ +#ifdef FEAT_WINDOWS + win_T *wp; +#endif + int nr; + + nr = (int)get_tv_number_chk(vp, NULL); + +#ifdef FEAT_WINDOWS + if (nr < 0) + return NULL; + if (nr == 0) + return curwin; + + for (wp = (tp == NULL || tp == curtab) ? firstwin : tp->tp_firstwin; + wp != NULL; wp = wp->w_next) + if (nr >= LOWEST_WIN_ID) + { + if (wp->w_id == nr) + return wp; + } + else if (--nr <= 0) + break; + if (nr >= LOWEST_WIN_ID) + return NULL; + return wp; +#else + if (nr == 0 || nr == 1 || nr == curwin->w_id) + return curwin; + return NULL; +#endif +} + +/* + * Find window specified by "wvp" in tabpage "tvp". + */ + win_T * +find_tabwin( + typval_T *wvp, /* VAR_UNKNOWN for current window */ + typval_T *tvp) /* VAR_UNKNOWN for current tab page */ +{ + win_T *wp = NULL; + tabpage_T *tp = NULL; + long n; + + if (wvp->v_type != VAR_UNKNOWN) + { + if (tvp->v_type != VAR_UNKNOWN) + { + n = (long)get_tv_number(tvp); + if (n >= 0) + tp = find_tabpage(n); + } + else + tp = curtab; + + if (tp != NULL) + wp = find_win_by_nr(wvp, tp); + } + else + wp = curwin; + + return wp; +} + +/* + * getwinvar() and gettabwinvar() + */ + void +getwinvar( + typval_T *argvars, + typval_T *rettv, + int off) /* 1 for gettabwinvar() */ +{ + win_T *win; + char_u *varname; + dictitem_T *v; + tabpage_T *tp = NULL; + int done = FALSE; +#ifdef FEAT_WINDOWS + win_T *oldcurwin; + tabpage_T *oldtabpage; + int need_switch_win; +#endif + +#ifdef FEAT_WINDOWS + if (off == 1) + tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + else + tp = curtab; +#endif + win = find_win_by_nr(&argvars[off], tp); + varname = get_tv_string_chk(&argvars[off + 1]); + ++emsg_off; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + if (win != NULL && varname != NULL) + { +#ifdef FEAT_WINDOWS + /* Set curwin to be our win, temporarily. Also set the tabpage, + * otherwise the window is not valid. Only do this when needed, + * autocommands get blocked. */ + need_switch_win = !(tp == curtab && win == curwin); + if (!need_switch_win + || switch_win(&oldcurwin, &oldtabpage, win, tp, TRUE) == OK) +#endif + { + if (*varname == '&') /* window-local-option */ + { + if (get_option_tv(&varname, rettv, 1) == OK) + done = TRUE; + } + else + { + /* Look up the variable. */ + /* Let getwinvar({nr}, "") return the "w:" dictionary. */ + v = find_var_in_ht(&win->w_vars->dv_hashtab, 'w', + varname, FALSE); + if (v != NULL) + { + copy_tv(&v->di_tv, rettv); + done = TRUE; + } + } + } + +#ifdef FEAT_WINDOWS + if (need_switch_win) + /* restore previous notion of curwin */ + restore_win(oldcurwin, oldtabpage, TRUE); +#endif + } + + if (!done && argvars[off + 2].v_type != VAR_UNKNOWN) + /* use the default return value */ + copy_tv(&argvars[off + 2], rettv); + + --emsg_off; +} + +/* + * "setwinvar()" and "settabwinvar()" functions + */ + void +setwinvar(typval_T *argvars, typval_T *rettv UNUSED, int off) +{ + win_T *win; +#ifdef FEAT_WINDOWS + win_T *save_curwin; + tabpage_T *save_curtab; + int need_switch_win; +#endif + char_u *varname, *winvarname; + typval_T *varp; + char_u nbuf[NUMBUFLEN]; + tabpage_T *tp = NULL; + + if (check_restricted() || check_secure()) + return; + +#ifdef FEAT_WINDOWS + if (off == 1) + tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + else + tp = curtab; +#endif + win = find_win_by_nr(&argvars[off], tp); + varname = get_tv_string_chk(&argvars[off + 1]); + varp = &argvars[off + 2]; + + if (win != NULL && varname != NULL && varp != NULL) + { +#ifdef FEAT_WINDOWS + need_switch_win = !(tp == curtab && win == curwin); + if (!need_switch_win + || switch_win(&save_curwin, &save_curtab, win, tp, TRUE) == OK) +#endif + { + if (*varname == '&') + { + long numval; + char_u *strval; + int error = FALSE; + + ++varname; + numval = (long)get_tv_number_chk(varp, &error); + strval = get_tv_string_buf_chk(varp, nbuf); + if (!error && strval != NULL) + set_option_value(varname, numval, strval, OPT_LOCAL); + } + else + { + winvarname = alloc((unsigned)STRLEN(varname) + 3); + if (winvarname != NULL) + { + STRCPY(winvarname, "w:"); + STRCPY(winvarname + 2, varname); + set_var(winvarname, varp, TRUE); + vim_free(winvarname); + } + } + } +#ifdef FEAT_WINDOWS + if (need_switch_win) + restore_win(save_curwin, save_curtab, TRUE); +#endif + } +} + /* * Skip over the name of an option: "&option", "&g:option" or "&l:option". * "arg" points to the "&" or '+' when called, to "option" when returning. @@ -21932,6 +8896,255 @@ reset_v_option_vars(void) set_vim_var_string(VV_OPTION_TYPE, NULL, -1); } +/* + * Prepare "gap" for an assert error and add the sourcing position. + */ + void +prepare_assert_error(garray_T *gap) +{ + char buf[NUMBUFLEN]; + + ga_init2(gap, 1, 100); + if (sourcing_name != NULL) + { + ga_concat(gap, sourcing_name); + if (sourcing_lnum > 0) + ga_concat(gap, (char_u *)" "); + } + if (sourcing_lnum > 0) + { + sprintf(buf, "line %ld", (long)sourcing_lnum); + ga_concat(gap, (char_u *)buf); + } + if (sourcing_name != NULL || sourcing_lnum > 0) + ga_concat(gap, (char_u *)": "); +} + +/* + * Add an assert error to v:errors. + */ + void +assert_error(garray_T *gap) +{ + struct vimvar *vp = &vimvars[VV_ERRORS]; + + if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL) + /* Make sure v:errors is a list. */ + set_vim_var_list(VV_ERRORS, list_alloc()); + list_append_string(vimvars[VV_ERRORS].vv_list, gap->ga_data, gap->ga_len); +} + + void +assert_equal_common(typval_T *argvars, assert_type_T atype) +{ + garray_T ga; + + if (tv_equal(&argvars[0], &argvars[1], FALSE, FALSE) + != (atype == ASSERT_EQUAL)) + { + prepare_assert_error(&ga); + fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], + atype); + assert_error(&ga); + ga_clear(&ga); + } +} + + void +assert_match_common(typval_T *argvars, assert_type_T atype) +{ + garray_T ga; + char_u buf1[NUMBUFLEN]; + char_u buf2[NUMBUFLEN]; + char_u *pat = get_tv_string_buf_chk(&argvars[0], buf1); + char_u *text = get_tv_string_buf_chk(&argvars[1], buf2); + + if (pat == NULL || text == NULL) + EMSG(_(e_invarg)); + else if (pattern_match(pat, text, FALSE) != (atype == ASSERT_MATCH)) + { + prepare_assert_error(&ga); + fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], + atype); + assert_error(&ga); + ga_clear(&ga); + } +} + +/* + * Common for assert_true() and assert_false(). + */ + void +assert_bool(typval_T *argvars, int isTrue) +{ + int error = FALSE; + garray_T ga; + + if (argvars[0].v_type == VAR_SPECIAL + && argvars[0].vval.v_number == (isTrue ? VVAL_TRUE : VVAL_FALSE)) + return; + if (argvars[0].v_type != VAR_NUMBER + || (get_tv_number_chk(&argvars[0], &error) == 0) == isTrue + || error) + { + prepare_assert_error(&ga); + fill_assert_error(&ga, &argvars[1], + (char_u *)(isTrue ? "True" : "False"), + NULL, &argvars[0], ASSERT_OTHER); + assert_error(&ga); + ga_clear(&ga); + } +} + + void +assert_exception(typval_T *argvars) +{ + garray_T ga; + char_u *error = get_tv_string_chk(&argvars[0]); + + if (vimvars[VV_EXCEPTION].vv_str == NULL) + { + prepare_assert_error(&ga); + ga_concat(&ga, (char_u *)"v:exception is not set"); + assert_error(&ga); + ga_clear(&ga); + } + else if (error != NULL + && strstr((char *)vimvars[VV_EXCEPTION].vv_str, (char *)error) == NULL) + { + prepare_assert_error(&ga); + fill_assert_error(&ga, &argvars[1], NULL, &argvars[0], + &vimvars[VV_EXCEPTION].vv_tv, ASSERT_OTHER); + assert_error(&ga); + ga_clear(&ga); + } +} + + void +assert_fails(typval_T *argvars) +{ + char_u *cmd = get_tv_string_chk(&argvars[0]); + garray_T ga; + + called_emsg = FALSE; + suppress_errthrow = TRUE; + emsg_silent = TRUE; + do_cmdline_cmd(cmd); + if (!called_emsg) + { + prepare_assert_error(&ga); + ga_concat(&ga, (char_u *)"command did not fail: "); + ga_concat(&ga, cmd); + assert_error(&ga); + ga_clear(&ga); + } + else if (argvars[1].v_type != VAR_UNKNOWN) + { + char_u buf[NUMBUFLEN]; + char *error = (char *)get_tv_string_buf_chk(&argvars[1], buf); + + if (error == NULL + || strstr((char *)vimvars[VV_ERRMSG].vv_str, error) == NULL) + { + prepare_assert_error(&ga); + fill_assert_error(&ga, &argvars[2], NULL, &argvars[1], + &vimvars[VV_ERRMSG].vv_tv, ASSERT_OTHER); + assert_error(&ga); + ga_clear(&ga); + } + } + + called_emsg = FALSE; + suppress_errthrow = FALSE; + emsg_silent = FALSE; + emsg_on_display = FALSE; + set_vim_var_string(VV_ERRMSG, NULL, 0); +} + +/* + * Append "str" to "gap", escaping unprintable characters. + * Changes NL to \n, CR to \r, etc. + */ + static void +ga_concat_esc(garray_T *gap, char_u *str) +{ + char_u *p; + char_u buf[NUMBUFLEN]; + + if (str == NULL) + { + ga_concat(gap, (char_u *)"NULL"); + return; + } + + for (p = str; *p != NUL; ++p) + switch (*p) + { + case BS: ga_concat(gap, (char_u *)"\\b"); break; + case ESC: ga_concat(gap, (char_u *)"\\e"); break; + case FF: ga_concat(gap, (char_u *)"\\f"); break; + case NL: ga_concat(gap, (char_u *)"\\n"); break; + case TAB: ga_concat(gap, (char_u *)"\\t"); break; + case CAR: ga_concat(gap, (char_u *)"\\r"); break; + case '\\': ga_concat(gap, (char_u *)"\\\\"); break; + default: + if (*p < ' ') + { + vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p); + ga_concat(gap, buf); + } + else + ga_append(gap, *p); + break; + } +} + +/* + * Fill "gap" with information about an assert error. + */ + void +fill_assert_error( + garray_T *gap, + typval_T *opt_msg_tv, + char_u *exp_str, + typval_T *exp_tv, + typval_T *got_tv, + assert_type_T atype) +{ + char_u numbuf[NUMBUFLEN]; + char_u *tofree; + + if (opt_msg_tv->v_type != VAR_UNKNOWN) + { + ga_concat(gap, tv2string(opt_msg_tv, &tofree, numbuf, 0)); + vim_free(tofree); + } + else + { + if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) + ga_concat(gap, (char_u *)"Pattern "); + else + ga_concat(gap, (char_u *)"Expected "); + if (exp_str == NULL) + { + ga_concat_esc(gap, tv2string(exp_tv, &tofree, numbuf, 0)); + vim_free(tofree); + } + else + ga_concat_esc(gap, exp_str); + if (atype == ASSERT_MATCH) + ga_concat(gap, (char_u *)" does not match "); + else if (atype == ASSERT_NOTMATCH) + ga_concat(gap, (char_u *)" does match "); + else if (atype == ASSERT_NOTEQUAL) + ga_concat(gap, (char_u *)" differs from "); + else + ga_concat(gap, (char_u *)" but got "); + ga_concat_esc(gap, tv2string(got_tv, &tofree, numbuf, 0)); + vim_free(tofree); + } +} + #endif /* FEAT_EVAL */ @@ -22678,4 +9891,192 @@ do_string_sub( return ret; } + static int +filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) +{ + typval_T rettv; + typval_T argv[3]; + char_u buf[NUMBUFLEN]; + char_u *s; + int retval = FAIL; + int dummy; + + copy_tv(tv, &vimvars[VV_VAL].vv_tv); + argv[0] = vimvars[VV_KEY].vv_tv; + argv[1] = vimvars[VV_VAL].vv_tv; + if (expr->v_type == VAR_FUNC) + { + s = expr->vval.v_string; + if (call_func(s, (int)STRLEN(s), + &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL, NULL) == FAIL) + goto theend; + } + else if (expr->v_type == VAR_PARTIAL) + { + partial_T *partial = expr->vval.v_partial; + + s = partial->pt_name; + if (call_func(s, (int)STRLEN(s), + &rettv, 2, argv, 0L, 0L, &dummy, TRUE, partial, NULL) + == FAIL) + goto theend; + } + else + { + s = get_tv_string_buf_chk(expr, buf); + if (s == NULL) + goto theend; + s = skipwhite(s); + if (eval1(&s, &rettv, TRUE) == FAIL) + goto theend; + if (*s != NUL) /* check for trailing chars after expr */ + { + EMSG2(_(e_invexpr2), s); + goto theend; + } + } + if (map) + { + /* map(): replace the list item value */ + clear_tv(tv); + rettv.v_lock = 0; + *tv = rettv; + } + else + { + int error = FALSE; + + /* filter(): when expr is zero remove the item */ + *remp = (get_tv_number_chk(&rettv, &error) == 0); + clear_tv(&rettv); + /* On type error, nothing has been removed; return FAIL to stop the + * loop. The error message was given by get_tv_number_chk(). */ + if (error) + goto theend; + } + retval = OK; +theend: + clear_tv(&vimvars[VV_VAL].vv_tv); + return retval; +} + + +/* + * Implementation of map() and filter(). + */ + void +filter_map(typval_T *argvars, typval_T *rettv, int map) +{ + typval_T *expr; + listitem_T *li, *nli; + list_T *l = NULL; + dictitem_T *di; + hashtab_T *ht; + hashitem_T *hi; + dict_T *d = NULL; + typval_T save_val; + typval_T save_key; + int rem; + int todo; + char_u *ermsg = (char_u *)(map ? "map()" : "filter()"); + char_u *arg_errmsg = (char_u *)(map ? N_("map() argument") + : N_("filter() argument")); + int save_did_emsg; + int idx = 0; + + if (argvars[0].v_type == VAR_LIST) + { + if ((l = argvars[0].vval.v_list) == NULL + || (!map && tv_check_lock(l->lv_lock, arg_errmsg, TRUE))) + return; + } + else if (argvars[0].v_type == VAR_DICT) + { + if ((d = argvars[0].vval.v_dict) == NULL + || (!map && tv_check_lock(d->dv_lock, arg_errmsg, TRUE))) + return; + } + else + { + EMSG2(_(e_listdictarg), ermsg); + return; + } + + expr = &argvars[1]; + /* On type errors, the preceding call has already displayed an error + * message. Avoid a misleading error message for an empty string that + * was not passed as argument. */ + if (expr->v_type != VAR_UNKNOWN) + { + prepare_vimvar(VV_VAL, &save_val); + + /* We reset "did_emsg" to be able to detect whether an error + * occurred during evaluation of the expression. */ + save_did_emsg = did_emsg; + did_emsg = FALSE; + + prepare_vimvar(VV_KEY, &save_key); + if (argvars[0].v_type == VAR_DICT) + { + vimvars[VV_KEY].vv_type = VAR_STRING; + + ht = &d->dv_hashtab; + hash_lock(ht); + todo = (int)ht->ht_used; + for (hi = ht->ht_array; todo > 0; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + int r; + + --todo; + di = HI2DI(hi); + if (map && + (tv_check_lock(di->di_tv.v_lock, arg_errmsg, TRUE) + || var_check_ro(di->di_flags, arg_errmsg, TRUE))) + break; + vimvars[VV_KEY].vv_str = vim_strsave(di->di_key); + r = filter_map_one(&di->di_tv, expr, map, &rem); + clear_tv(&vimvars[VV_KEY].vv_tv); + if (r == FAIL || did_emsg) + break; + if (!map && rem) + { + if (var_check_fixed(di->di_flags, arg_errmsg, TRUE) + || var_check_ro(di->di_flags, arg_errmsg, TRUE)) + break; + dictitem_remove(d, di); + } + } + } + hash_unlock(ht); + } + else + { + vimvars[VV_KEY].vv_type = VAR_NUMBER; + + for (li = l->lv_first; li != NULL; li = nli) + { + if (map && tv_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE)) + break; + nli = li->li_next; + vimvars[VV_KEY].vv_nr = idx; + if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL + || did_emsg) + break; + if (!map && rem) + listitem_remove(l, li); + ++idx; + } + } + + restore_vimvar(VV_KEY, &save_key); + restore_vimvar(VV_VAL, &save_val); + + did_emsg |= save_did_emsg; + } + + copy_tv(&argvars[0], rettv); +} + #endif /* defined(FEAT_MODIFY_FNAME) || defined(FEAT_EVAL) */ diff --git a/src/evalfunc.c b/src/evalfunc.c new file mode 100644 index 0000000000..7d3f875210 --- /dev/null +++ b/src/evalfunc.c @@ -0,0 +1,12552 @@ +/* vi:set ts=8 sts=4 sw=4: + * + * 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 AMIGA +# include /* for strftime() */ +#endif + +#ifdef VMS +# include +#endif + +#ifdef MACOS +# include /* for time_t */ +#endif + +static char *e_listarg = N_("E686: Argument of %s must be a List"); +#ifdef FEAT_QUICKFIX +static char *e_stringreq = N_("E928: String required"); +#endif + +#ifdef FEAT_FLOAT +static void f_abs(typval_T *argvars, typval_T *rettv); +static void f_acos(typval_T *argvars, typval_T *rettv); +#endif +static void f_add(typval_T *argvars, typval_T *rettv); +static void f_and(typval_T *argvars, typval_T *rettv); +static void f_append(typval_T *argvars, typval_T *rettv); +static void f_argc(typval_T *argvars, typval_T *rettv); +static void f_argidx(typval_T *argvars, typval_T *rettv); +static void f_arglistid(typval_T *argvars, typval_T *rettv); +static void f_argv(typval_T *argvars, typval_T *rettv); +static void f_assert_equal(typval_T *argvars, typval_T *rettv); +static void f_assert_exception(typval_T *argvars, typval_T *rettv); +static void f_assert_fails(typval_T *argvars, typval_T *rettv); +static void f_assert_false(typval_T *argvars, typval_T *rettv); +static void f_assert_match(typval_T *argvars, typval_T *rettv); +static void f_assert_notequal(typval_T *argvars, typval_T *rettv); +static void f_assert_notmatch(typval_T *argvars, typval_T *rettv); +static void f_assert_true(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_FLOAT +static void f_asin(typval_T *argvars, typval_T *rettv); +static void f_atan(typval_T *argvars, typval_T *rettv); +static void f_atan2(typval_T *argvars, typval_T *rettv); +#endif +static void f_browse(typval_T *argvars, typval_T *rettv); +static void f_browsedir(typval_T *argvars, typval_T *rettv); +static void f_bufexists(typval_T *argvars, typval_T *rettv); +static void f_buflisted(typval_T *argvars, typval_T *rettv); +static void f_bufloaded(typval_T *argvars, typval_T *rettv); +static void f_bufname(typval_T *argvars, typval_T *rettv); +static void f_bufnr(typval_T *argvars, typval_T *rettv); +static void f_bufwinid(typval_T *argvars, typval_T *rettv); +static void f_bufwinnr(typval_T *argvars, typval_T *rettv); +static void f_byte2line(typval_T *argvars, typval_T *rettv); +static void byteidx(typval_T *argvars, typval_T *rettv, int comp); +static void f_byteidx(typval_T *argvars, typval_T *rettv); +static void f_byteidxcomp(typval_T *argvars, typval_T *rettv); +static void f_call(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_FLOAT +static void f_ceil(typval_T *argvars, typval_T *rettv); +#endif +#ifdef FEAT_JOB_CHANNEL +static void f_ch_close(typval_T *argvars, typval_T *rettv); +static void f_ch_evalexpr(typval_T *argvars, typval_T *rettv); +static void f_ch_evalraw(typval_T *argvars, typval_T *rettv); +static void f_ch_getbufnr(typval_T *argvars, typval_T *rettv); +static void f_ch_getjob(typval_T *argvars, typval_T *rettv); +static void f_ch_info(typval_T *argvars, typval_T *rettv); +static void f_ch_log(typval_T *argvars, typval_T *rettv); +static void f_ch_logfile(typval_T *argvars, typval_T *rettv); +static void f_ch_open(typval_T *argvars, typval_T *rettv); +static void f_ch_read(typval_T *argvars, typval_T *rettv); +static void f_ch_readraw(typval_T *argvars, typval_T *rettv); +static void f_ch_sendexpr(typval_T *argvars, typval_T *rettv); +static void f_ch_sendraw(typval_T *argvars, typval_T *rettv); +static void f_ch_setoptions(typval_T *argvars, typval_T *rettv); +static void f_ch_status(typval_T *argvars, typval_T *rettv); +#endif +static void f_changenr(typval_T *argvars, typval_T *rettv); +static void f_char2nr(typval_T *argvars, typval_T *rettv); +static void f_cindent(typval_T *argvars, typval_T *rettv); +static void f_clearmatches(typval_T *argvars, typval_T *rettv); +static void f_col(typval_T *argvars, typval_T *rettv); +#if defined(FEAT_INS_EXPAND) +static void f_complete(typval_T *argvars, typval_T *rettv); +static void f_complete_add(typval_T *argvars, typval_T *rettv); +static void f_complete_check(typval_T *argvars, typval_T *rettv); +#endif +static void f_confirm(typval_T *argvars, typval_T *rettv); +static void f_copy(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_FLOAT +static void f_cos(typval_T *argvars, typval_T *rettv); +static void f_cosh(typval_T *argvars, typval_T *rettv); +#endif +static void f_count(typval_T *argvars, typval_T *rettv); +static void f_cscope_connection(typval_T *argvars, typval_T *rettv); +static void f_cursor(typval_T *argsvars, typval_T *rettv); +static void f_deepcopy(typval_T *argvars, typval_T *rettv); +static void f_delete(typval_T *argvars, typval_T *rettv); +static void f_did_filetype(typval_T *argvars, typval_T *rettv); +static void f_diff_filler(typval_T *argvars, typval_T *rettv); +static void f_diff_hlID(typval_T *argvars, typval_T *rettv); +static void f_empty(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_executable(typval_T *argvars, typval_T *rettv); +static void f_execute(typval_T *argvars, typval_T *rettv); +static void f_exepath(typval_T *argvars, typval_T *rettv); +static void f_exists(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_FLOAT +static void f_exp(typval_T *argvars, typval_T *rettv); +#endif +static void f_expand(typval_T *argvars, typval_T *rettv); +static void f_extend(typval_T *argvars, typval_T *rettv); +static void f_feedkeys(typval_T *argvars, typval_T *rettv); +static void f_filereadable(typval_T *argvars, typval_T *rettv); +static void f_filewritable(typval_T *argvars, typval_T *rettv); +static void f_filter(typval_T *argvars, typval_T *rettv); +static void f_finddir(typval_T *argvars, typval_T *rettv); +static void f_findfile(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_FLOAT +static void f_float2nr(typval_T *argvars, typval_T *rettv); +static void f_floor(typval_T *argvars, typval_T *rettv); +static void f_fmod(typval_T *argvars, typval_T *rettv); +#endif +static void f_fnameescape(typval_T *argvars, typval_T *rettv); +static void f_fnamemodify(typval_T *argvars, typval_T *rettv); +static void f_foldclosed(typval_T *argvars, typval_T *rettv); +static void f_foldclosedend(typval_T *argvars, typval_T *rettv); +static void f_foldlevel(typval_T *argvars, typval_T *rettv); +static void f_foldtext(typval_T *argvars, typval_T *rettv); +static void f_foldtextresult(typval_T *argvars, typval_T *rettv); +static void f_foreground(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_getbufline(typval_T *argvars, typval_T *rettv); +static void f_getbufvar(typval_T *argvars, typval_T *rettv); +static void f_getchar(typval_T *argvars, typval_T *rettv); +static void f_getcharmod(typval_T *argvars, typval_T *rettv); +static void f_getcharsearch(typval_T *argvars, typval_T *rettv); +static void f_getcmdline(typval_T *argvars, typval_T *rettv); +#if defined(FEAT_CMDL_COMPL) +static void f_getcompletion(typval_T *argvars, typval_T *rettv); +#endif +static void f_getcmdpos(typval_T *argvars, typval_T *rettv); +static void f_getcmdtype(typval_T *argvars, typval_T *rettv); +static void f_getcmdwintype(typval_T *argvars, typval_T *rettv); +static void f_getcwd(typval_T *argvars, typval_T *rettv); +static void f_getfontname(typval_T *argvars, typval_T *rettv); +static void f_getfperm(typval_T *argvars, typval_T *rettv); +static void f_getfsize(typval_T *argvars, typval_T *rettv); +static void f_getftime(typval_T *argvars, typval_T *rettv); +static void f_getftype(typval_T *argvars, typval_T *rettv); +static void f_getline(typval_T *argvars, typval_T *rettv); +static void f_getmatches(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_getpos(typval_T *argvars, typval_T *rettv); +static void f_getqflist(typval_T *argvars, typval_T *rettv); +static void f_getreg(typval_T *argvars, typval_T *rettv); +static void f_getregtype(typval_T *argvars, typval_T *rettv); +static void f_gettabvar(typval_T *argvars, typval_T *rettv); +static void f_gettabwinvar(typval_T *argvars, typval_T *rettv); +static void f_getwinposx(typval_T *argvars, typval_T *rettv); +static void f_getwinposy(typval_T *argvars, typval_T *rettv); +static void f_getwinvar(typval_T *argvars, typval_T *rettv); +static void f_glob(typval_T *argvars, typval_T *rettv); +static void f_globpath(typval_T *argvars, typval_T *rettv); +static void f_glob2regpat(typval_T *argvars, typval_T *rettv); +static void f_has(typval_T *argvars, typval_T *rettv); +static void f_has_key(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_histadd(typval_T *argvars, typval_T *rettv); +static void f_histdel(typval_T *argvars, typval_T *rettv); +static void f_histget(typval_T *argvars, typval_T *rettv); +static void f_histnr(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_iconv(typval_T *argvars, typval_T *rettv); +static void f_indent(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_insert(typval_T *argvars, typval_T *rettv); +static void f_invert(typval_T *argvars, typval_T *rettv); +static void f_isdirectory(typval_T *argvars, typval_T *rettv); +static void f_islocked(typval_T *argvars, typval_T *rettv); +#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H) +static void f_isnan(typval_T *argvars, typval_T *rettv); +#endif +static void f_items(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_JOB_CHANNEL +static void f_job_getchannel(typval_T *argvars, typval_T *rettv); +static void f_job_info(typval_T *argvars, typval_T *rettv); +static void f_job_setoptions(typval_T *argvars, typval_T *rettv); +static void f_job_start(typval_T *argvars, typval_T *rettv); +static void f_job_stop(typval_T *argvars, typval_T *rettv); +static void f_job_status(typval_T *argvars, typval_T *rettv); +#endif +static void f_join(typval_T *argvars, typval_T *rettv); +static void f_js_decode(typval_T *argvars, typval_T *rettv); +static void f_js_encode(typval_T *argvars, typval_T *rettv); +static void f_json_decode(typval_T *argvars, typval_T *rettv); +static void f_json_encode(typval_T *argvars, typval_T *rettv); +static void f_keys(typval_T *argvars, typval_T *rettv); +static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv); +static void f_len(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); +static void f_lispindent(typval_T *argvars, typval_T *rettv); +static void f_localtime(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_FLOAT +static void f_log(typval_T *argvars, typval_T *rettv); +static void f_log10(typval_T *argvars, typval_T *rettv); +#endif +#ifdef FEAT_LUA +static void f_luaeval(typval_T *argvars, typval_T *rettv); +#endif +static void f_map(typval_T *argvars, typval_T *rettv); +static void f_maparg(typval_T *argvars, typval_T *rettv); +static void f_mapcheck(typval_T *argvars, typval_T *rettv); +static void f_match(typval_T *argvars, typval_T *rettv); +static void f_matchadd(typval_T *argvars, typval_T *rettv); +static void f_matchaddpos(typval_T *argvars, typval_T *rettv); +static void f_matcharg(typval_T *argvars, typval_T *rettv); +static void f_matchdelete(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 vim_mkdir +static void f_mkdir(typval_T *argvars, typval_T *rettv); +#endif +static void f_mode(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); +static void f_pathshorten(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_PERL +static void f_perleval(typval_T *argvars, typval_T *rettv); +#endif +#ifdef FEAT_FLOAT +static void f_pow(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_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 +static void f_range(typval_T *argvars, typval_T *rettv); +static void f_readfile(typval_T *argvars, typval_T *rettv); +static void f_reltime(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_FLOAT +static void f_reltimefloat(typval_T *argvars, typval_T *rettv); +#endif +static void f_reltimestr(typval_T *argvars, typval_T *rettv); +static void f_remote_expr(typval_T *argvars, typval_T *rettv); +static void f_remote_foreground(typval_T *argvars, typval_T *rettv); +static void f_remote_peek(typval_T *argvars, typval_T *rettv); +static void f_remote_read(typval_T *argvars, typval_T *rettv); +static void f_remote_send(typval_T *argvars, typval_T *rettv); +static void f_remove(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); +static void f_resolve(typval_T *argvars, typval_T *rettv); +static void f_reverse(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_FLOAT +static void f_round(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_screencol(typval_T *argvars, typval_T *rettv); +static void f_screenrow(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_server2client(typval_T *argvars, typval_T *rettv); +static void f_serverlist(typval_T *argvars, typval_T *rettv); +static void f_setbufvar(typval_T *argvars, typval_T *rettv); +static void f_setcharsearch(typval_T *argvars, typval_T *rettv); +static void f_setcmdpos(typval_T *argvars, typval_T *rettv); +static void f_setfperm(typval_T *argvars, typval_T *rettv); +static void f_setline(typval_T *argvars, typval_T *rettv); +static void f_setloclist(typval_T *argvars, typval_T *rettv); +static void f_setmatches(typval_T *argvars, typval_T *rettv); +static void f_setpos(typval_T *argvars, typval_T *rettv); +static void f_setqflist(typval_T *argvars, typval_T *rettv); +static void f_setreg(typval_T *argvars, typval_T *rettv); +static void f_settabvar(typval_T *argvars, typval_T *rettv); +static void f_settabwinvar(typval_T *argvars, typval_T *rettv); +static void f_setwinvar(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_CRYPT +static void f_sha256(typval_T *argvars, typval_T *rettv); +#endif /* FEAT_CRYPT */ +static void f_shellescape(typval_T *argvars, typval_T *rettv); +static void f_shiftwidth(typval_T *argvars, typval_T *rettv); +static void f_simplify(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_FLOAT +static void f_sin(typval_T *argvars, typval_T *rettv); +static void f_sinh(typval_T *argvars, typval_T *rettv); +#endif +static void f_sort(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); +#ifdef FEAT_FLOAT +static void f_sqrt(typval_T *argvars, typval_T *rettv); +static void f_str2float(typval_T *argvars, typval_T *rettv); +#endif +static void f_str2nr(typval_T *argvars, typval_T *rettv); +static void f_strchars(typval_T *argvars, typval_T *rettv); +#ifdef HAVE_STRFTIME +static void f_strftime(typval_T *argvars, typval_T *rettv); +#endif +static void f_strgetchar(typval_T *argvars, typval_T *rettv); +static void f_stridx(typval_T *argvars, typval_T *rettv); +static void f_string(typval_T *argvars, typval_T *rettv); +static void f_strlen(typval_T *argvars, typval_T *rettv); +static void f_strcharpart(typval_T *argvars, typval_T *rettv); +static void f_strpart(typval_T *argvars, typval_T *rettv); +static void f_strridx(typval_T *argvars, typval_T *rettv); +static void f_strtrans(typval_T *argvars, typval_T *rettv); +static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv); +static void f_strwidth(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_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_system(typval_T *argvars, typval_T *rettv); +static void f_systemlist(typval_T *argvars, typval_T *rettv); +static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv); +static void f_tabpagenr(typval_T *argvars, typval_T *rettv); +static void f_tabpagewinnr(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_tempname(typval_T *argvars, typval_T *rettv); +static void f_test_alloc_fail(typval_T *argvars, typval_T *rettv); +static void f_test_autochdir(typval_T *argvars, typval_T *rettv); +static void f_test_disable_char_avail(typval_T *argvars, typval_T *rettv); +static void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_JOB_CHANNEL +static void f_test_null_channel(typval_T *argvars, typval_T *rettv); +#endif +static void f_test_null_dict(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_JOB_CHANNEL +static void f_test_null_job(typval_T *argvars, typval_T *rettv); +#endif +static void f_test_null_list(typval_T *argvars, typval_T *rettv); +static void f_test_null_partial(typval_T *argvars, typval_T *rettv); +static void f_test_null_string(typval_T *argvars, typval_T *rettv); +static void f_test_settime(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_FLOAT +static void f_tan(typval_T *argvars, typval_T *rettv); +static void f_tanh(typval_T *argvars, typval_T *rettv); +#endif +#ifdef FEAT_TIMERS +static void f_timer_start(typval_T *argvars, typval_T *rettv); +static void f_timer_stop(typval_T *argvars, typval_T *rettv); +#endif +static void f_tolower(typval_T *argvars, typval_T *rettv); +static void f_toupper(typval_T *argvars, typval_T *rettv); +static void f_tr(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_FLOAT +static void f_trunc(typval_T *argvars, typval_T *rettv); +#endif +static void f_type(typval_T *argvars, typval_T *rettv); +static void f_undofile(typval_T *argvars, typval_T *rettv); +static void f_undotree(typval_T *argvars, typval_T *rettv); +static void f_uniq(typval_T *argvars, typval_T *rettv); +static void f_values(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_win_findbuf(typval_T *argvars, typval_T *rettv); +static void f_win_getid(typval_T *argvars, typval_T *rettv); +static void f_win_gotoid(typval_T *argvars, typval_T *rettv); +static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv); +static void f_win_id2win(typval_T *argvars, typval_T *rettv); +static void f_winbufnr(typval_T *argvars, typval_T *rettv); +static void f_wincol(typval_T *argvars, typval_T *rettv); +static void f_winheight(typval_T *argvars, typval_T *rettv); +static void f_winline(typval_T *argvars, typval_T *rettv); +static void f_winnr(typval_T *argvars, typval_T *rettv); +static void f_winrestcmd(typval_T *argvars, typval_T *rettv); +static void f_winrestview(typval_T *argvars, typval_T *rettv); +static void f_winsaveview(typval_T *argvars, typval_T *rettv); +static void f_winwidth(typval_T *argvars, typval_T *rettv); +static void f_writefile(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); + +/* + * Array with names and number of arguments of all internal functions + * MUST BE KEPT SORTED IN strcmp() ORDER FOR BINARY SEARCH! + */ +static struct fst +{ + char *f_name; /* function name */ + char f_min_argc; /* minimal number of arguments */ + char f_max_argc; /* maximal number of arguments */ + void (*f_func)(typval_T *args, typval_T *rvar); + /* implementation of function */ +} functions[] = +{ +#ifdef FEAT_FLOAT + {"abs", 1, 1, f_abs}, + {"acos", 1, 1, f_acos}, /* WJMc */ +#endif + {"add", 2, 2, f_add}, + {"and", 2, 2, f_and}, + {"append", 2, 2, f_append}, + {"argc", 0, 0, f_argc}, + {"argidx", 0, 0, f_argidx}, + {"arglistid", 0, 2, f_arglistid}, + {"argv", 0, 1, f_argv}, +#ifdef FEAT_FLOAT + {"asin", 1, 1, f_asin}, /* WJMc */ +#endif + {"assert_equal", 2, 3, f_assert_equal}, + {"assert_exception", 1, 2, f_assert_exception}, + {"assert_fails", 1, 2, f_assert_fails}, + {"assert_false", 1, 2, f_assert_false}, + {"assert_match", 2, 3, f_assert_match}, + {"assert_notequal", 2, 3, f_assert_notequal}, + {"assert_notmatch", 2, 3, f_assert_notmatch}, + {"assert_true", 1, 2, f_assert_true}, +#ifdef FEAT_FLOAT + {"atan", 1, 1, f_atan}, + {"atan2", 2, 2, f_atan2}, +#endif + {"browse", 4, 4, f_browse}, + {"browsedir", 2, 2, f_browsedir}, + {"bufexists", 1, 1, f_bufexists}, + {"buffer_exists", 1, 1, f_bufexists}, /* obsolete */ + {"buffer_name", 1, 1, f_bufname}, /* obsolete */ + {"buffer_number", 1, 1, f_bufnr}, /* obsolete */ + {"buflisted", 1, 1, f_buflisted}, + {"bufloaded", 1, 1, f_bufloaded}, + {"bufname", 1, 1, f_bufname}, + {"bufnr", 1, 2, f_bufnr}, + {"bufwinid", 1, 1, f_bufwinid}, + {"bufwinnr", 1, 1, f_bufwinnr}, + {"byte2line", 1, 1, f_byte2line}, + {"byteidx", 2, 2, f_byteidx}, + {"byteidxcomp", 2, 2, f_byteidxcomp}, + {"call", 2, 3, f_call}, +#ifdef FEAT_FLOAT + {"ceil", 1, 1, f_ceil}, +#endif +#ifdef FEAT_JOB_CHANNEL + {"ch_close", 1, 1, f_ch_close}, + {"ch_evalexpr", 2, 3, f_ch_evalexpr}, + {"ch_evalraw", 2, 3, f_ch_evalraw}, + {"ch_getbufnr", 2, 2, f_ch_getbufnr}, + {"ch_getjob", 1, 1, f_ch_getjob}, + {"ch_info", 1, 1, f_ch_info}, + {"ch_log", 1, 2, f_ch_log}, + {"ch_logfile", 1, 2, f_ch_logfile}, + {"ch_open", 1, 2, f_ch_open}, + {"ch_read", 1, 2, f_ch_read}, + {"ch_readraw", 1, 2, f_ch_readraw}, + {"ch_sendexpr", 2, 3, f_ch_sendexpr}, + {"ch_sendraw", 2, 3, f_ch_sendraw}, + {"ch_setoptions", 2, 2, f_ch_setoptions}, + {"ch_status", 1, 1, f_ch_status}, +#endif + {"changenr", 0, 0, f_changenr}, + {"char2nr", 1, 2, f_char2nr}, + {"cindent", 1, 1, f_cindent}, + {"clearmatches", 0, 0, f_clearmatches}, + {"col", 1, 1, f_col}, +#if defined(FEAT_INS_EXPAND) + {"complete", 2, 2, f_complete}, + {"complete_add", 1, 1, f_complete_add}, + {"complete_check", 0, 0, f_complete_check}, +#endif + {"confirm", 1, 4, f_confirm}, + {"copy", 1, 1, f_copy}, +#ifdef FEAT_FLOAT + {"cos", 1, 1, f_cos}, + {"cosh", 1, 1, f_cosh}, +#endif + {"count", 2, 4, f_count}, + {"cscope_connection",0,3, f_cscope_connection}, + {"cursor", 1, 3, f_cursor}, + {"deepcopy", 1, 2, f_deepcopy}, + {"delete", 1, 2, f_delete}, + {"did_filetype", 0, 0, f_did_filetype}, + {"diff_filler", 1, 1, f_diff_filler}, + {"diff_hlID", 2, 2, f_diff_hlID}, + {"empty", 1, 1, f_empty}, + {"escape", 2, 2, f_escape}, + {"eval", 1, 1, f_eval}, + {"eventhandler", 0, 0, f_eventhandler}, + {"executable", 1, 1, f_executable}, + {"execute", 1, 2, f_execute}, + {"exepath", 1, 1, f_exepath}, + {"exists", 1, 1, f_exists}, +#ifdef FEAT_FLOAT + {"exp", 1, 1, f_exp}, +#endif + {"expand", 1, 3, f_expand}, + {"extend", 2, 3, f_extend}, + {"feedkeys", 1, 2, f_feedkeys}, + {"file_readable", 1, 1, f_filereadable}, /* obsolete */ + {"filereadable", 1, 1, f_filereadable}, + {"filewritable", 1, 1, f_filewritable}, + {"filter", 2, 2, f_filter}, + {"finddir", 1, 3, f_finddir}, + {"findfile", 1, 3, f_findfile}, +#ifdef FEAT_FLOAT + {"float2nr", 1, 1, f_float2nr}, + {"floor", 1, 1, f_floor}, + {"fmod", 2, 2, f_fmod}, +#endif + {"fnameescape", 1, 1, f_fnameescape}, + {"fnamemodify", 2, 2, f_fnamemodify}, + {"foldclosed", 1, 1, f_foldclosed}, + {"foldclosedend", 1, 1, f_foldclosedend}, + {"foldlevel", 1, 1, f_foldlevel}, + {"foldtext", 0, 0, f_foldtext}, + {"foldtextresult", 1, 1, f_foldtextresult}, + {"foreground", 0, 0, f_foreground}, + {"function", 1, 3, f_function}, + {"garbagecollect", 0, 1, f_garbagecollect}, + {"get", 2, 3, f_get}, + {"getbufline", 2, 3, f_getbufline}, + {"getbufvar", 2, 3, f_getbufvar}, + {"getchar", 0, 1, f_getchar}, + {"getcharmod", 0, 0, f_getcharmod}, + {"getcharsearch", 0, 0, f_getcharsearch}, + {"getcmdline", 0, 0, f_getcmdline}, + {"getcmdpos", 0, 0, f_getcmdpos}, + {"getcmdtype", 0, 0, f_getcmdtype}, + {"getcmdwintype", 0, 0, f_getcmdwintype}, +#if defined(FEAT_CMDL_COMPL) + {"getcompletion", 2, 2, f_getcompletion}, +#endif + {"getcurpos", 0, 0, f_getcurpos}, + {"getcwd", 0, 2, f_getcwd}, + {"getfontname", 0, 1, f_getfontname}, + {"getfperm", 1, 1, f_getfperm}, + {"getfsize", 1, 1, f_getfsize}, + {"getftime", 1, 1, f_getftime}, + {"getftype", 1, 1, f_getftype}, + {"getline", 1, 2, f_getline}, + {"getloclist", 1, 1, f_getqflist}, + {"getmatches", 0, 0, f_getmatches}, + {"getpid", 0, 0, f_getpid}, + {"getpos", 1, 1, f_getpos}, + {"getqflist", 0, 0, f_getqflist}, + {"getreg", 0, 3, f_getreg}, + {"getregtype", 0, 1, f_getregtype}, + {"gettabvar", 2, 3, f_gettabvar}, + {"gettabwinvar", 3, 4, f_gettabwinvar}, + {"getwinposx", 0, 0, f_getwinposx}, + {"getwinposy", 0, 0, f_getwinposy}, + {"getwinvar", 2, 3, f_getwinvar}, + {"glob", 1, 4, f_glob}, + {"glob2regpat", 1, 1, f_glob2regpat}, + {"globpath", 2, 5, f_globpath}, + {"has", 1, 1, f_has}, + {"has_key", 2, 2, f_has_key}, + {"haslocaldir", 0, 2, f_haslocaldir}, + {"hasmapto", 1, 3, f_hasmapto}, + {"highlightID", 1, 1, f_hlID}, /* obsolete */ + {"highlight_exists",1, 1, f_hlexists}, /* obsolete */ + {"histadd", 2, 2, f_histadd}, + {"histdel", 1, 2, f_histdel}, + {"histget", 1, 2, f_histget}, + {"histnr", 1, 1, f_histnr}, + {"hlID", 1, 1, f_hlID}, + {"hlexists", 1, 1, f_hlexists}, + {"hostname", 0, 0, f_hostname}, + {"iconv", 3, 3, f_iconv}, + {"indent", 1, 1, f_indent}, + {"index", 2, 4, f_index}, + {"input", 1, 3, f_input}, + {"inputdialog", 1, 3, f_inputdialog}, + {"inputlist", 1, 1, f_inputlist}, + {"inputrestore", 0, 0, f_inputrestore}, + {"inputsave", 0, 0, f_inputsave}, + {"inputsecret", 1, 2, f_inputsecret}, + {"insert", 2, 3, f_insert}, + {"invert", 1, 1, f_invert}, + {"isdirectory", 1, 1, f_isdirectory}, + {"islocked", 1, 1, f_islocked}, +#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H) + {"isnan", 1, 1, f_isnan}, +#endif + {"items", 1, 1, f_items}, +#ifdef FEAT_JOB_CHANNEL + {"job_getchannel", 1, 1, f_job_getchannel}, + {"job_info", 1, 1, f_job_info}, + {"job_setoptions", 2, 2, f_job_setoptions}, + {"job_start", 1, 2, f_job_start}, + {"job_status", 1, 1, f_job_status}, + {"job_stop", 1, 2, f_job_stop}, +#endif + {"join", 1, 2, f_join}, + {"js_decode", 1, 1, f_js_decode}, + {"js_encode", 1, 1, f_js_encode}, + {"json_decode", 1, 1, f_json_decode}, + {"json_encode", 1, 1, f_json_encode}, + {"keys", 1, 1, f_keys}, + {"last_buffer_nr", 0, 0, f_last_buffer_nr},/* obsolete */ + {"len", 1, 1, f_len}, + {"libcall", 3, 3, f_libcall}, + {"libcallnr", 3, 3, f_libcallnr}, + {"line", 1, 1, f_line}, + {"line2byte", 1, 1, f_line2byte}, + {"lispindent", 1, 1, f_lispindent}, + {"localtime", 0, 0, f_localtime}, +#ifdef FEAT_FLOAT + {"log", 1, 1, f_log}, + {"log10", 1, 1, f_log10}, +#endif +#ifdef FEAT_LUA + {"luaeval", 1, 2, f_luaeval}, +#endif + {"map", 2, 2, f_map}, + {"maparg", 1, 4, f_maparg}, + {"mapcheck", 1, 3, f_mapcheck}, + {"match", 2, 4, f_match}, + {"matchadd", 2, 5, f_matchadd}, + {"matchaddpos", 2, 5, f_matchaddpos}, + {"matcharg", 1, 1, f_matcharg}, + {"matchdelete", 1, 1, f_matchdelete}, + {"matchend", 2, 4, f_matchend}, + {"matchlist", 2, 4, f_matchlist}, + {"matchstr", 2, 4, f_matchstr}, + {"matchstrpos", 2, 4, f_matchstrpos}, + {"max", 1, 1, f_max}, + {"min", 1, 1, f_min}, +#ifdef vim_mkdir + {"mkdir", 1, 3, f_mkdir}, +#endif + {"mode", 0, 1, f_mode}, +#ifdef FEAT_MZSCHEME + {"mzeval", 1, 1, f_mzeval}, +#endif + {"nextnonblank", 1, 1, f_nextnonblank}, + {"nr2char", 1, 2, f_nr2char}, + {"or", 2, 2, f_or}, + {"pathshorten", 1, 1, f_pathshorten}, +#ifdef FEAT_PERL + {"perleval", 1, 1, f_perleval}, +#endif +#ifdef FEAT_FLOAT + {"pow", 2, 2, f_pow}, +#endif + {"prevnonblank", 1, 1, f_prevnonblank}, + {"printf", 2, 19, f_printf}, + {"pumvisible", 0, 0, f_pumvisible}, +#ifdef FEAT_PYTHON3 + {"py3eval", 1, 1, f_py3eval}, +#endif +#ifdef FEAT_PYTHON + {"pyeval", 1, 1, f_pyeval}, +#endif + {"range", 1, 3, f_range}, + {"readfile", 1, 3, f_readfile}, + {"reltime", 0, 2, f_reltime}, +#ifdef FEAT_FLOAT + {"reltimefloat", 1, 1, f_reltimefloat}, +#endif + {"reltimestr", 1, 1, f_reltimestr}, + {"remote_expr", 2, 3, f_remote_expr}, + {"remote_foreground", 1, 1, f_remote_foreground}, + {"remote_peek", 1, 2, f_remote_peek}, + {"remote_read", 1, 1, f_remote_read}, + {"remote_send", 2, 3, f_remote_send}, + {"remove", 2, 3, f_remove}, + {"rename", 2, 2, f_rename}, + {"repeat", 2, 2, f_repeat}, + {"resolve", 1, 1, f_resolve}, + {"reverse", 1, 1, f_reverse}, +#ifdef FEAT_FLOAT + {"round", 1, 1, f_round}, +#endif + {"screenattr", 2, 2, f_screenattr}, + {"screenchar", 2, 2, f_screenchar}, + {"screencol", 0, 0, f_screencol}, + {"screenrow", 0, 0, f_screenrow}, + {"search", 1, 4, f_search}, + {"searchdecl", 1, 3, f_searchdecl}, + {"searchpair", 3, 7, f_searchpair}, + {"searchpairpos", 3, 7, f_searchpairpos}, + {"searchpos", 1, 4, f_searchpos}, + {"server2client", 2, 2, f_server2client}, + {"serverlist", 0, 0, f_serverlist}, + {"setbufvar", 3, 3, f_setbufvar}, + {"setcharsearch", 1, 1, f_setcharsearch}, + {"setcmdpos", 1, 1, f_setcmdpos}, + {"setfperm", 2, 2, f_setfperm}, + {"setline", 2, 2, f_setline}, + {"setloclist", 2, 3, f_setloclist}, + {"setmatches", 1, 1, f_setmatches}, + {"setpos", 2, 2, f_setpos}, + {"setqflist", 1, 2, f_setqflist}, + {"setreg", 2, 3, f_setreg}, + {"settabvar", 3, 3, f_settabvar}, + {"settabwinvar", 4, 4, f_settabwinvar}, + {"setwinvar", 3, 3, f_setwinvar}, +#ifdef FEAT_CRYPT + {"sha256", 1, 1, f_sha256}, +#endif + {"shellescape", 1, 2, f_shellescape}, + {"shiftwidth", 0, 0, f_shiftwidth}, + {"simplify", 1, 1, f_simplify}, +#ifdef FEAT_FLOAT + {"sin", 1, 1, f_sin}, + {"sinh", 1, 1, f_sinh}, +#endif + {"sort", 1, 3, f_sort}, + {"soundfold", 1, 1, f_soundfold}, + {"spellbadword", 0, 1, f_spellbadword}, + {"spellsuggest", 1, 3, f_spellsuggest}, + {"split", 1, 3, f_split}, +#ifdef FEAT_FLOAT + {"sqrt", 1, 1, f_sqrt}, + {"str2float", 1, 1, f_str2float}, +#endif + {"str2nr", 1, 2, f_str2nr}, + {"strcharpart", 2, 3, f_strcharpart}, + {"strchars", 1, 2, f_strchars}, + {"strdisplaywidth", 1, 2, f_strdisplaywidth}, +#ifdef HAVE_STRFTIME + {"strftime", 1, 2, f_strftime}, +#endif + {"strgetchar", 2, 2, f_strgetchar}, + {"stridx", 2, 3, f_stridx}, + {"string", 1, 1, f_string}, + {"strlen", 1, 1, f_strlen}, + {"strpart", 2, 3, f_strpart}, + {"strridx", 2, 3, f_strridx}, + {"strtrans", 1, 1, f_strtrans}, + {"strwidth", 1, 1, f_strwidth}, + {"submatch", 1, 2, f_submatch}, + {"substitute", 4, 4, f_substitute}, + {"synID", 3, 3, f_synID}, + {"synIDattr", 2, 3, f_synIDattr}, + {"synIDtrans", 1, 1, f_synIDtrans}, + {"synconcealed", 2, 2, f_synconcealed}, + {"synstack", 2, 2, f_synstack}, + {"system", 1, 2, f_system}, + {"systemlist", 1, 2, f_systemlist}, + {"tabpagebuflist", 0, 1, f_tabpagebuflist}, + {"tabpagenr", 0, 1, f_tabpagenr}, + {"tabpagewinnr", 1, 2, f_tabpagewinnr}, + {"tagfiles", 0, 0, f_tagfiles}, + {"taglist", 1, 1, f_taglist}, +#ifdef FEAT_FLOAT + {"tan", 1, 1, f_tan}, + {"tanh", 1, 1, f_tanh}, +#endif + {"tempname", 0, 0, f_tempname}, + {"test_alloc_fail", 3, 3, f_test_alloc_fail}, + {"test_autochdir", 0, 0, f_test_autochdir}, + {"test_disable_char_avail", 1, 1, f_test_disable_char_avail}, + {"test_garbagecollect_now", 0, 0, f_test_garbagecollect_now}, +#ifdef FEAT_JOB_CHANNEL + {"test_null_channel", 0, 0, f_test_null_channel}, +#endif + {"test_null_dict", 0, 0, f_test_null_dict}, +#ifdef FEAT_JOB_CHANNEL + {"test_null_job", 0, 0, f_test_null_job}, +#endif + {"test_null_list", 0, 0, f_test_null_list}, + {"test_null_partial", 0, 0, f_test_null_partial}, + {"test_null_string", 0, 0, f_test_null_string}, + {"test_settime", 1, 1, f_test_settime}, +#ifdef FEAT_TIMERS + {"timer_start", 2, 3, f_timer_start}, + {"timer_stop", 1, 1, f_timer_stop}, +#endif + {"tolower", 1, 1, f_tolower}, + {"toupper", 1, 1, f_toupper}, + {"tr", 3, 3, f_tr}, +#ifdef FEAT_FLOAT + {"trunc", 1, 1, f_trunc}, +#endif + {"type", 1, 1, f_type}, + {"undofile", 1, 1, f_undofile}, + {"undotree", 0, 0, f_undotree}, + {"uniq", 1, 3, f_uniq}, + {"values", 1, 1, f_values}, + {"virtcol", 1, 1, f_virtcol}, + {"visualmode", 0, 1, f_visualmode}, + {"wildmenumode", 0, 0, f_wildmenumode}, + {"win_findbuf", 1, 1, f_win_findbuf}, + {"win_getid", 0, 2, f_win_getid}, + {"win_gotoid", 1, 1, f_win_gotoid}, + {"win_id2tabwin", 1, 1, f_win_id2tabwin}, + {"win_id2win", 1, 1, f_win_id2win}, + {"winbufnr", 1, 1, f_winbufnr}, + {"wincol", 0, 0, f_wincol}, + {"winheight", 1, 1, f_winheight}, + {"winline", 0, 0, f_winline}, + {"winnr", 0, 1, f_winnr}, + {"winrestcmd", 0, 0, f_winrestcmd}, + {"winrestview", 1, 1, f_winrestview}, + {"winsaveview", 0, 0, f_winsaveview}, + {"winwidth", 1, 1, f_winwidth}, + {"wordcount", 0, 0, f_wordcount}, + {"writefile", 2, 3, f_writefile}, + {"xor", 2, 2, f_xor}, +}; + +#if defined(FEAT_CMDL_COMPL) || defined(PROTO) + +/* + * 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) + return name; + } + if (++intidx < (int)(sizeof(functions) / sizeof(struct fst))) + { + STRCPY(IObuff, functions[intidx].f_name); + STRCAT(IObuff, "("); + if (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); +} + +#endif /* FEAT_CMDL_COMPL */ + +#if defined(EBCDIC) || defined(PROTO) +/* + * Compare struct fst by function name. + */ + static int +compare_func_name(const void *s1, const void *s2) +{ + struct fst *p1 = (struct fst *)s1; + struct fst *p2 = (struct fst *)s2; + + return STRCMP(p1->f_name, p2->f_name); +} + +/* + * Sort the function table by function name. + * The sorting of the table above is ASCII dependant. + * On machines using EBCDIC we have to sort it. + */ + static void +sortFunctions(void) +{ + int funcCnt = (int)(sizeof(functions) / sizeof(struct fst)) - 1; + + qsort(functions, (size_t)funcCnt, sizeof(struct fst), compare_func_name); +} +#endif + + +/* + * Find internal function in table above. + * Return index, or -1 if not found + */ + int +find_internal_func( + char_u *name) /* name of the function */ +{ + int first = 0; + int last = (int)(sizeof(functions) / sizeof(struct fst)) - 1; + int cmp; + int x; + + /* + * Find the function name in the table. Binary search. + */ + while (first <= last) + { + x = first + ((unsigned)(last - first) >> 1); + cmp = STRCMP(name, functions[x].f_name); + if (cmp < 0) + last = x - 1; + else if (cmp > 0) + first = x + 1; + else + return x; + } + 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 ERROR_UNKNOWN; + if (argcount < functions[i].f_min_argc) + return ERROR_TOOFEW; + if (argcount > functions[i].f_max_argc) + return ERROR_TOOMANY; + argvars[argcount].v_type = VAR_UNKNOWN; + functions[i].f_func(argvars, rettv); + return ERROR_NONE; +} + +/* + * Return TRUE for a non-zero Number and a non-empty String. + */ + static 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_SPECIAL + && 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)); +} + +/* + * Get the lnum from the first argument. + * Also accepts ".", "$", etc., but that only works for the current buffer. + * Returns -1 on error. + */ + static linenr_T +get_tv_lnum(typval_T *argvars) +{ + typval_T rettv; + linenr_T lnum; + + lnum = (linenr_T)get_tv_number_chk(&argvars[0], NULL); + if (lnum == 0) /* no valid number, try using line() */ + { + rettv.v_type = VAR_NUMBER; + f_line(argvars, &rettv); + lnum = (linenr_T)rettv.vval.v_number; + clear_tv(&rettv); + } + return lnum; +} + +#ifdef FEAT_FLOAT +static int get_float_arg(typval_T *argvars, float_T *f); + +/* + * Get the float value of "argvars[0]" into "f". + * Returns FAIL when the argument is not a Number or Float. + */ + static int +get_float_arg(typval_T *argvars, float_T *f) +{ + if (argvars[0].v_type == VAR_FLOAT) + { + *f = argvars[0].vval.v_float; + return OK; + } + if (argvars[0].v_type == VAR_NUMBER) + { + *f = (float_T)argvars[0].vval.v_number; + return OK; + } + EMSG(_("E808: Number or Float required")); + return FAIL; +} + +/* + * "abs(expr)" function + */ + static void +f_abs(typval_T *argvars, typval_T *rettv) +{ + if (argvars[0].v_type == VAR_FLOAT) + { + rettv->v_type = VAR_FLOAT; + rettv->vval.v_float = fabs(argvars[0].vval.v_float); + } + else + { + varnumber_T n; + int error = FALSE; + + n = get_tv_number_chk(&argvars[0], &error); + if (error) + rettv->vval.v_number = -1; + else if (n > 0) + rettv->vval.v_number = n; + else + rettv->vval.v_number = -n; + } +} + +/* + * "acos()" function + */ + static void +f_acos(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = acos(f); + else + rettv->vval.v_float = 0.0; +} +#endif + +/* + * "add(list, item)" function + */ + static void +f_add(typval_T *argvars, typval_T *rettv) +{ + list_T *l; + + rettv->vval.v_number = 1; /* Default: Failed */ + if (argvars[0].v_type == VAR_LIST) + { + if ((l = argvars[0].vval.v_list) != NULL + && !tv_check_lock(l->lv_lock, + (char_u *)N_("add() argument"), TRUE) + && list_append_tv(l, &argvars[1]) == OK) + copy_tv(&argvars[0], rettv); + } + else + EMSG(_(e_listreq)); +} + +/* + * "and(expr, expr)" function + */ + static void +f_and(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) + & get_tv_number_chk(&argvars[1], NULL); +} + +/* + * "append(lnum, string/list)" function + */ + static void +f_append(typval_T *argvars, typval_T *rettv) +{ + long lnum; + char_u *line; + list_T *l = NULL; + listitem_T *li = NULL; + typval_T *tv; + long added = 0; + + /* When coming here from Insert mode, sync undo, so that this can be + * undone separately from what was previously inserted. */ + if (u_sync_once == 2) + { + u_sync_once = 1; /* notify that u_sync() was called */ + u_sync(TRUE); + } + + lnum = get_tv_lnum(argvars); + if (lnum >= 0 + && lnum <= curbuf->b_ml.ml_line_count + && u_save(lnum, lnum + 1) == OK) + { + if (argvars[1].v_type == VAR_LIST) + { + l = argvars[1].vval.v_list; + if (l == NULL) + return; + li = l->lv_first; + } + for (;;) + { + if (l == NULL) + tv = &argvars[1]; /* append a string */ + else if (li == NULL) + break; /* end of list */ + else + tv = &li->li_tv; /* append item from list */ + line = get_tv_string_chk(tv); + if (line == NULL) /* type error */ + { + rettv->vval.v_number = 1; /* Failed */ + break; + } + ml_append(lnum + added, line, (colnr_T)0, FALSE); + ++added; + if (l == NULL) + break; + li = li->li_next; + } + + appended_lines_mark(lnum, added); + if (curwin->w_cursor.lnum > lnum) + curwin->w_cursor.lnum += added; + } + else + rettv->vval.v_number = 1; /* Failed */ +} + +/* + * "argc()" function + */ + static void +f_argc(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = ARGCOUNT; +} + +/* + * "argidx()" function + */ + static void +f_argidx(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = curwin->w_arg_idx; +} + +/* + * "arglistid()" function + */ + static void +f_arglistid(typval_T *argvars, typval_T *rettv) +{ + win_T *wp; + + rettv->vval.v_number = -1; + wp = find_tabwin(&argvars[0], &argvars[1]); + if (wp != NULL) + rettv->vval.v_number = wp->w_alist->id; +} + +/* + * "argv(nr)" function + */ + static void +f_argv(typval_T *argvars, typval_T *rettv) +{ + int idx; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + idx = (int)get_tv_number_chk(&argvars[0], NULL); + if (idx >= 0 && idx < ARGCOUNT) + rettv->vval.v_string = vim_strsave(alist_name(&ARGLIST[idx])); + else + rettv->vval.v_string = NULL; + rettv->v_type = VAR_STRING; + } + else if (rettv_list_alloc(rettv) == OK) + for (idx = 0; idx < ARGCOUNT; ++idx) + list_append_string(rettv->vval.v_list, + alist_name(&ARGLIST[idx]), -1); +} + +/* + * "assert_equal(expected, actual[, msg])" function + */ + static void +f_assert_equal(typval_T *argvars, typval_T *rettv UNUSED) +{ + assert_equal_common(argvars, ASSERT_EQUAL); +} + +/* + * "assert_notequal(expected, actual[, msg])" function + */ + static void +f_assert_notequal(typval_T *argvars, typval_T *rettv UNUSED) +{ + assert_equal_common(argvars, ASSERT_NOTEQUAL); +} + +/* + * "assert_exception(string[, msg])" function + */ + static void +f_assert_exception(typval_T *argvars, typval_T *rettv UNUSED) +{ + assert_exception(argvars); +} + +/* + * "assert_fails(cmd [, error])" function + */ + static void +f_assert_fails(typval_T *argvars, typval_T *rettv UNUSED) +{ + assert_fails(argvars); +} + +/* + * "assert_false(actual[, msg])" function + */ + static void +f_assert_false(typval_T *argvars, typval_T *rettv UNUSED) +{ + assert_bool(argvars, FALSE); +} + +/* + * "assert_match(pattern, actual[, msg])" function + */ + static void +f_assert_match(typval_T *argvars, typval_T *rettv UNUSED) +{ + assert_match_common(argvars, ASSERT_MATCH); +} + +/* + * "assert_notmatch(pattern, actual[, msg])" function + */ + static void +f_assert_notmatch(typval_T *argvars, typval_T *rettv UNUSED) +{ + assert_match_common(argvars, ASSERT_NOTMATCH); +} + +/* + * "assert_true(actual[, msg])" function + */ + static void +f_assert_true(typval_T *argvars, typval_T *rettv UNUSED) +{ + assert_bool(argvars, TRUE); +} + +#ifdef FEAT_FLOAT +/* + * "asin()" function + */ + static void +f_asin(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = asin(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "atan()" function + */ + static void +f_atan(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = atan(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "atan2()" function + */ + static void +f_atan2(typval_T *argvars, typval_T *rettv) +{ + float_T fx = 0.0, fy = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &fx) == OK + && get_float_arg(&argvars[1], &fy) == OK) + rettv->vval.v_float = atan2(fx, fy); + else + rettv->vval.v_float = 0.0; +} +#endif + +/* + * "browse(save, title, initdir, default)" function + */ + static void +f_browse(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_BROWSE + int save; + char_u *title; + char_u *initdir; + char_u *defname; + char_u buf[NUMBUFLEN]; + char_u buf2[NUMBUFLEN]; + int error = FALSE; + + save = (int)get_tv_number_chk(&argvars[0], &error); + title = get_tv_string_chk(&argvars[1]); + initdir = get_tv_string_buf_chk(&argvars[2], buf); + defname = get_tv_string_buf_chk(&argvars[3], buf2); + + if (error || title == NULL || initdir == NULL || defname == NULL) + rettv->vval.v_string = NULL; + else + rettv->vval.v_string = + do_browse(save ? BROWSE_SAVE : 0, + title, defname, NULL, initdir, NULL, curbuf); +#else + rettv->vval.v_string = NULL; +#endif + rettv->v_type = VAR_STRING; +} + +/* + * "browsedir(title, initdir)" function + */ + static void +f_browsedir(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_BROWSE + char_u *title; + char_u *initdir; + char_u buf[NUMBUFLEN]; + + title = get_tv_string_chk(&argvars[0]); + initdir = get_tv_string_buf_chk(&argvars[1], buf); + + if (title == NULL || initdir == NULL) + rettv->vval.v_string = NULL; + else + rettv->vval.v_string = do_browse(BROWSE_DIR, + title, NULL, NULL, initdir, NULL, curbuf); +#else + rettv->vval.v_string = NULL; +#endif + rettv->v_type = VAR_STRING; +} + +static buf_T *find_buffer(typval_T *avar); + +/* + * Find a buffer by number or exact name. + */ + static buf_T * +find_buffer(typval_T *avar) +{ + buf_T *buf = NULL; + + if (avar->v_type == VAR_NUMBER) + buf = buflist_findnr((int)avar->vval.v_number); + else if (avar->v_type == VAR_STRING && avar->vval.v_string != NULL) + { + buf = buflist_findname_exp(avar->vval.v_string); + if (buf == NULL) + { + /* No full path name match, try a match with a URL or a "nofile" + * buffer, these don't use the full path. */ + for (buf = firstbuf; buf != NULL; buf = buf->b_next) + if (buf->b_fname != NULL + && (path_with_url(buf->b_fname) +#ifdef FEAT_QUICKFIX + || bt_nofile(buf) +#endif + ) + && STRCMP(buf->b_fname, avar->vval.v_string) == 0) + break; + } + } + return buf; +} + +/* + * "bufexists(expr)" function + */ + static void +f_bufexists(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = (find_buffer(&argvars[0]) != NULL); +} + +/* + * "buflisted(expr)" function + */ + static void +f_buflisted(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf; + + buf = find_buffer(&argvars[0]); + rettv->vval.v_number = (buf != NULL && buf->b_p_bl); +} + +/* + * "bufloaded(expr)" function + */ + static void +f_bufloaded(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf; + + buf = find_buffer(&argvars[0]); + rettv->vval.v_number = (buf != NULL && buf->b_ml.ml_mfp != NULL); +} + + buf_T * +buflist_find_by_name(char_u *name, int curtab_only) +{ + int save_magic; + char_u *save_cpo; + buf_T *buf; + + /* Ignore 'magic' and 'cpoptions' here to make scripts portable */ + save_magic = p_magic; + p_magic = TRUE; + save_cpo = p_cpo; + p_cpo = (char_u *)""; + + buf = buflist_findnr(buflist_findpat(name, name + STRLEN(name), + TRUE, FALSE, curtab_only)); + + p_magic = save_magic; + p_cpo = save_cpo; + return buf; +} + +/* + * Get buffer by number or pattern. + */ + static buf_T * +get_buf_tv(typval_T *tv, int curtab_only) +{ + char_u *name = tv->vval.v_string; + buf_T *buf; + + if (tv->v_type == VAR_NUMBER) + return buflist_findnr((int)tv->vval.v_number); + if (tv->v_type != VAR_STRING) + return NULL; + if (name == NULL || *name == NUL) + return curbuf; + if (name[0] == '$' && name[1] == NUL) + return lastbuf; + + buf = buflist_find_by_name(name, curtab_only); + + /* If not found, try expanding the name, like done for bufexists(). */ + if (buf == NULL) + buf = find_buffer(tv); + + return buf; +} + +/* + * "bufname(expr)" function + */ + static void +f_bufname(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf; + + (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ + ++emsg_off; + buf = get_buf_tv(&argvars[0], FALSE); + rettv->v_type = VAR_STRING; + if (buf != NULL && buf->b_fname != NULL) + rettv->vval.v_string = vim_strsave(buf->b_fname); + else + rettv->vval.v_string = NULL; + --emsg_off; +} + +/* + * "bufnr(expr)" function + */ + static void +f_bufnr(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf; + int error = FALSE; + char_u *name; + + (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ + ++emsg_off; + buf = get_buf_tv(&argvars[0], FALSE); + --emsg_off; + + /* If the buffer isn't found and the second argument is not zero create a + * new buffer. */ + if (buf == NULL + && argvars[1].v_type != VAR_UNKNOWN + && get_tv_number_chk(&argvars[1], &error) != 0 + && !error + && (name = get_tv_string_chk(&argvars[0])) != NULL + && !error) + buf = buflist_new(name, NULL, (linenr_T)1, 0); + + if (buf != NULL) + rettv->vval.v_number = buf->b_fnum; + else + rettv->vval.v_number = -1; +} + + static void +buf_win_common(typval_T *argvars, typval_T *rettv, int get_nr) +{ +#ifdef FEAT_WINDOWS + win_T *wp; + int winnr = 0; +#endif + buf_T *buf; + + (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ + ++emsg_off; + buf = get_buf_tv(&argvars[0], TRUE); +#ifdef FEAT_WINDOWS + for (wp = firstwin; wp; wp = wp->w_next) + { + ++winnr; + if (wp->w_buffer == buf) + break; + } + rettv->vval.v_number = (wp != NULL ? (get_nr ? winnr : wp->w_id) : -1); +#else + rettv->vval.v_number = (curwin->w_buffer == buf + ? (get_nr ? 1 : curwin->w_id) : -1); +#endif + --emsg_off; +} + +/* + * "bufwinid(nr)" function + */ + static void +f_bufwinid(typval_T *argvars, typval_T *rettv) +{ + buf_win_common(argvars, rettv, FALSE); +} + +/* + * "bufwinnr(nr)" function + */ + static void +f_bufwinnr(typval_T *argvars, typval_T *rettv) +{ + buf_win_common(argvars, rettv, TRUE); +} + +/* + * "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; + + boff = get_tv_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 +} + + static void +byteidx(typval_T *argvars, typval_T *rettv, int comp UNUSED) +{ +#ifdef FEAT_MBYTE + char_u *t; +#endif + char_u *str; + varnumber_T idx; + + str = get_tv_string_chk(&argvars[0]); + idx = get_tv_number_chk(&argvars[1], NULL); + rettv->vval.v_number = -1; + if (str == NULL || idx < 0) + return; + +#ifdef FEAT_MBYTE + t = str; + for ( ; idx > 0; idx--) + { + if (*t == NUL) /* EOL reached */ + return; + if (enc_utf8 && comp) + t += utf_ptr2len(t); + else + t += (*mb_ptr2len)(t); + } + rettv->vval.v_number = (varnumber_T)(t - str); +#else + if ((size_t)idx <= STRLEN(str)) + rettv->vval.v_number = idx; +#endif +} + +/* + * "byteidx()" function + */ + static void +f_byteidx(typval_T *argvars, typval_T *rettv) +{ + byteidx(argvars, rettv, FALSE); +} + +/* + * "byteidxcomp()" function + */ + static void +f_byteidxcomp(typval_T *argvars, typval_T *rettv) +{ + byteidx(argvars, rettv, TRUE); +} + +/* + * "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; + + if (argvars[1].v_type != VAR_LIST) + { + EMSG(_(e_listreq)); + 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->pt_name; + } + else + func = get_tv_string(&argvars[0]); + if (*func == NUL) + return; /* type error or empty name */ + + if (argvars[2].v_type != VAR_UNKNOWN) + { + if (argvars[2].v_type != VAR_DICT) + { + EMSG(_(e_dictreq)); + return; + } + selfdict = argvars[2].vval.v_dict; + } + + (void)func_call(func, &argvars[1], partial, selfdict, rettv); +} + +#ifdef FEAT_FLOAT +/* + * "ceil({float})" function + */ + static void +f_ceil(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = ceil(f); + else + rettv->vval.v_float = 0.0; +} +#endif + +#ifdef FEAT_JOB_CHANNEL +/* + * "ch_close()" function + */ + static void +f_ch_close(typval_T *argvars, typval_T *rettv UNUSED) +{ + channel_T *channel = get_channel_arg(&argvars[0], TRUE, FALSE, 0); + + if (channel != NULL) + { + channel_close(channel, FALSE); + channel_clear(channel); + } +} + +/* + * "ch_getbufnr()" function + */ + static void +f_ch_getbufnr(typval_T *argvars, typval_T *rettv) +{ + channel_T *channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0); + + rettv->vval.v_number = -1; + if (channel != NULL) + { + char_u *what = get_tv_string(&argvars[1]); + int part; + + if (STRCMP(what, "err") == 0) + part = PART_ERR; + else if (STRCMP(what, "out") == 0) + part = PART_OUT; + else if (STRCMP(what, "in") == 0) + part = PART_IN; + else + part = PART_SOCK; + if (channel->ch_part[part].ch_bufref.br_buf != NULL) + rettv->vval.v_number = + channel->ch_part[part].ch_bufref.br_buf->b_fnum; + } +} + +/* + * "ch_getjob()" function + */ + static void +f_ch_getjob(typval_T *argvars, typval_T *rettv) +{ + channel_T *channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0); + + if (channel != NULL) + { + rettv->v_type = VAR_JOB; + rettv->vval.v_job = channel->ch_job; + if (channel->ch_job != NULL) + ++channel->ch_job->jv_refcount; + } +} + +/* + * "ch_info()" function + */ + static void +f_ch_info(typval_T *argvars, typval_T *rettv UNUSED) +{ + channel_T *channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0); + + if (channel != NULL && rettv_dict_alloc(rettv) != FAIL) + channel_info(channel, rettv->vval.v_dict); +} + +/* + * "ch_log()" function + */ + static void +f_ch_log(typval_T *argvars, typval_T *rettv UNUSED) +{ + char_u *msg = get_tv_string(&argvars[0]); + channel_T *channel = NULL; + + if (argvars[1].v_type != VAR_UNKNOWN) + channel = get_channel_arg(&argvars[1], FALSE, FALSE, 0); + + ch_log(channel, (char *)msg); +} + +/* + * "ch_logfile()" function + */ + static void +f_ch_logfile(typval_T *argvars, typval_T *rettv UNUSED) +{ + char_u *fname; + char_u *opt = (char_u *)""; + char_u buf[NUMBUFLEN]; + + fname = get_tv_string(&argvars[0]); + if (argvars[1].v_type == VAR_STRING) + opt = get_tv_string_buf(&argvars[1], buf); + ch_logfile(fname, opt); +} + +/* + * "ch_open()" function + */ + static void +f_ch_open(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_CHANNEL; + if (check_restricted() || check_secure()) + return; + rettv->vval.v_channel = channel_open_func(argvars); +} + +/* + * "ch_read()" function + */ + static void +f_ch_read(typval_T *argvars, typval_T *rettv) +{ + common_channel_read(argvars, rettv, FALSE); +} + +/* + * "ch_readraw()" function + */ + static void +f_ch_readraw(typval_T *argvars, typval_T *rettv) +{ + common_channel_read(argvars, rettv, TRUE); +} + +/* + * "ch_evalexpr()" function + */ + static void +f_ch_evalexpr(typval_T *argvars, typval_T *rettv) +{ + ch_expr_common(argvars, rettv, TRUE); +} + +/* + * "ch_sendexpr()" function + */ + static void +f_ch_sendexpr(typval_T *argvars, typval_T *rettv) +{ + ch_expr_common(argvars, rettv, FALSE); +} + +/* + * "ch_evalraw()" function + */ + static void +f_ch_evalraw(typval_T *argvars, typval_T *rettv) +{ + ch_raw_common(argvars, rettv, TRUE); +} + +/* + * "ch_sendraw()" function + */ + static void +f_ch_sendraw(typval_T *argvars, typval_T *rettv) +{ + ch_raw_common(argvars, rettv, FALSE); +} + +/* + * "ch_setoptions()" function + */ + static void +f_ch_setoptions(typval_T *argvars, typval_T *rettv UNUSED) +{ + channel_T *channel; + jobopt_T opt; + + channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0); + if (channel == NULL) + return; + clear_job_options(&opt); + if (get_job_options(&argvars[1], &opt, + JO_CB_ALL + JO_TIMEOUT_ALL + JO_MODE_ALL) == OK) + channel_set_options(channel, &opt); + free_job_options(&opt); +} + +/* + * "ch_status()" function + */ + static void +f_ch_status(typval_T *argvars, typval_T *rettv) +{ + channel_T *channel; + + /* return an empty string by default */ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + channel = get_channel_arg(&argvars[0], FALSE, FALSE, 0); + rettv->vval.v_string = vim_strsave((char_u *)channel_status(channel)); +} +#endif + +/* + * "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) +{ +#ifdef FEAT_MBYTE + if (has_mbyte) + { + int utf8 = 0; + + if (argvars[1].v_type != VAR_UNKNOWN) + utf8 = (int)get_tv_number_chk(&argvars[1], NULL); + + if (utf8) + rettv->vval.v_number = (*utf_ptr2char)(get_tv_string(&argvars[0])); + else + rettv->vval.v_number = (*mb_ptr2char)(get_tv_string(&argvars[0])); + } + else +#endif + rettv->vval.v_number = get_tv_string(&argvars[0])[0]; +} + +/* + * "cindent(lnum)" function + */ + static void +f_cindent(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_CINDENT + pos_T pos; + linenr_T lnum; + + pos = curwin->w_cursor; + lnum = get_tv_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + { + curwin->w_cursor.lnum = lnum; + rettv->vval.v_number = get_c_indent(); + curwin->w_cursor = pos; + } + else +#endif + rettv->vval.v_number = -1; +} + +/* + * "clearmatches()" function + */ + static void +f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_SEARCH_EXTRA + clear_matches(curwin); +#endif +} + +/* + * "col(string)" function + */ + static void +f_col(typval_T *argvars, typval_T *rettv) +{ + colnr_T col = 0; + pos_T *fp; + int fnum = curbuf->b_fnum; + + fp = var2fpos(&argvars[0], FALSE, &fnum); + 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; +#ifdef FEAT_VIRTUALEDIT + /* 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)) + { +# ifdef FEAT_MBYTE + int l; + + if (*p != NUL && p[(l = (*mb_ptr2len)(p))] == NUL) + col += l; +# else + if (*p != NUL && p[1] == NUL) + ++col; +# endif + } + } +#endif + } + } + rettv->vval.v_number = col; +} + +#if defined(FEAT_INS_EXPAND) +/* + * "complete()" function + */ + static void +f_complete(typval_T *argvars, typval_T *rettv UNUSED) +{ + int startcol; + + if ((State & INSERT) == 0) + { + EMSG(_("E785: complete() can only be used in Insert mode")); + return; + } + + /* Check for undo allowed here, because if something was already inserted + * the line was already saved for undo and this check isn't done. */ + if (!undo_allowed()) + return; + + if (argvars[1].v_type != VAR_LIST || argvars[1].vval.v_list == NULL) + { + EMSG(_(e_invarg)); + return; + } + + startcol = (int)get_tv_number_chk(&argvars[0], NULL); + if (startcol <= 0) + return; + + set_completion(startcol - 1, argvars[1].vval.v_list); +} + +/* + * "complete_add()" function + */ + static void +f_complete_add(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = ins_compl_add_tv(&argvars[0], 0); +} + +/* + * "complete_check()" function + */ + static void +f_complete_check(typval_T *argvars UNUSED, typval_T *rettv) +{ + int saved = RedrawingDisabled; + + RedrawingDisabled = 0; + ins_compl_check_keys(0); + rettv->vval.v_number = compl_interrupted; + RedrawingDisabled = saved; +} +#endif + +/* + * "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; + + message = get_tv_string_chk(&argvars[0]); + if (message == NULL) + error = TRUE; + if (argvars[1].v_type != VAR_UNKNOWN) + { + buttons = get_tv_string_buf_chk(&argvars[1], buf); + if (buttons == NULL) + error = TRUE; + if (argvars[2].v_type != VAR_UNKNOWN) + { + def = (int)get_tv_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) + { + typestr = get_tv_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, 0); +} + +#ifdef FEAT_FLOAT +/* + * "cos()" function + */ + static void +f_cos(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = cos(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "cosh()" function + */ + static void +f_cosh(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = cosh(f); + else + rettv->vval.v_float = 0.0; +} +#endif + +/* + * "count()" function + */ + static void +f_count(typval_T *argvars, typval_T *rettv) +{ + long n = 0; + int ic = FALSE; + + if (argvars[0].v_type == VAR_LIST) + { + listitem_T *li; + list_T *l; + long idx; + + if ((l = argvars[0].vval.v_list) != NULL) + { + li = l->lv_first; + if (argvars[2].v_type != VAR_UNKNOWN) + { + int error = FALSE; + + ic = (int)get_tv_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) + { + idx = (long)get_tv_number_chk(&argvars[3], &error); + if (!error) + { + li = list_find(l, idx); + if (li == NULL) + EMSGN(_(e_listidx), idx); + } + } + if (error) + li = NULL; + } + + for ( ; li != NULL; li = li->li_next) + if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE)) + ++n; + } + } + else if (argvars[0].v_type == VAR_DICT) + { + int todo; + dict_T *d; + hashitem_T *hi; + + if ((d = argvars[0].vval.v_dict) != NULL) + { + int error = FALSE; + + if (argvars[2].v_type != VAR_UNKNOWN) + { + ic = (int)get_tv_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) + EMSG(_(e_invarg)); + } + + todo = error ? 0 : (int)d->dv_hashtab.ht_used; + for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + --todo; + if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE)) + ++n; + } + } + } + } + else + EMSG2(_(e_listdictarg), "count()"); + rettv->vval.v_number = n; +} + +/* + * "cscope_connection([{num} , {dbpath} [, {prepend}]])" function + * + * Checks the existence of a cscope connection. + */ + static void +f_cscope_connection(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_CSCOPE + int num = 0; + char_u *dbpath = NULL; + char_u *prepend = NULL; + char_u buf[NUMBUFLEN]; + + if (argvars[0].v_type != VAR_UNKNOWN + && argvars[1].v_type != VAR_UNKNOWN) + { + num = (int)get_tv_number(&argvars[0]); + dbpath = get_tv_string(&argvars[1]); + if (argvars[2].v_type != VAR_UNKNOWN) + prepend = get_tv_string_buf(&argvars[2], buf); + } + + rettv->vval.v_number = cs_connection(num, dbpath, prepend); +#endif +} + +/* + * "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) +{ + long line, col; +#ifdef FEAT_VIRTUALEDIT + long coladd = 0; +#endif + int set_curswant = TRUE; + + rettv->vval.v_number = -1; + if (argvars[1].v_type == VAR_UNKNOWN) + { + pos_T pos; + colnr_T curswant = -1; + + if (list2fpos(argvars, &pos, NULL, &curswant) == FAIL) + { + EMSG(_(e_invarg)); + return; + } + line = pos.lnum; + col = pos.col; +#ifdef FEAT_VIRTUALEDIT + coladd = pos.coladd; +#endif + if (curswant >= 0) + { + curwin->w_curswant = curswant - 1; + set_curswant = FALSE; + } + } + else + { + line = get_tv_lnum(argvars); + col = (long)get_tv_number_chk(&argvars[1], NULL); +#ifdef FEAT_VIRTUALEDIT + if (argvars[2].v_type != VAR_UNKNOWN) + coladd = (long)get_tv_number_chk(&argvars[2], NULL); +#endif + } + if (line < 0 || col < 0 +#ifdef FEAT_VIRTUALEDIT + || coladd < 0 +#endif + ) + return; /* type error; errmsg already given */ + if (line > 0) + curwin->w_cursor.lnum = line; + if (col > 0) + curwin->w_cursor.col = col - 1; +#ifdef FEAT_VIRTUALEDIT + curwin->w_cursor.coladd = coladd; +#endif + + /* Make sure the cursor is in a valid position. */ + check_cursor(); +#ifdef FEAT_MBYTE + /* Correct cursor for multi-byte character. */ + if (has_mbyte) + mb_adjust_cursor(); +#endif + + curwin->w_set_curswant = set_curswant; + rettv->vval.v_number = 0; +} + +/* + * "deepcopy()" function + */ + static void +f_deepcopy(typval_T *argvars, typval_T *rettv) +{ + int noref = 0; + int copyID; + + if (argvars[1].v_type != VAR_UNKNOWN) + noref = (int)get_tv_number_chk(&argvars[1], NULL); + if (noref < 0 || noref > 1) + EMSG(_(e_invarg)); + else + { + copyID = get_copyID(); + item_copy(&argvars[0], rettv, TRUE, noref == 0 ? copyID : 0); + } +} + +/* + * "delete()" function + */ + static void +f_delete(typval_T *argvars, typval_T *rettv) +{ + char_u nbuf[NUMBUFLEN]; + char_u *name; + char_u *flags; + + rettv->vval.v_number = -1; + if (check_restricted() || check_secure()) + return; + + name = get_tv_string(&argvars[0]); + if (name == NULL || *name == NUL) + { + EMSG(_(e_invarg)); + return; + } + + if (argvars[1].v_type != VAR_UNKNOWN) + flags = get_tv_string_buf(&argvars[1], nbuf); + else + flags = (char_u *)""; + + if (*flags == NUL) + /* delete a file */ + rettv->vval.v_number = mch_remove(name) == 0 ? 0 : -1; + else if (STRCMP(flags, "d") == 0) + /* delete an empty directory */ + rettv->vval.v_number = mch_rmdir(name) == 0 ? 0 : -1; + else if (STRCMP(flags, "rf") == 0) + /* delete a directory recursively */ + rettv->vval.v_number = delete_recursive(name); + else + EMSG2(_(e_invexpr2), flags); +} + +/* + * "did_filetype()" function + */ + static void +f_did_filetype(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_AUTOCMD + rettv->vval.v_number = did_filetype; +#endif +} + +/* + * "diff_filler()" function + */ + static void +f_diff_filler(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_DIFF + rettv->vval.v_number = diff_check_fill(curwin, get_tv_lnum(argvars)); +#endif +} + +/* + * "diff_hlID()" function + */ + static void +f_diff_hlID(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_DIFF + linenr_T lnum = get_tv_lnum(argvars); + static linenr_T prev_lnum = 0; + static int changedtick = 0; + static int fnum = 0; + static int change_start = 0; + static int change_end = 0; + static hlf_T hlID = (hlf_T)0; + int filler_lines; + int col; + + if (lnum < 0) /* ignore type error in {lnum} arg */ + lnum = 0; + if (lnum != prev_lnum + || changedtick != curbuf->b_changedtick + || fnum != curbuf->b_fnum) + { + /* New line, buffer, change: need to get the values. */ + filler_lines = diff_check(curwin, lnum); + if (filler_lines < 0) + { + if (filler_lines == -1) + { + change_start = MAXCOL; + change_end = -1; + if (diff_find_change(curwin, lnum, &change_start, &change_end)) + hlID = HLF_ADD; /* added line */ + else + hlID = HLF_CHD; /* changed line */ + } + else + hlID = HLF_ADD; /* added line */ + } + else + hlID = (hlf_T)0; + prev_lnum = lnum; + changedtick = curbuf->b_changedtick; + fnum = curbuf->b_fnum; + } + + if (hlID == HLF_CHD || hlID == HLF_TXD) + { + col = get_tv_number(&argvars[1]) - 1; /* ignore type error in {col} */ + if (col >= change_start && col <= change_end) + hlID = HLF_TXD; /* changed text */ + else + hlID = HLF_CHD; /* changed line */ + } + rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)hlID; +#endif +} + +/* + * "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_first == NULL; + break; + case VAR_DICT: + n = argvars[0].vval.v_dict == NULL + || argvars[0].vval.v_dict->dv_hashtab.ht_used == 0; + break; + case VAR_SPECIAL: + n = argvars[0].vval.v_number != VVAL_TRUE; + 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: + EMSG2(_(e_intern2), "f_empty(UNKNOWN)"); + n = TRUE; + break; + } + + rettv->vval.v_number = n; +} + +/* + * "escape({string}, {chars})" function + */ + static void +f_escape(typval_T *argvars, typval_T *rettv) +{ + char_u buf[NUMBUFLEN]; + + rettv->vval.v_string = vim_strsave_escaped(get_tv_string(&argvars[0]), + get_tv_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; + + s = get_tv_string_chk(&argvars[0]); + if (s != NULL) + s = skipwhite(s); + + p = s; + if (s == NULL || eval1(&s, rettv, TRUE) == FAIL) + { + if (p != NULL && !aborting()) + EMSG2(_(e_invexpr2), p); + need_clr_eos = FALSE; + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + } + else if (*s != NUL) + EMSG(_(e_trailing)); +} + +/* + * "eventhandler()" function + */ + static void +f_eventhandler(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = vgetc_busy; +} + +/* + * "executable()" function + */ + static void +f_executable(typval_T *argvars, typval_T *rettv) +{ + char_u *name = get_tv_string(&argvars[0]); + + /* Check in $PATH and also check directly if there is a directory name. */ + rettv->vval.v_number = mch_can_exe(name, NULL, TRUE) + || (gettail(name) != name && mch_can_exe(name, NULL, FALSE)); +} + +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; + } +} + +/* + * Get next line from a list. + * Called by do_cmdline() to get the next line. + * Returns allocated string, or NULL for end of function. + */ + + static char_u * +get_list_line( + int c UNUSED, + void *cookie, + int indent UNUSED) +{ + listitem_T **p = (listitem_T **)cookie; + listitem_T *item = *p; + char_u buf[NUMBUFLEN]; + char_u *s; + + if (item == NULL) + return NULL; + s = get_tv_string_buf_chk(&item->li_tv, buf); + *p = item->li_next; + return s == NULL ? NULL : vim_strsave(s); +} + +/* + * "execute()" function + */ + static void +f_execute(typval_T *argvars, typval_T *rettv) +{ + 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; + garray_T save_ga; + + rettv->vval.v_string = NULL; + rettv->v_type = VAR_STRING; + + if (argvars[0].v_type == VAR_LIST) + { + list = argvars[0].vval.v_list; + if (list == NULL || list->lv_first == NULL) + /* empty list, no commands, empty output */ + return; + ++list->lv_refcount; + } + else + { + cmd = get_tv_string_chk(&argvars[0]); + if (cmd == NULL) + return; + } + + if (argvars[1].v_type != VAR_UNKNOWN) + { + char_u buf[NUMBUFLEN]; + char_u *s = get_tv_string_buf_chk(&argvars[1], buf); + + if (s == NULL) + return; + 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, (int)sizeof(char), 500); + redir_execute = TRUE; + + if (cmd != NULL) + do_cmdline_cmd(cmd); + else + { + listitem_T *item = list->lv_first; + + do_cmdline(NULL, get_list_line, (void *)&item, + DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); + --list->lv_refcount; + } + + rettv->vval.v_string = redir_execute_ga.ga_data; + 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; + + /* "silent reg" or "silent echo x" leaves msg_col somewhere in the + * line. Put it back in the first column. */ + msg_col = 0; +} + +/* + * "exepath()" function + */ + static void +f_exepath(typval_T *argvars, typval_T *rettv) +{ + char_u *p = NULL; + + (void)mch_can_exe(get_tv_string(&argvars[0]), &p, TRUE); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = p; +} + +/* + * "exists()" function + */ + static void +f_exists(typval_T *argvars, typval_T *rettv) +{ + char_u *p; + char_u *name; + int n = FALSE; + int len = 0; + + p = get_tv_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 = (get_option_tv(&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); + } + else if (*p == ':') + { + n = cmd_exists(p + 1); + } + else if (*p == '#') + { +#ifdef FEAT_AUTOCMD + if (p[1] == '#') + n = autocmd_supported(p + 2); + else + n = au_exists(p + 1); +#endif + } + else /* internal variable */ + { + char_u *tofree; + typval_T tv; + + /* get_name_len() takes care of expanding curly braces */ + name = p; + len = get_name_len(&p, &tofree, TRUE, FALSE); + if (len > 0) + { + if (tofree != NULL) + name = tofree; + n = (get_var_tv(name, len, &tv, NULL, FALSE, TRUE) == OK); + if (n) + { + /* handle d.key, l[idx], f(expr) */ + n = (handle_subscript(&p, &tv, TRUE, FALSE) == OK); + if (n) + clear_tv(&tv); + } + } + if (*p != NUL) + n = FALSE; + + vim_free(tofree); + } + + rettv->vval.v_number = n; +} + +#ifdef FEAT_FLOAT +/* + * "exp()" function + */ + static void +f_exp(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = exp(f); + else + rettv->vval.v_float = 0.0; +} +#endif + +/* + * "expand()" function + */ + static void +f_expand(typval_T *argvars, typval_T *rettv) +{ + char_u *s; + int len; + char_u *errormsg; + int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND; + expand_T xpc; + int error = FALSE; + char_u *result; + + rettv->v_type = VAR_STRING; + if (argvars[1].v_type != VAR_UNKNOWN + && argvars[2].v_type != VAR_UNKNOWN + && get_tv_number_chk(&argvars[2], &error) + && !error) + { + rettv->v_type = VAR_LIST; + rettv->vval.v_list = NULL; + } + + s = get_tv_string(&argvars[0]); + if (*s == '%' || *s == '#' || *s == '<') + { + ++emsg_off; + result = eval_vars(s, s, &len, NULL, &errormsg, NULL); + --emsg_off; + if (rettv->v_type == VAR_LIST) + { + if (rettv_list_alloc(rettv) != FAIL && result != NULL) + list_append_string(rettv->vval.v_list, result, -1); + else + 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 + && get_tv_number_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) != FAIL) + { + 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; + } +} + +/* + * "extend(list, list [, idx])" function + * "extend(dict, dict [, action])" function + */ + static void +f_extend(typval_T *argvars, typval_T *rettv) +{ + char_u *arg_errmsg = (char_u *)N_("extend() argument"); + + if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) + { + list_T *l1, *l2; + listitem_T *item; + long before; + int error = FALSE; + + l1 = argvars[0].vval.v_list; + l2 = argvars[1].vval.v_list; + if (l1 != NULL && !tv_check_lock(l1->lv_lock, arg_errmsg, TRUE) + && l2 != NULL) + { + if (argvars[2].v_type != VAR_UNKNOWN) + { + before = (long)get_tv_number_chk(&argvars[2], &error); + if (error) + return; /* type error; errmsg already given */ + + if (before == l1->lv_len) + item = NULL; + else + { + item = list_find(l1, before); + if (item == NULL) + { + EMSGN(_(e_listidx), before); + return; + } + } + } + else + item = NULL; + list_extend(l1, l2, item); + + copy_tv(&argvars[0], rettv); + } + } + else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) + { + dict_T *d1, *d2; + char_u *action; + int i; + + d1 = argvars[0].vval.v_dict; + d2 = argvars[1].vval.v_dict; + if (d1 != NULL && !tv_check_lock(d1->dv_lock, arg_errmsg, TRUE) + && d2 != NULL) + { + /* Check the third argument. */ + if (argvars[2].v_type != VAR_UNKNOWN) + { + static char *(av[]) = {"keep", "force", "error"}; + + action = get_tv_string_chk(&argvars[2]); + if (action == NULL) + return; /* type error; errmsg already given */ + for (i = 0; i < 3; ++i) + if (STRCMP(action, av[i]) == 0) + break; + if (i == 3) + { + EMSG2(_(e_invarg2), action); + return; + } + } + else + action = (char_u *)"force"; + + dict_extend(d1, d2, action); + + copy_tv(&argvars[0], rettv); + } + } + else + EMSG2(_(e_listdictarg), "extend()"); +} + +/* + * "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 dangerous = 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; + + keys = get_tv_string(&argvars[0]); + + if (argvars[1].v_type != VAR_UNKNOWN) + { + flags = get_tv_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 '!': dangerous = 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) + { + ins_typebuf(keys_esc, (remap ? REMAP_YES : REMAP_NONE), + insert ? 0 : typebuf.tb_len, !typed, FALSE); + vim_free(keys_esc); + if (vgetc_busy) + typebuf_was_filled = TRUE; + if (execute) + { + int save_msg_scroll = msg_scroll; + + /* Avoid a 1 second delay when the keys start Insert mode. */ + msg_scroll = FALSE; + + if (!dangerous) + ++ex_normal_busy; + exec_normal(TRUE); + if (!dangerous) + --ex_normal_busy; + msg_scroll |= save_msg_scroll; + } + } + } +} + +/* + * "filereadable()" function + */ + static void +f_filereadable(typval_T *argvars, typval_T *rettv) +{ + int fd; + char_u *p; + int n; + +#ifndef O_NONBLOCK +# define O_NONBLOCK 0 +#endif + p = get_tv_string(&argvars[0]); + if (*p && !mch_isdir(p) && (fd = mch_open((char *)p, + O_RDONLY | O_NONBLOCK, 0)) >= 0) + { + n = TRUE; + close(fd); + } + else + n = FALSE; + + rettv->vval.v_number = n; +} + +/* + * Return 0 for not writable, 1 for writable file, 2 for a dir which we have + * rights to write into. + */ + static void +f_filewritable(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = filewritable(get_tv_string(&argvars[0])); +} + + static void +findfilendir( + typval_T *argvars UNUSED, + typval_T *rettv, + int find_what UNUSED) +{ +#ifdef FEAT_SEARCHPATH + char_u *fname; + char_u *fresult = NULL; + char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; + char_u *p; + char_u pathbuf[NUMBUFLEN]; + int count = 1; + int first = TRUE; + int error = FALSE; +#endif + + rettv->vval.v_string = NULL; + rettv->v_type = VAR_STRING; + +#ifdef FEAT_SEARCHPATH + fname = get_tv_string(&argvars[0]); + + if (argvars[1].v_type != VAR_UNKNOWN) + { + p = get_tv_string_buf_chk(&argvars[1], pathbuf); + if (p == NULL) + error = TRUE; + else + { + if (*p != NUL) + path = p; + + if (argvars[2].v_type != VAR_UNKNOWN) + count = (int)get_tv_number_chk(&argvars[2], &error); + } + } + + if (count < 0 && rettv_list_alloc(rettv) == FAIL) + error = TRUE; + + if (*fname != NUL && !error) + { + do + { + if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) + vim_free(fresult); + fresult = find_file_in_path_option(first ? fname : NULL, + first ? (int)STRLEN(fname) : 0, + 0, first, path, + find_what, + curbuf->b_ffname, + find_what == FINDFILE_DIR + ? (char_u *)"" : curbuf->b_p_sua); + first = FALSE; + + if (fresult != NULL && rettv->v_type == VAR_LIST) + list_append_string(rettv->vval.v_list, fresult, -1); + + } while ((rettv->v_type == VAR_LIST || --count > 0) && fresult != NULL); + } + + if (rettv->v_type == VAR_STRING) + rettv->vval.v_string = fresult; +#endif +} + +/* + * "filter()" function + */ + static void +f_filter(typval_T *argvars, typval_T *rettv) +{ + filter_map(argvars, rettv, FALSE); +} + +/* + * "finddir({fname}[, {path}[, {count}]])" function + */ + static void +f_finddir(typval_T *argvars, typval_T *rettv) +{ + findfilendir(argvars, rettv, FINDFILE_DIR); +} + +/* + * "findfile({fname}[, {path}[, {count}]])" function + */ + static void +f_findfile(typval_T *argvars, typval_T *rettv) +{ + findfilendir(argvars, rettv, FINDFILE_FILE); +} + +#ifdef FEAT_FLOAT +/* + * "float2nr({float})" function + */ + static void +f_float2nr(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + if (get_float_arg(argvars, &f) == OK) + { +# ifdef FEAT_NUM64 + if (f < -0x7fffffffffffffff) + rettv->vval.v_number = -0x7fffffffffffffff; + else if (f > 0x7fffffffffffffff) + rettv->vval.v_number = 0x7fffffffffffffff; + else + rettv->vval.v_number = (varnumber_T)f; +# else + if (f < -0x7fffffff) + rettv->vval.v_number = -0x7fffffff; + else if (f > 0x7fffffff) + rettv->vval.v_number = 0x7fffffff; + else + rettv->vval.v_number = (varnumber_T)f; +# endif + } +} + +/* + * "floor({float})" function + */ + static void +f_floor(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = floor(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "fmod()" function + */ + static void +f_fmod(typval_T *argvars, typval_T *rettv) +{ + float_T fx = 0.0, fy = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &fx) == OK + && get_float_arg(&argvars[1], &fy) == OK) + rettv->vval.v_float = fmod(fx, fy); + else + rettv->vval.v_float = 0.0; +} +#endif + +/* + * "fnameescape({string})" function + */ + static void +f_fnameescape(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_string = vim_strsave_fnameescape( + get_tv_string(&argvars[0]), FALSE); + rettv->v_type = VAR_STRING; +} + +/* + * "fnamemodify({fname}, {mods})" function + */ + static void +f_fnamemodify(typval_T *argvars, typval_T *rettv) +{ + char_u *fname; + char_u *mods; + int usedlen = 0; + int len; + char_u *fbuf = NULL; + char_u buf[NUMBUFLEN]; + + fname = get_tv_string_chk(&argvars[0]); + mods = get_tv_string_buf_chk(&argvars[1], buf); + if (fname == NULL || mods == NULL) + fname = NULL; + else + { + len = (int)STRLEN(fname); + (void)modify_fname(mods, &usedlen, &fname, &fbuf, &len); + } + + rettv->v_type = VAR_STRING; + if (fname == NULL) + rettv->vval.v_string = NULL; + else + rettv->vval.v_string = vim_strnsave(fname, len); + vim_free(fbuf); +} + +static void foldclosed_both(typval_T *argvars, typval_T *rettv, int end); + +/* + * "foldclosed()" function + */ + static void +foldclosed_both( + typval_T *argvars UNUSED, + typval_T *rettv, + int end UNUSED) +{ +#ifdef FEAT_FOLDING + linenr_T lnum; + linenr_T first, last; + + lnum = get_tv_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + { + if (hasFoldingWin(curwin, lnum, &first, &last, FALSE, NULL)) + { + if (end) + rettv->vval.v_number = (varnumber_T)last; + else + rettv->vval.v_number = (varnumber_T)first; + return; + } + } +#endif + rettv->vval.v_number = -1; +} + +/* + * "foldclosed()" function + */ + static void +f_foldclosed(typval_T *argvars, typval_T *rettv) +{ + foldclosed_both(argvars, rettv, FALSE); +} + +/* + * "foldclosedend()" function + */ + static void +f_foldclosedend(typval_T *argvars, typval_T *rettv) +{ + foldclosed_both(argvars, rettv, TRUE); +} + +/* + * "foldlevel()" function + */ + static void +f_foldlevel(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_FOLDING + linenr_T lnum; + + lnum = get_tv_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + rettv->vval.v_number = foldLevel(lnum); +#endif +} + +/* + * "foldtext()" function + */ + static void +f_foldtext(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_FOLDING + linenr_T foldstart; + linenr_T foldend; + char_u *dashes; + linenr_T lnum; + char_u *s; + char_u *r; + int len; + char *txt; +#endif + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; +#ifdef FEAT_FOLDING + foldstart = (linenr_T)get_vim_var_nr(VV_FOLDSTART); + foldend = (linenr_T)get_vim_var_nr(VV_FOLDEND); + dashes = get_vim_var_str(VV_FOLDDASHES); + if (foldstart > 0 && foldend <= curbuf->b_ml.ml_line_count + && dashes != NULL) + { + /* Find first non-empty line in the fold. */ + while (lnum < (linenr_T)get_vim_var_nr(VV_FOLDEND)) + { + if (!linewhite(lnum)) + break; + ++lnum; + } + + /* Find interesting text in this line. */ + s = skipwhite(ml_get(lnum)); + /* skip C comment-start */ + if (s[0] == '/' && (s[1] == '*' || s[1] == '/')) + { + s = skipwhite(s + 2); + if (*skipwhite(s) == NUL + && lnum + 1 < (linenr_T)get_vim_var_nr(VV_FOLDEND)) + { + s = skipwhite(ml_get(lnum + 1)); + if (*s == '*') + s = skipwhite(s + 1); + } + } + txt = _("+-%s%3ld lines: "); + r = alloc((unsigned)(STRLEN(txt) + + STRLEN(dashes) /* for %s */ + + 20 /* for %3ld */ + + STRLEN(s))); /* concatenated */ + if (r != NULL) + { + sprintf((char *)r, txt, dashes, (long)(foldend - foldstart + 1)); + len = (int)STRLEN(r); + STRCAT(r, s); + /* remove 'foldmarker' and 'commentstring' */ + foldtext_cleanup(r + len); + rettv->vval.v_string = r; + } + } +#endif +} + +/* + * "foldtextresult(lnum)" function + */ + static void +f_foldtextresult(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_FOLDING + linenr_T lnum; + char_u *text; + char_u buf[51]; + foldinfo_T foldinfo; + int fold_count; +#endif + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; +#ifdef FEAT_FOLDING + lnum = get_tv_lnum(argvars); + /* treat illegal types and illegal string values for {lnum} the same */ + if (lnum < 0) + lnum = 0; + fold_count = foldedCount(curwin, lnum, &foldinfo); + if (fold_count > 0) + { + text = get_foldtext(curwin, lnum, lnum + fold_count - 1, + &foldinfo, buf); + if (text == buf) + text = vim_strsave(text); + rettv->vval.v_string = text; + } +#endif +} + +/* + * "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(); +#else +# ifdef WIN32 + win32_set_foreground(); +# endif +#endif +} + +/* + * "function()" function + */ + static void +f_function(typval_T *argvars, typval_T *rettv) +{ + char_u *s; + char_u *name; + int use_string = FALSE; + partial_T *arg_pt = NULL; + + 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 = arg_pt->pt_name; + } + else + { + /* function('MyFunc', [arg], dict) */ + s = get_tv_string(&argvars[0]); + use_string = TRUE; + } + + if (s == NULL || *s == NUL || (use_string && VIM_ISDIGIT(*s))) + EMSG2(_(e_invarg2), s); + /* Don't check an autoload name for existence here. */ + else if (use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL + && !function_exists(s)) + EMSG2(_("E700: Unknown function: %s"), s); + else + { + int dict_idx = 0; + int arg_idx = 0; + list_T *list = NULL; + + if (STRNCMP(s, "s:", 2) == 0 || STRNCMP(s, "", 5) == 0) + { + char sid_buf[25]; + int off = *s == 's' ? 2 : 5; + + /* Expand s: and into 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. */ + sprintf(sid_buf, "%ld_", (long)current_SID); + name = alloc((int)(STRLEN(sid_buf) + STRLEN(s + off) + 1)); + if (name != NULL) + { + STRCPY(name, sid_buf); + STRCAT(name, s + off); + } + } + 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(_("E922: expected a dict")); + vim_free(name); + return; + } + if (argvars[dict_idx].vval.v_dict == NULL) + dict_idx = 0; + } + if (arg_idx > 0) + { + if (argvars[arg_idx].v_type != VAR_LIST) + { + EMSG(_("E923: Second argument of function() must be a list or a dict")); + vim_free(name); + return; + } + list = argvars[arg_idx].vval.v_list; + if (list == NULL || list->lv_len == 0) + arg_idx = 0; + } + } + if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL) + { + partial_T *pt = (partial_T *)alloc_clear(sizeof(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 = (typval_T *)alloc( + sizeof(typval_T) * pt->pt_argc); + if (pt->pt_argv == NULL) + { + vim_free(pt); + vim_free(name); + return; + } + else + { + for (i = 0; i < arg_len; i++) + copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]); + if (lv_len > 0) + for (li = list->lv_first; li != NULL; + li = li->li_next) + 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; + pt->pt_name = name; + func_ref(pt->pt_name); + } + 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); + } + } +} + +/* + * "garbagecollect()" function + */ + static void +f_garbagecollect(typval_T *argvars, typval_T *rettv UNUSED) +{ + /* 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 && get_tv_number(&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; + + if (argvars[0].v_type == VAR_LIST) + { + if ((l = argvars[0].vval.v_list) != NULL) + { + int error = FALSE; + + li = list_find(l, (long)get_tv_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, get_tv_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 + { + vim_memset(&fref_pt, 0, sizeof(fref_pt)); + fref_pt.pt_name = argvars[0].vval.v_string; + pt = &fref_pt; + } + + if (pt != NULL) + { + char_u *what = get_tv_string(&argvars[1]); + + if (STRCMP(what, "func") == 0 || STRCMP(what, "name") == 0) + { + rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING); + if (pt->pt_name == NULL) + rettv->vval.v_string = NULL; + else + rettv->vval.v_string = vim_strsave(pt->pt_name); + } + else if (STRCMP(what, "dict") == 0) + { + rettv->v_type = VAR_DICT; + rettv->vval.v_dict = pt->pt_dict; + if (pt->pt_dict != NULL) + ++pt->pt_dict->dv_refcount; + } + 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 + EMSG2(_(e_invarg2), what); + return; + } + } + else + EMSG2(_(e_listdictarg), "get()"); + + if (tv == NULL) + { + if (argvars[2].v_type != VAR_UNKNOWN) + copy_tv(&argvars[2], rettv); + } + else + copy_tv(tv, rettv); +} + +static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retlist, typval_T *rettv); + +/* + * Get line or list of lines from buffer "buf" into "rettv". + * Return a range (from start to end) of lines in rettv from the specified + * buffer. + * If 'retlist' is TRUE, then the lines are returned as a Vim List. + */ + static void +get_buffer_lines( + buf_T *buf, + linenr_T start, + linenr_T end, + int retlist, + typval_T *rettv) +{ + char_u *p; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + if (retlist && rettv_list_alloc(rettv) == FAIL) + return; + + if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0) + return; + + if (!retlist) + { + if (start >= 1 && start <= buf->b_ml.ml_line_count) + p = ml_get_buf(buf, start, FALSE); + else + p = (char_u *)""; + rettv->vval.v_string = vim_strsave(p); + } + else + { + if (end < start) + return; + + if (start < 1) + start = 1; + if (end > buf->b_ml.ml_line_count) + end = buf->b_ml.ml_line_count; + while (start <= end) + if (list_append_string(rettv->vval.v_list, + ml_get_buf(buf, start++, FALSE), -1) == FAIL) + break; + } +} + +/* + * Get the lnum from the first argument. + * Also accepts "$", then "buf" is used. + * Returns 0 on error. + */ + static linenr_T +get_tv_lnum_buf(typval_T *argvars, buf_T *buf) +{ + if (argvars[0].v_type == VAR_STRING + && argvars[0].vval.v_string != NULL + && argvars[0].vval.v_string[0] == '$' + && buf != NULL) + return buf->b_ml.ml_line_count; + return (linenr_T)get_tv_number_chk(&argvars[0], NULL); +} + +/* + * "getbufline()" function + */ + static void +f_getbufline(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum; + linenr_T end; + buf_T *buf; + + (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ + ++emsg_off; + buf = get_buf_tv(&argvars[0], FALSE); + --emsg_off; + + lnum = get_tv_lnum_buf(&argvars[1], buf); + if (argvars[2].v_type == VAR_UNKNOWN) + end = lnum; + else + end = get_tv_lnum_buf(&argvars[2], buf); + + get_buffer_lines(buf, lnum, end, TRUE, rettv); +} + +/* + * "getbufvar()" function + */ + static void +f_getbufvar(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf; + buf_T *save_curbuf; + char_u *varname; + dictitem_T *v; + int done = FALSE; + + (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ + varname = get_tv_string_chk(&argvars[1]); + ++emsg_off; + buf = get_buf_tv(&argvars[0], FALSE); + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + if (buf != NULL && varname != NULL) + { + /* set curbuf to be our buf, temporarily */ + save_curbuf = curbuf; + curbuf = buf; + + if (*varname == '&') /* buffer-local-option */ + { + if (get_option_tv(&varname, rettv, TRUE) == OK) + done = TRUE; + } + else if (STRCMP(varname, "changedtick") == 0) + { + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = curbuf->b_changedtick; + done = TRUE; + } + else + { + /* Look up the variable. */ + /* Let getbufvar({nr}, "") return the "b:" dictionary. */ + v = find_var_in_ht(&curbuf->b_vars->dv_hashtab, + 'b', varname, FALSE); + if (v != NULL) + { + copy_tv(&v->di_tv, rettv); + done = TRUE; + } + } + + /* restore previous notion of curbuf */ + curbuf = save_curbuf; + } + + if (!done && argvars[2].v_type != VAR_UNKNOWN) + /* use the default value */ + copy_tv(&argvars[2], rettv); + + --emsg_off; +} + +/* + * "getchar()" function + */ + static void +f_getchar(typval_T *argvars, typval_T *rettv) +{ + varnumber_T n; + int error = FALSE; + + /* Position the cursor. Needed after a message that ends in a space. */ + windgoto(msg_row, msg_col); + + ++no_mapping; + ++allow_keys; + for (;;) + { + if (argvars[0].v_type == VAR_UNKNOWN) + /* getchar(): blocking wait. */ + n = safe_vgetc(); + else if (get_tv_number_chk(&argvars[0], &error) == 1) + /* getchar(1): only check if char avail */ + n = vpeekc_any(); + else if (error || vpeekc_any() == NUL) + /* illegal argument or getchar(0) and no char avail: return zero */ + n = 0; + else + /* getchar(0) and char avail: return char */ + n = safe_vgetc(); + + if (n == K_IGNORE) + continue; + break; + } + --no_mapping; + --allow_keys; + + set_vim_var_nr(VV_MOUSE_WIN, 0); + set_vim_var_nr(VV_MOUSE_WINID, 0); + set_vim_var_nr(VV_MOUSE_LNUM, 0); + set_vim_var_nr(VV_MOUSE_COL, 0); + + rettv->vval.v_number = n; + if (IS_SPECIAL(n) || mod_mask != 0) + { + char_u temp[10]; /* modifier: 3, mbyte-char: 6, NUL: 1 */ + int i = 0; + + /* Turn a special key into three bytes, plus modifier. */ + if (mod_mask != 0) + { + temp[i++] = K_SPECIAL; + temp[i++] = KS_MODIFIER; + temp[i++] = mod_mask; + } + if (IS_SPECIAL(n)) + { + temp[i++] = K_SPECIAL; + temp[i++] = K_SECOND(n); + temp[i++] = K_THIRD(n); + } +#ifdef FEAT_MBYTE + else if (has_mbyte) + i += (*mb_char2bytes)(n, temp + i); +#endif + else + temp[i++] = n; + temp[i++] = NUL; + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave(temp); + +#ifdef FEAT_MOUSE + if (is_mouse_key(n)) + { + int row = mouse_row; + int col = mouse_col; + win_T *win; + linenr_T lnum; +# ifdef FEAT_WINDOWS + win_T *wp; +# endif + int winnr = 1; + + if (row >= 0 && col >= 0) + { + /* Find the window at the mouse coordinates and compute the + * text position. */ + win = mouse_find_win(&row, &col); + (void)mouse_comp_pos(win, &row, &col, &lnum); +# ifdef FEAT_WINDOWS + for (wp = firstwin; wp != win; wp = wp->w_next) + ++winnr; +# endif + set_vim_var_nr(VV_MOUSE_WIN, winnr); + set_vim_var_nr(VV_MOUSE_WINID, win->w_id); + set_vim_var_nr(VV_MOUSE_LNUM, lnum); + set_vim_var_nr(VV_MOUSE_COL, col + 1); + } + } +#endif + } +} + +/* + * "getcharmod()" function + */ + static void +f_getcharmod(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = mod_mask; +} + +/* + * "getcharsearch()" function + */ + static void +f_getcharsearch(typval_T *argvars UNUSED, typval_T *rettv) +{ + if (rettv_dict_alloc(rettv) != FAIL) + { + dict_T *dict = rettv->vval.v_dict; + + dict_add_nr_str(dict, "char", 0L, last_csearch()); + dict_add_nr_str(dict, "forward", last_csearch_forward(), NULL); + dict_add_nr_str(dict, "until", last_csearch_until(), NULL); + } +} + +/* + * "getcmdline()" function + */ + static void +f_getcmdline(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = get_cmdline_str(); +} + +/* + * "getcmdpos()" function + */ + static void +f_getcmdpos(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = get_cmdline_pos() + 1; +} + +/* + * "getcmdtype()" function + */ + static void +f_getcmdtype(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = alloc(2); + if (rettv->vval.v_string != NULL) + { + rettv->vval.v_string[0] = get_cmdline_type(); + rettv->vval.v_string[1] = NUL; + } +} + +/* + * "getcmdwintype()" function + */ + static void +f_getcmdwintype(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; +#ifdef FEAT_CMDWIN + rettv->vval.v_string = alloc(2); + if (rettv->vval.v_string != NULL) + { + rettv->vval.v_string[0] = cmdwin_type; + rettv->vval.v_string[1] = NUL; + } +#endif +} + +#if defined(FEAT_CMDL_COMPL) +/* + * "getcompletion()" function + */ + static void +f_getcompletion(typval_T *argvars, typval_T *rettv) +{ + char_u *pat; + expand_T xpc; + int options = WILD_KEEP_ALL | WILD_SILENT | WILD_USE_NL + | WILD_LIST_NOTFOUND | WILD_NO_BEEP; + + if (p_wic) + options |= WILD_ICASE; + + ExpandInit(&xpc); + xpc.xp_pattern = get_tv_string(&argvars[0]); + xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); + xpc.xp_context = cmdcomplete_str_to_type(get_tv_string(&argvars[1])); + if (xpc.xp_context == EXPAND_NOTHING) + { + if (argvars[1].v_type == VAR_STRING) + EMSG2(_(e_invarg2), argvars[1].vval.v_string); + else + EMSG(_(e_invarg)); + return; + } + +# if defined(FEAT_MENU) + if (xpc.xp_context == EXPAND_MENUS) + { + set_context_in_menu_cmd(&xpc, (char_u *)"menu", xpc.xp_pattern, FALSE); + xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); + } +# endif + + pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context); + if ((rettv_list_alloc(rettv) != FAIL) && (pat != NULL)) + { + int i; + + ExpandOne(&xpc, pat, 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); + } + vim_free(pat); + ExpandCleanup(&xpc); +} +#endif + +/* + * "getcwd()" function + */ + static void +f_getcwd(typval_T *argvars, typval_T *rettv) +{ + win_T *wp = NULL; + char_u *cwd; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + wp = find_tabwin(&argvars[0], &argvars[1]); + if (wp != NULL) + { + if (wp->w_localdir != NULL) + rettv->vval.v_string = vim_strsave(wp->w_localdir); + else if (globaldir != NULL) + rettv->vval.v_string = vim_strsave(globaldir); + else + { + cwd = alloc(MAXPATHL); + if (cwd != NULL) + { + if (mch_dirname(cwd, MAXPATHL) != FAIL) + rettv->vval.v_string = vim_strsave(cwd); + vim_free(cwd); + } + } +#ifdef BACKSLASH_IN_FILENAME + if (rettv->vval.v_string != NULL) + slash_adjust(rettv->vval.v_string); +#endif + } +} + +/* + * "getfontname()" function + */ + static void +f_getfontname(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; +#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 = get_tv_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 +} + +/* + * "getfperm({fname})" function + */ + static void +f_getfperm(typval_T *argvars, typval_T *rettv) +{ + char_u *fname; + stat_T st; + char_u *perm = NULL; + char_u flags[] = "rwx"; + int i; + + fname = get_tv_string(&argvars[0]); + + rettv->v_type = VAR_STRING; + if (mch_stat((char *)fname, &st) >= 0) + { + perm = vim_strsave((char_u *)"---------"); + if (perm != NULL) + { + for (i = 0; i < 9; i++) + { + if (st.st_mode & (1 << (8 - i))) + perm[i] = flags[i % 3]; + } + } + } + rettv->vval.v_string = perm; +} + +/* + * "getfsize({fname})" function + */ + static void +f_getfsize(typval_T *argvars, typval_T *rettv) +{ + char_u *fname; + stat_T st; + + fname = get_tv_string(&argvars[0]); + + rettv->v_type = VAR_NUMBER; + + if (mch_stat((char *)fname, &st) >= 0) + { + if (mch_isdir(fname)) + rettv->vval.v_number = 0; + else + { + rettv->vval.v_number = (varnumber_T)st.st_size; + + /* non-perfect check for overflow */ + if ((off_T)rettv->vval.v_number != (off_T)st.st_size) + rettv->vval.v_number = -2; + } + } + else + rettv->vval.v_number = -1; +} + +/* + * "getftime({fname})" function + */ + static void +f_getftime(typval_T *argvars, typval_T *rettv) +{ + char_u *fname; + stat_T st; + + fname = get_tv_string(&argvars[0]); + + if (mch_stat((char *)fname, &st) >= 0) + rettv->vval.v_number = (varnumber_T)st.st_mtime; + else + rettv->vval.v_number = -1; +} + +/* + * "getftype({fname})" function + */ + static void +f_getftype(typval_T *argvars, typval_T *rettv) +{ + char_u *fname; + stat_T st; + char_u *type = NULL; + char *t; + + fname = get_tv_string(&argvars[0]); + + rettv->v_type = VAR_STRING; + if (mch_lstat((char *)fname, &st) >= 0) + { +#ifdef S_ISREG + if (S_ISREG(st.st_mode)) + t = "file"; + else if (S_ISDIR(st.st_mode)) + t = "dir"; +# ifdef S_ISLNK + else if (S_ISLNK(st.st_mode)) + t = "link"; +# endif +# ifdef S_ISBLK + else if (S_ISBLK(st.st_mode)) + t = "bdev"; +# endif +# ifdef S_ISCHR + else if (S_ISCHR(st.st_mode)) + t = "cdev"; +# endif +# ifdef S_ISFIFO + else if (S_ISFIFO(st.st_mode)) + t = "fifo"; +# endif +# ifdef S_ISSOCK + else if (S_ISSOCK(st.st_mode)) + t = "fifo"; +# endif + else + t = "other"; +#else +# ifdef S_IFMT + switch (st.st_mode & S_IFMT) + { + case S_IFREG: t = "file"; break; + case S_IFDIR: t = "dir"; break; +# ifdef S_IFLNK + case S_IFLNK: t = "link"; break; +# endif +# ifdef S_IFBLK + case S_IFBLK: t = "bdev"; break; +# endif +# ifdef S_IFCHR + case S_IFCHR: t = "cdev"; break; +# endif +# ifdef S_IFIFO + case S_IFIFO: t = "fifo"; break; +# endif +# ifdef S_IFSOCK + case S_IFSOCK: t = "socket"; break; +# endif + default: t = "other"; + } +# else + if (mch_isdir(fname)) + t = "dir"; + else + t = "file"; +# endif +#endif + type = vim_strsave((char_u *)t); + } + rettv->vval.v_string = type; +} + +/* + * "getline(lnum, [end])" function + */ + static void +f_getline(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum; + linenr_T end; + int retlist; + + lnum = get_tv_lnum(argvars); + if (argvars[1].v_type == VAR_UNKNOWN) + { + end = 0; + retlist = FALSE; + } + else + { + end = get_tv_lnum(&argvars[1]); + retlist = TRUE; + } + + get_buffer_lines(curbuf, lnum, end, retlist, rettv); +} + +/* + * "getmatches()" function + */ + static void +f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_SEARCH_EXTRA + dict_T *dict; + matchitem_T *cur = curwin->w_match_head; + int i; + + if (rettv_list_alloc(rettv) == OK) + { + while (cur != NULL) + { + dict = dict_alloc(); + if (dict == NULL) + return; + if (cur->match.regprog == NULL) + { + /* match added with matchaddpos() */ + for (i = 0; i < MAXPOSMATCH; ++i) + { + llpos_T *llpos; + char buf[6]; + list_T *l; + + llpos = &cur->pos.pos[i]; + if (llpos->lnum == 0) + break; + l = list_alloc(); + if (l == NULL) + break; + list_append_number(l, (varnumber_T)llpos->lnum); + if (llpos->col > 0) + { + list_append_number(l, (varnumber_T)llpos->col); + list_append_number(l, (varnumber_T)llpos->len); + } + sprintf(buf, "pos%d", i + 1); + dict_add_list(dict, buf, l); + } + } + else + { + dict_add_nr_str(dict, "pattern", 0L, cur->pattern); + } + dict_add_nr_str(dict, "group", 0L, syn_id2name(cur->hlg_id)); + dict_add_nr_str(dict, "priority", (long)cur->priority, NULL); + dict_add_nr_str(dict, "id", (long)cur->id, NULL); +# if defined(FEAT_CONCEAL) && defined(FEAT_MBYTE) + if (cur->conceal_char) + { + char_u buf[MB_MAXBYTES + 1]; + + buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL; + dict_add_nr_str(dict, "conceal", 0L, (char_u *)&buf); + } +# endif + list_append_dict(rettv->vval.v_list, dict); + cur = cur->next; + } + } +#endif +} + +/* + * "getpid()" function + */ + static void +f_getpid(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = mch_get_pid(); +} + + static void +getpos_both( + typval_T *argvars, + typval_T *rettv, + int getcurpos) +{ + pos_T *fp; + list_T *l; + int fnum = -1; + + if (rettv_list_alloc(rettv) == OK) + { + l = rettv->vval.v_list; + if (getcurpos) + fp = &curwin->w_cursor; + else + fp = var2fpos(&argvars[0], TRUE, &fnum); + 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, +#ifdef FEAT_VIRTUALEDIT + (fp != NULL) ? (varnumber_T)fp->coladd : +#endif + (varnumber_T)0); + if (getcurpos) + { + update_curswant(); + list_append_number(l, curwin->w_curswant == MAXCOL ? + (varnumber_T)MAXCOL : (varnumber_T)curwin->w_curswant + 1); + } + } + else + rettv->vval.v_number = FALSE; +} + + +/* + * "getcurpos()" function + */ + static void +f_getcurpos(typval_T *argvars, typval_T *rettv) +{ + getpos_both(argvars, rettv, TRUE); +} + +/* + * "getpos(string)" function + */ + static void +f_getpos(typval_T *argvars, typval_T *rettv) +{ + getpos_both(argvars, rettv, FALSE); +} + +/* + * "getqflist()" and "getloclist()" functions + */ + static void +f_getqflist(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_QUICKFIX + win_T *wp; +#endif + +#ifdef FEAT_QUICKFIX + if (rettv_list_alloc(rettv) == OK) + { + wp = NULL; + if (argvars[0].v_type != VAR_UNKNOWN) /* getloclist() */ + { + wp = find_win_by_nr(&argvars[0], NULL); + if (wp == NULL) + return; + } + + (void)get_errorlist(wp, rettv->vval.v_list); + } +#endif +} + +/* + * "getreg()" function + */ + static void +f_getreg(typval_T *argvars, typval_T *rettv) +{ + char_u *strregname; + int regname; + int arg2 = FALSE; + int return_list = FALSE; + int error = FALSE; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + strregname = get_tv_string_chk(&argvars[0]); + error = strregname == NULL; + if (argvars[1].v_type != VAR_UNKNOWN) + { + arg2 = (int)get_tv_number_chk(&argvars[1], &error); + if (!error && argvars[2].v_type != VAR_UNKNOWN) + return_list = (int)get_tv_number_chk(&argvars[2], &error); + } + } + else + strregname = get_vim_var_str(VV_REG); + + if (error) + return; + + regname = (strregname == NULL ? '"' : *strregname); + if (regname == 0) + regname = '"'; + + 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) +{ + char_u *strregname; + int regname; + char_u buf[NUMBUFLEN + 2]; + long reglen = 0; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + strregname = get_tv_string_chk(&argvars[0]); + if (strregname == NULL) /* type error; errmsg already given */ + { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + return; + } + } + else + /* Default to v:register */ + strregname = get_vim_var_str(VV_REG); + + regname = (strregname == NULL ? '"' : *strregname); + if (regname == 0) + regname = '"'; + + buf[0] = NUL; + buf[1] = NUL; + switch (get_reg_type(regname, ®len)) + { + case MLINE: buf[0] = 'V'; break; + case MCHAR: buf[0] = 'v'; break; + case MBLOCK: + buf[0] = Ctrl_V; + sprintf((char *)buf + 1, "%ld", reglen + 1); + break; + } + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave(buf); +} + +/* + * "gettabvar()" function + */ + static void +f_gettabvar(typval_T *argvars, typval_T *rettv) +{ + win_T *oldcurwin; + tabpage_T *tp, *oldtabpage; + dictitem_T *v; + char_u *varname; + int done = FALSE; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + varname = get_tv_string_chk(&argvars[1]); + tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + if (tp != NULL && varname != NULL) + { + /* Set tp to be our tabpage, temporarily. Also set the window to the + * first window in the tabpage, otherwise the window is not valid. */ + if (switch_win(&oldcurwin, &oldtabpage, + tp->tp_firstwin == NULL ? firstwin : tp->tp_firstwin, tp, TRUE) + == OK) + { + /* look up the variable */ + /* Let gettabvar({nr}, "") return the "t:" dictionary. */ + v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', varname, FALSE); + if (v != NULL) + { + copy_tv(&v->di_tv, rettv); + done = TRUE; + } + } + + /* restore previous notion of curwin */ + restore_win(oldcurwin, oldtabpage, TRUE); + } + + if (!done && argvars[2].v_type != VAR_UNKNOWN) + copy_tv(&argvars[2], rettv); +} + +/* + * "gettabwinvar()" function + */ + static void +f_gettabwinvar(typval_T *argvars, typval_T *rettv) +{ + getwinvar(argvars, rettv, 1); +} + +/* + * "getwinposx()" function + */ + static void +f_getwinposx(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = -1; +#ifdef FEAT_GUI + if (gui.in_use) + { + int x, y; + + if (gui_mch_get_winpos(&x, &y) == OK) + rettv->vval.v_number = x; + } +#endif +} + +/* + * "win_findbuf()" function + */ + static void +f_win_findbuf(typval_T *argvars, typval_T *rettv) +{ + if (rettv_list_alloc(rettv) != FAIL) + win_findbuf(argvars, rettv->vval.v_list); +} + +/* + * "win_getid()" function + */ + static void +f_win_getid(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = win_getid(argvars); +} + +/* + * "win_gotoid()" function + */ + static void +f_win_gotoid(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = win_gotoid(argvars); +} + +/* + * "win_id2tabwin()" function + */ + static void +f_win_id2tabwin(typval_T *argvars, typval_T *rettv) +{ + if (rettv_list_alloc(rettv) != FAIL) + win_id2tabwin(argvars, rettv->vval.v_list); +} + +/* + * "win_id2win()" function + */ + static void +f_win_id2win(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = win_id2win(argvars); +} + +/* + * "getwinposy()" function + */ + static void +f_getwinposy(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = -1; +#ifdef FEAT_GUI + if (gui.in_use) + { + int x, y; + + if (gui_mch_get_winpos(&x, &y) == OK) + rettv->vval.v_number = y; + } +#endif +} + +/* + * "getwinvar()" function + */ + static void +f_getwinvar(typval_T *argvars, typval_T *rettv) +{ + getwinvar(argvars, rettv, 0); +} + +/* + * "glob()" function + */ + static void +f_glob(typval_T *argvars, typval_T *rettv) +{ + int options = WILD_SILENT|WILD_USE_NL; + expand_T xpc; + int error = FALSE; + + /* When the optional second argument is non-zero, don't remove matches + * for 'wildignore' and don't put matches for 'suffixes' at the end. */ + rettv->v_type = VAR_STRING; + if (argvars[1].v_type != VAR_UNKNOWN) + { + if (get_tv_number_chk(&argvars[1], &error)) + options |= WILD_KEEP_ALL; + if (argvars[2].v_type != VAR_UNKNOWN) + { + if (get_tv_number_chk(&argvars[2], &error)) + { + rettv->v_type = VAR_LIST; + rettv->vval.v_list = NULL; + } + if (argvars[3].v_type != VAR_UNKNOWN + && get_tv_number_chk(&argvars[3], &error)) + options |= WILD_ALLLINKS; + } + } + 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, get_tv_string(&argvars[0]), + NULL, options, WILD_ALL); + else if (rettv_list_alloc(rettv) != FAIL) + { + int i; + + ExpandOne(&xpc, get_tv_string(&argvars[0]), + 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; +} + +/* + * "globpath()" function + */ + static void +f_globpath(typval_T *argvars, typval_T *rettv) +{ + int flags = 0; + char_u buf1[NUMBUFLEN]; + char_u *file = get_tv_string_buf_chk(&argvars[1], buf1); + int error = FALSE; + garray_T ga; + int i; + + /* When the optional second argument is non-zero, don't remove matches + * for 'wildignore' and don't put matches for 'suffixes' at the end. */ + rettv->v_type = VAR_STRING; + if (argvars[2].v_type != VAR_UNKNOWN) + { + if (get_tv_number_chk(&argvars[2], &error)) + flags |= WILD_KEEP_ALL; + if (argvars[3].v_type != VAR_UNKNOWN) + { + if (get_tv_number_chk(&argvars[3], &error)) + { + rettv->v_type = VAR_LIST; + rettv->vval.v_list = NULL; + } + if (argvars[4].v_type != VAR_UNKNOWN + && get_tv_number_chk(&argvars[4], &error)) + flags |= WILD_ALLLINKS; + } + } + if (file != NULL && !error) + { + ga_init2(&ga, (int)sizeof(char_u *), 10); + globpath(get_tv_string(&argvars[0]), file, &ga, flags); + if (rettv->v_type == VAR_STRING) + rettv->vval.v_string = ga_concat_strings(&ga, "\n"); + else if (rettv_list_alloc(rettv) != FAIL) + for (i = 0; i < ga.ga_len; ++i) + list_append_string(rettv->vval.v_list, + ((char_u **)(ga.ga_data))[i], -1); + ga_clear_strings(&ga); + } + else + rettv->vval.v_string = NULL; +} + +/* + * "glob2regpat()" function + */ + static void +f_glob2regpat(typval_T *argvars, typval_T *rettv) +{ + char_u *pat = get_tv_string_chk(&argvars[0]); + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = (pat == NULL) + ? NULL : file_pat_to_reg_pat(pat, NULL, NULL, FALSE); +} + +/* for VIM_VERSION_ defines */ +#include "version.h" + +/* + * "has()" function + */ + static void +f_has(typval_T *argvars, typval_T *rettv) +{ + int i; + char_u *name; + int n = FALSE; + static char *(has_list[]) = + { +#ifdef AMIGA + "amiga", +# ifdef FEAT_ARP + "arp", +# endif +#endif +#ifdef __BEOS__ + "beos", +#endif +#ifdef MACOS + "mac", +#endif +#if defined(MACOS_X_UNIX) + "macunix", /* built with 'darwin' enabled */ +#endif +#if defined(__APPLE__) && __APPLE__ == 1 + "osx", /* built with or without 'darwin' enabled */ +#endif +#ifdef __QNX__ + "qnx", +#endif +#ifdef UNIX + "unix", +#endif +#ifdef VMS + "vms", +#endif +#ifdef WIN32 + "win32", +#endif +#if defined(UNIX) && (defined(__CYGWIN32__) || defined(__CYGWIN__)) + "win32unix", +#endif +#if defined(WIN64) || defined(_WIN64) + "win64", +#endif +#ifdef EBCDIC + "ebcdic", +#endif +#ifndef CASE_INSENSITIVE_FILENAME + "fname_case", +#endif +#ifdef HAVE_ACL + "acl", +#endif +#ifdef FEAT_ARABIC + "arabic", +#endif +#ifdef FEAT_AUTOCMD + "autocmd", +#endif +#ifdef FEAT_BEVAL + "balloon_eval", +# ifndef FEAT_GUI_W32 /* other GUIs always have multiline balloons */ + "balloon_multiline", +# endif +#endif +#if defined(SOME_BUILTIN_TCAPS) || defined(ALL_BUILTIN_TCAPS) + "builtin_terms", +# ifdef ALL_BUILTIN_TCAPS + "all_builtin_terms", +# endif +#endif +#if defined(FEAT_BROWSE) && (defined(USE_FILE_CHOOSER) \ + || defined(FEAT_GUI_W32) \ + || defined(FEAT_GUI_MOTIF)) + "browsefilter", +#endif +#ifdef FEAT_BYTEOFF + "byte_offset", +#endif +#ifdef FEAT_JOB_CHANNEL + "channel", +#endif +#ifdef FEAT_CINDENT + "cindent", +#endif +#ifdef FEAT_CLIENTSERVER + "clientserver", +#endif +#ifdef FEAT_CLIPBOARD + "clipboard", +#endif +#ifdef FEAT_CMDL_COMPL + "cmdline_compl", +#endif +#ifdef FEAT_CMDHIST + "cmdline_hist", +#endif +#ifdef FEAT_COMMENTS + "comments", +#endif +#ifdef FEAT_CONCEAL + "conceal", +#endif +#ifdef FEAT_CRYPT + "cryptv", + "crypt-blowfish", + "crypt-blowfish2", +#endif +#ifdef FEAT_CSCOPE + "cscope", +#endif +#ifdef FEAT_CURSORBIND + "cursorbind", +#endif +#ifdef CURSOR_SHAPE + "cursorshape", +#endif +#ifdef DEBUG + "debug", +#endif +#ifdef FEAT_CON_DIALOG + "dialog_con", +#endif +#ifdef FEAT_GUI_DIALOG + "dialog_gui", +#endif +#ifdef FEAT_DIFF + "diff", +#endif +#ifdef FEAT_DIGRAPHS + "digraphs", +#endif +#ifdef FEAT_DIRECTX + "directx", +#endif +#ifdef FEAT_DND + "dnd", +#endif +#ifdef FEAT_EMACS_TAGS + "emacs_tags", +#endif + "eval", /* always present, of course! */ + "ex_extra", /* graduated feature */ +#ifdef FEAT_SEARCH_EXTRA + "extra_search", +#endif +#ifdef FEAT_FKMAP + "farsi", +#endif +#ifdef FEAT_SEARCHPATH + "file_in_path", +#endif +#ifdef FEAT_FILTERPIPE + "filterpipe", +#endif +#ifdef FEAT_FIND_ID + "find_in_path", +#endif +#ifdef FEAT_FLOAT + "float", +#endif +#ifdef FEAT_FOLDING + "folding", +#endif +#ifdef FEAT_FOOTER + "footer", +#endif +#if !defined(USE_SYSTEM) && defined(UNIX) + "fork", +#endif +#ifdef FEAT_GETTEXT + "gettext", +#endif +#ifdef FEAT_GUI + "gui", +#endif +#ifdef FEAT_GUI_ATHENA +# ifdef FEAT_GUI_NEXTAW + "gui_neXtaw", +# else + "gui_athena", +# endif +#endif +#ifdef FEAT_GUI_GTK + "gui_gtk", +# ifdef USE_GTK3 + "gui_gtk3", +# else + "gui_gtk2", +# endif +#endif +#ifdef FEAT_GUI_GNOME + "gui_gnome", +#endif +#ifdef FEAT_GUI_MAC + "gui_mac", +#endif +#ifdef FEAT_GUI_MOTIF + "gui_motif", +#endif +#ifdef FEAT_GUI_PHOTON + "gui_photon", +#endif +#ifdef FEAT_GUI_W32 + "gui_win32", +#endif +#ifdef FEAT_HANGULIN + "hangul_input", +#endif +#if defined(HAVE_ICONV_H) && defined(USE_ICONV) + "iconv", +#endif +#ifdef FEAT_INS_EXPAND + "insert_expand", +#endif +#ifdef FEAT_JOB_CHANNEL + "job", +#endif +#ifdef FEAT_JUMPLIST + "jumplist", +#endif +#ifdef FEAT_KEYMAP + "keymap", +#endif +#ifdef FEAT_LANGMAP + "langmap", +#endif +#ifdef FEAT_LIBCALL + "libcall", +#endif +#ifdef FEAT_LINEBREAK + "linebreak", +#endif +#ifdef FEAT_LISP + "lispindent", +#endif +#ifdef FEAT_LISTCMDS + "listcmds", +#endif +#ifdef FEAT_LOCALMAP + "localmap", +#endif +#ifdef FEAT_LUA +# ifndef DYNAMIC_LUA + "lua", +# endif +#endif +#ifdef FEAT_MENU + "menu", +#endif +#ifdef FEAT_SESSION + "mksession", +#endif +#ifdef FEAT_MODIFY_FNAME + "modify_fname", +#endif +#ifdef FEAT_MOUSE + "mouse", +#endif +#ifdef FEAT_MOUSESHAPE + "mouseshape", +#endif +#if defined(UNIX) || defined(VMS) +# ifdef FEAT_MOUSE_DEC + "mouse_dec", +# endif +# ifdef FEAT_MOUSE_GPM + "mouse_gpm", +# endif +# ifdef FEAT_MOUSE_JSB + "mouse_jsbterm", +# endif +# ifdef FEAT_MOUSE_NET + "mouse_netterm", +# endif +# ifdef FEAT_MOUSE_PTERM + "mouse_pterm", +# endif +# ifdef FEAT_MOUSE_SGR + "mouse_sgr", +# endif +# ifdef FEAT_SYSMOUSE + "mouse_sysmouse", +# endif +# ifdef FEAT_MOUSE_URXVT + "mouse_urxvt", +# endif +# ifdef FEAT_MOUSE_XTERM + "mouse_xterm", +# endif +#endif +#ifdef FEAT_MBYTE + "multi_byte", +#endif +#ifdef FEAT_MBYTE_IME + "multi_byte_ime", +#endif +#ifdef FEAT_MULTI_LANG + "multi_lang", +#endif +#ifdef FEAT_MZSCHEME +#ifndef DYNAMIC_MZSCHEME + "mzscheme", +#endif +#endif +#ifdef FEAT_NUM64 + "num64", +#endif +#ifdef FEAT_OLE + "ole", +#endif + "packages", +#ifdef FEAT_PATH_EXTRA + "path_extra", +#endif +#ifdef FEAT_PERL +#ifndef DYNAMIC_PERL + "perl", +#endif +#endif +#ifdef FEAT_PERSISTENT_UNDO + "persistent_undo", +#endif +#ifdef FEAT_PYTHON +#ifndef DYNAMIC_PYTHON + "python", +#endif +#endif +#ifdef FEAT_PYTHON3 +#ifndef DYNAMIC_PYTHON3 + "python3", +#endif +#endif +#ifdef FEAT_POSTSCRIPT + "postscript", +#endif +#ifdef FEAT_PRINTER + "printer", +#endif +#ifdef FEAT_PROFILE + "profile", +#endif +#ifdef FEAT_RELTIME + "reltime", +#endif +#ifdef FEAT_QUICKFIX + "quickfix", +#endif +#ifdef FEAT_RIGHTLEFT + "rightleft", +#endif +#if defined(FEAT_RUBY) && !defined(DYNAMIC_RUBY) + "ruby", +#endif +#ifdef FEAT_SCROLLBIND + "scrollbind", +#endif +#ifdef FEAT_CMDL_INFO + "showcmd", + "cmdline_info", +#endif +#ifdef FEAT_SIGNS + "signs", +#endif +#ifdef FEAT_SMARTINDENT + "smartindent", +#endif +#ifdef STARTUPTIME + "startuptime", +#endif +#ifdef FEAT_STL_OPT + "statusline", +#endif +#ifdef FEAT_SUN_WORKSHOP + "sun_workshop", +#endif +#ifdef FEAT_NETBEANS_INTG + "netbeans_intg", +#endif +#ifdef FEAT_SPELL + "spell", +#endif +#ifdef FEAT_SYN_HL + "syntax", +#endif +#if defined(USE_SYSTEM) || !defined(UNIX) + "system", +#endif +#ifdef FEAT_TAG_BINS + "tag_binary", +#endif +#ifdef FEAT_TAG_OLDSTATIC + "tag_old_static", +#endif +#ifdef FEAT_TAG_ANYWHITE + "tag_any_white", +#endif +#ifdef FEAT_TCL +# ifndef DYNAMIC_TCL + "tcl", +# endif +#endif +#ifdef FEAT_TERMGUICOLORS + "termguicolors", +#endif +#ifdef TERMINFO + "terminfo", +#endif +#ifdef FEAT_TERMRESPONSE + "termresponse", +#endif +#ifdef FEAT_TEXTOBJ + "textobjects", +#endif +#ifdef HAVE_TGETENT + "tgetent", +#endif +#ifdef FEAT_TIMERS + "timers", +#endif +#ifdef FEAT_TITLE + "title", +#endif +#ifdef FEAT_TOOLBAR + "toolbar", +#endif +#if defined(FEAT_CLIPBOARD) && defined(FEAT_X11) + "unnamedplus", +#endif +#ifdef FEAT_USR_CMDS + "user-commands", /* was accidentally included in 5.4 */ + "user_commands", +#endif +#ifdef FEAT_VIMINFO + "viminfo", +#endif +#ifdef FEAT_WINDOWS + "vertsplit", +#endif +#ifdef FEAT_VIRTUALEDIT + "virtualedit", +#endif + "visual", +#ifdef FEAT_VISUALEXTRA + "visualextra", +#endif +#ifdef FEAT_VREPLACE + "vreplace", +#endif +#ifdef FEAT_WILDIGN + "wildignore", +#endif +#ifdef FEAT_WILDMENU + "wildmenu", +#endif +#ifdef FEAT_WINDOWS + "windows", +#endif +#ifdef FEAT_WAK + "winaltkeys", +#endif +#ifdef FEAT_WRITEBACKUP + "writebackup", +#endif +#ifdef FEAT_XIM + "xim", +#endif +#ifdef FEAT_XFONTSET + "xfontset", +#endif +#ifdef FEAT_XPM_W32 + "xpm", + "xpm_w32", /* for backward compatibility */ +#else +# if defined(HAVE_XPM) + "xpm", +# endif +#endif +#ifdef USE_XSMP + "xsmp", +#endif +#ifdef USE_XSMP_INTERACT + "xsmp_interact", +#endif +#ifdef FEAT_XCLIPBOARD + "xterm_clipboard", +#endif +#ifdef FEAT_XTERM_SAVE + "xterm_save", +#endif +#if defined(UNIX) && defined(FEAT_X11) + "X11", +#endif + NULL + }; + + name = get_tv_string(&argvars[0]); + for (i = 0; has_list[i] != NULL; ++i) + if (STRICMP(name, has_list[i]) == 0) + { + n = TRUE; + break; + } + + if (n == FALSE) + { + if (STRNICMP(name, "patch", 5) == 0) + { + 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) + n = (starting != 0); +#ifdef FEAT_MBYTE + else if (STRICMP(name, "multi_byte_encoding") == 0) + n = has_mbyte; +#endif +#if defined(FEAT_BEVAL) && defined(FEAT_GUI_W32) + else if (STRICMP(name, "balloon_multiline") == 0) + n = multiline_balloon_available(); +#endif +#ifdef DYNAMIC_TCL + else if (STRICMP(name, "tcl") == 0) + n = tcl_enabled(FALSE); +#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_RUBY + else if (STRICMP(name, "ruby") == 0) + n = ruby_enabled(FALSE); +#endif +#ifdef FEAT_PYTHON +#ifdef DYNAMIC_PYTHON + else if (STRICMP(name, "python") == 0) + n = python_enabled(FALSE); +#endif +#endif +#ifdef FEAT_PYTHON3 +#ifdef DYNAMIC_PYTHON3 + else if (STRICMP(name, "python3") == 0) + n = python3_enabled(FALSE); +#endif +#endif +#ifdef DYNAMIC_PERL + else if (STRICMP(name, "perl") == 0) + n = perl_enabled(FALSE); +#endif +#ifdef FEAT_GUI + else if (STRICMP(name, "gui_running") == 0) + n = (gui.in_use || gui.starting); +# ifdef FEAT_GUI_W32 + else if (STRICMP(name, "gui_win32s") == 0) + n = gui_is_win32s(); +# endif +# ifdef FEAT_BROWSE + else if (STRICMP(name, "browse") == 0) + n = gui.in_use; /* gui_mch_browse() works when GUI is running */ +# endif +#endif +#ifdef FEAT_SYN_HL + else if (STRICMP(name, "syntax_items") == 0) + n = syntax_present(curwin); +#endif +#if defined(WIN3264) + else if (STRICMP(name, "win95") == 0) + n = mch_windows95(); +#endif +#ifdef FEAT_NETBEANS_INTG + else if (STRICMP(name, "netbeans_enabled") == 0) + n = netbeans_active(); +#endif + } + + rettv->vval.v_number = n; +} + +/* + * "has_key()" function + */ + static void +f_has_key(typval_T *argvars, typval_T *rettv) +{ + if (argvars[0].v_type != VAR_DICT) + { + EMSG(_(e_dictreq)); + return; + } + if (argvars[0].vval.v_dict == NULL) + return; + + rettv->vval.v_number = dict_find(argvars[0].vval.v_dict, + get_tv_string(&argvars[1]), -1) != NULL; +} + +/* + * "haslocaldir()" function + */ + static void +f_haslocaldir(typval_T *argvars, typval_T *rettv) +{ + win_T *wp = NULL; + + wp = find_tabwin(&argvars[0], &argvars[1]); + rettv->vval.v_number = (wp != NULL && wp->w_localdir != NULL); +} + +/* + * "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; + + name = get_tv_string(&argvars[0]); + if (argvars[1].v_type == VAR_UNKNOWN) + mode = (char_u *)"nvo"; + else + { + mode = get_tv_string_buf(&argvars[1], buf); + if (argvars[2].v_type != VAR_UNKNOWN) + abbr = (int)get_tv_number(&argvars[2]); + } + + if (map_to_exists(name, mode, abbr)) + rettv->vval.v_number = TRUE; + else + rettv->vval.v_number = FALSE; +} + +/* + * "histadd()" function + */ + static void +f_histadd(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_CMDHIST + int histype; + char_u *str; + char_u buf[NUMBUFLEN]; +#endif + + rettv->vval.v_number = FALSE; + if (check_restricted() || check_secure()) + return; +#ifdef FEAT_CMDHIST + str = get_tv_string_chk(&argvars[0]); /* NULL on type error */ + histype = str != NULL ? get_histtype(str) : -1; + if (histype >= 0) + { + str = get_tv_string_buf(&argvars[1], buf); + if (*str != NUL) + { + init_history(); + add_to_history(histype, str, FALSE, NUL); + rettv->vval.v_number = TRUE; + return; + } + } +#endif +} + +/* + * "histdel()" function + */ + static void +f_histdel(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_CMDHIST + int n; + char_u buf[NUMBUFLEN]; + char_u *str; + + str = get_tv_string_chk(&argvars[0]); /* NULL on type error */ + if (str == NULL) + n = 0; + else if (argvars[1].v_type == VAR_UNKNOWN) + /* only one argument: clear entire history */ + n = clr_history(get_histtype(str)); + else if (argvars[1].v_type == VAR_NUMBER) + /* index given: remove that entry */ + n = del_history_idx(get_histtype(str), + (int)get_tv_number(&argvars[1])); + else + /* string given: remove all matching entries */ + n = del_history_entry(get_histtype(str), + get_tv_string_buf(&argvars[1], buf)); + rettv->vval.v_number = n; +#endif +} + +/* + * "histget()" function + */ + static void +f_histget(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_CMDHIST + int type; + int idx; + char_u *str; + + str = get_tv_string_chk(&argvars[0]); /* NULL on type error */ + if (str == NULL) + rettv->vval.v_string = NULL; + else + { + type = get_histtype(str); + if (argvars[1].v_type == VAR_UNKNOWN) + idx = get_history_idx(type); + else + idx = (int)get_tv_number_chk(&argvars[1], NULL); + /* -1 on type error */ + rettv->vval.v_string = vim_strsave(get_history_entry(type, idx)); + } +#else + rettv->vval.v_string = NULL; +#endif + rettv->v_type = VAR_STRING; +} + +/* + * "histnr()" function + */ + static void +f_histnr(typval_T *argvars UNUSED, typval_T *rettv) +{ + int i; + +#ifdef FEAT_CMDHIST + char_u *history = get_tv_string_chk(&argvars[0]); + + i = history == NULL ? HIST_CMD - 1 : get_histtype(history); + if (i >= HIST_CMD && i < HIST_COUNT) + i = get_history_idx(i); + else +#endif + i = -1; + rettv->vval.v_number = i; +} + +/* + * "highlightID(name)" function + */ + static void +f_hlID(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = syn_name2id(get_tv_string(&argvars[0])); +} + +/* + * "highlight_exists()" function + */ + static void +f_hlexists(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = highlight_exists(get_tv_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); +} + +/* + * iconv() function + */ + static void +f_iconv(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_MBYTE + char_u buf1[NUMBUFLEN]; + char_u buf2[NUMBUFLEN]; + char_u *from, *to, *str; + vimconv_T vimconv; +#endif + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + +#ifdef FEAT_MBYTE + str = get_tv_string(&argvars[0]); + from = enc_canonize(enc_skip(get_tv_string_buf(&argvars[1], buf1))); + to = enc_canonize(enc_skip(get_tv_string_buf(&argvars[2], buf2))); + vimconv.vc_type = CONV_NONE; + convert_setup(&vimconv, from, to); + + /* If the encodings are equal, no conversion needed. */ + if (vimconv.vc_type == CONV_NONE) + rettv->vval.v_string = vim_strsave(str); + else + rettv->vval.v_string = string_convert(&vimconv, str, NULL); + + convert_setup(&vimconv, NULL, NULL); + vim_free(from); + vim_free(to); +#endif +} + +/* + * "indent()" function + */ + static void +f_indent(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum; + + lnum = get_tv_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + rettv->vval.v_number = get_indent_lnum(lnum); + else + rettv->vval.v_number = -1; +} + +/* + * "index()" function + */ + static void +f_index(typval_T *argvars, typval_T *rettv) +{ + list_T *l; + listitem_T *item; + long idx = 0; + int ic = FALSE; + + rettv->vval.v_number = -1; + if (argvars[0].v_type != VAR_LIST) + { + EMSG(_(e_listreq)); + return; + } + l = argvars[0].vval.v_list; + if (l != NULL) + { + item = l->lv_first; + if (argvars[2].v_type != VAR_UNKNOWN) + { + int error = FALSE; + + /* Start at specified item. Use the cached index that list_find() + * sets, so that a negative number also works. */ + item = list_find(l, (long)get_tv_number_chk(&argvars[2], &error)); + idx = l->lv_idx; + if (argvars[3].v_type != VAR_UNKNOWN) + ic = (int)get_tv_number_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 *)""; + + message = get_tv_string_chk(&argvars[0]); + if (argvars[1].v_type != VAR_UNKNOWN + && (defstr = get_tv_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( + get_tv_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) +{ + listitem_T *li; + int selected; + int mouse_used; + +#ifdef NO_CONSOLE_INPUT + /* While starting up, there is no place to enter text. */ + if (no_console_input()) + return; +#endif + if (argvars[0].v_type != VAR_LIST || argvars[0].vval.v_list == NULL) + { + EMSG2(_(e_listarg), "inputlist()"); + return; + } + + msg_start(); + msg_row = Rows - 1; /* for when 'cmdheight' > 1 */ + lines_left = Rows; /* avoid more prompt */ + msg_scroll = TRUE; + msg_clr_eos(); + + for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next) + { + msg_puts(get_tv_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); + /* default return is zero == OK */ + } + else if (p_verbose > 1) + { + verb_msg((char_u *)_("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) +{ + ++cmdline_star; + ++inputsecret_flag; + f_input(argvars, rettv); + --cmdline_star; + --inputsecret_flag; +} + +/* + * "insert()" function + */ + static void +f_insert(typval_T *argvars, typval_T *rettv) +{ + long before = 0; + listitem_T *item; + list_T *l; + int error = FALSE; + + if (argvars[0].v_type != VAR_LIST) + EMSG2(_(e_listarg), "insert()"); + else if ((l = argvars[0].vval.v_list) != NULL + && !tv_check_lock(l->lv_lock, (char_u *)N_("insert() argument"), TRUE)) + { + if (argvars[2].v_type != VAR_UNKNOWN) + before = (long)get_tv_number_chk(&argvars[2], &error); + if (error) + return; /* type error; errmsg already given */ + + if (before == l->lv_len) + item = NULL; + else + { + item = list_find(l, before); + if (item == NULL) + { + EMSGN(_(e_listidx), before); + l = NULL; + } + } + if (l != NULL) + { + list_insert_tv(l, &argvars[1], item); + copy_tv(&argvars[0], rettv); + } + } +} + +/* + * "invert(expr)" function + */ + static void +f_invert(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = ~get_tv_number_chk(&argvars[0], NULL); +} + +/* + * "isdirectory()" function + */ + static void +f_isdirectory(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = mch_isdir(get_tv_string(&argvars[0])); +} + +/* + * Return TRUE if typeval "tv" is locked: Either that value is locked itself + * or it refers to a List or Dictionary that is locked. + */ + static int +tv_islocked(typval_T *tv) +{ + return (tv->v_lock & VAR_LOCKED) + || (tv->v_type == VAR_LIST + && tv->vval.v_list != NULL + && (tv->vval.v_list->lv_lock & VAR_LOCKED)) + || (tv->v_type == VAR_DICT + && tv->vval.v_dict != NULL + && (tv->vval.v_dict->dv_lock & VAR_LOCKED)); +} + +/* + * "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; + end = get_lval(get_tv_string(&argvars[0]), NULL, &lv, FALSE, FALSE, + GLV_NO_AUTOLOAD, FNE_CHECK_START); + if (end != NULL && lv.ll_name != NULL) + { + if (*end != NUL) + EMSG(_(e_trailing)); + else + { + if (lv.ll_tv == NULL) + { + if (check_changedtick(lv.ll_name)) + rettv->vval.v_number = 1; /* always locked */ + else + { + 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(_("E786: Range not allowed")); + else if (lv.ll_newkey != NULL) + EMSG2(_(e_dictkey), 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); +} + +#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H) +/* + * "isnan()" function + */ + static void +f_isnan(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = argvars[0].v_type == VAR_FLOAT + && isnan(argvars[0].vval.v_float); +} +#endif + +/* + * "items(dict)" function + */ + static void +f_items(typval_T *argvars, typval_T *rettv) +{ + dict_list(argvars, rettv, 2); +} + +#if defined(FEAT_JOB_CHANNEL) || defined(PROTO) +/* + * Get the job from the argument. + * Returns NULL if the job is invalid. + */ + static job_T * +get_job_arg(typval_T *tv) +{ + job_T *job; + + if (tv->v_type != VAR_JOB) + { + EMSG2(_(e_invarg2), get_tv_string(tv)); + return NULL; + } + job = tv->vval.v_job; + + if (job == NULL) + EMSG(_("E916: not a valid job")); + return job; +} + +/* + * "job_getchannel()" function + */ + static void +f_job_getchannel(typval_T *argvars, typval_T *rettv) +{ + job_T *job = get_job_arg(&argvars[0]); + + if (job != NULL) + { + rettv->v_type = VAR_CHANNEL; + rettv->vval.v_channel = job->jv_channel; + if (job->jv_channel != NULL) + ++job->jv_channel->ch_refcount; + } +} + +/* + * "job_info()" function + */ + static void +f_job_info(typval_T *argvars, typval_T *rettv) +{ + job_T *job = get_job_arg(&argvars[0]); + + if (job != NULL && rettv_dict_alloc(rettv) != FAIL) + job_info(job, rettv->vval.v_dict); +} + +/* + * "job_setoptions()" function + */ + static void +f_job_setoptions(typval_T *argvars, typval_T *rettv UNUSED) +{ + job_T *job = get_job_arg(&argvars[0]); + jobopt_T opt; + + if (job == NULL) + return; + clear_job_options(&opt); + if (get_job_options(&argvars[1], &opt, JO_STOPONEXIT + JO_EXIT_CB) == OK) + job_set_options(job, &opt); + free_job_options(&opt); +} + +/* + * "job_start()" function + */ + static void +f_job_start(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_JOB; + if (check_restricted() || check_secure()) + return; + rettv->vval.v_job = job_start(argvars); +} + +/* + * "job_status()" function + */ + static void +f_job_status(typval_T *argvars, typval_T *rettv) +{ + job_T *job = get_job_arg(&argvars[0]); + + if (job != NULL) + { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave((char_u *)job_status(job)); + } +} + +/* + * "job_stop()" function + */ + static void +f_job_stop(typval_T *argvars, typval_T *rettv) +{ + job_T *job = get_job_arg(&argvars[0]); + + if (job != NULL) + rettv->vval.v_number = job_stop(job, argvars); +} +#endif + +/* + * "join()" function + */ + static void +f_join(typval_T *argvars, typval_T *rettv) +{ + garray_T ga; + char_u *sep; + + if (argvars[0].v_type != VAR_LIST) + { + EMSG(_(e_listreq)); + return; + } + if (argvars[0].vval.v_list == NULL) + return; + if (argvars[1].v_type == VAR_UNKNOWN) + sep = (char_u *)" "; + else + sep = get_tv_string_chk(&argvars[1]); + + rettv->v_type = VAR_STRING; + + if (sep != NULL) + { + ga_init2(&ga, (int)sizeof(char), 80); + list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0); + ga_append(&ga, NUL); + rettv->vval.v_string = (char_u *)ga.ga_data; + } + else + rettv->vval.v_string = NULL; +} + +/* + * "js_decode()" function + */ + static void +f_js_decode(typval_T *argvars, typval_T *rettv) +{ + js_read_T reader; + + reader.js_buf = get_tv_string(&argvars[0]); + reader.js_fill = NULL; + reader.js_used = 0; + if (json_decode_all(&reader, rettv, JSON_JS) != OK) + EMSG(_(e_invarg)); +} + +/* + * "js_encode()" function + */ + static void +f_js_encode(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = json_encode(&argvars[0], JSON_JS); +} + +/* + * "json_decode()" function + */ + static void +f_json_decode(typval_T *argvars, typval_T *rettv) +{ + js_read_T reader; + + reader.js_buf = get_tv_string(&argvars[0]); + reader.js_fill = NULL; + reader.js_used = 0; + if (json_decode_all(&reader, rettv, 0) != OK) + EMSG(_(e_invarg)); +} + +/* + * "json_encode()" function + */ + static void +f_json_encode(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = json_encode(&argvars[0], 0); +} + +/* + * "keys()" function + */ + static void +f_keys(typval_T *argvars, typval_T *rettv) +{ + dict_list(argvars, rettv, 0); +} + +/* + * "last_buffer_nr()" function. + */ + static void +f_last_buffer_nr(typval_T *argvars UNUSED, typval_T *rettv) +{ + int n = 0; + buf_T *buf; + + for (buf = firstbuf; buf != NULL; buf = buf->b_next) + if (n < buf->b_fnum) + n = buf->b_fnum; + + rettv->vval.v_number = n; +} + +/* + * "len()" function + */ + static 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( + get_tv_string(&argvars[0])); + 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_SPECIAL: + case VAR_FLOAT: + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_JOB: + case VAR_CHANNEL: + EMSG(_("E701: Invalid type for len()")); + break; + } +} + +static void libcall_common(typval_T *argvars, typval_T *rettv, int type); + + static void +libcall_common(typval_T *argvars, 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; + +#ifdef FEAT_LIBCALL + /* The first two args must be strings, otherwise its 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 + 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)" function + */ + static void +f_line(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum = 0; + pos_T *fp; + int fnum; + + fp = var2fpos(&argvars[0], TRUE, &fnum); + 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; + + lnum = get_tv_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 +} + +/* + * "lispindent(lnum)" function + */ + static void +f_lispindent(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_LISP + pos_T pos; + linenr_T lnum; + + pos = curwin->w_cursor; + lnum = get_tv_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + { + curwin->w_cursor.lnum = lnum; + rettv->vval.v_number = get_lisp_indent(); + curwin->w_cursor = pos; + } + else +#endif + rettv->vval.v_number = -1; +} + +/* + * "localtime()" function + */ + static void +f_localtime(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = (varnumber_T)time(NULL); +} + +static void get_maparg(typval_T *argvars, typval_T *rettv, int exact); + + static void +get_maparg(typval_T *argvars, typval_T *rettv, int exact) +{ + char_u *keys; + char_u *which; + char_u buf[NUMBUFLEN]; + char_u *keys_buf = NULL; + char_u *rhs; + int mode; + int abbr = FALSE; + int get_dict = FALSE; + mapblock_T *mp; + int buffer_local; + + /* return empty string for failure */ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + keys = get_tv_string(&argvars[0]); + if (*keys == NUL) + return; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + which = get_tv_string_buf_chk(&argvars[1], buf); + if (argvars[2].v_type != VAR_UNKNOWN) + { + abbr = (int)get_tv_number(&argvars[2]); + if (argvars[3].v_type != VAR_UNKNOWN) + get_dict = (int)get_tv_number(&argvars[3]); + } + } + else + which = (char_u *)""; + if (which == NULL) + return; + + mode = get_map_mode(&which, 0); + + keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, FALSE); + rhs = check_map(keys, mode, exact, FALSE, abbr, &mp, &buffer_local); + vim_free(keys_buf); + + if (!get_dict) + { + /* Return a string. */ + if (rhs != NULL) + rettv->vval.v_string = str2special_save(rhs, FALSE); + + } + else if (rettv_dict_alloc(rettv) != FAIL && rhs != NULL) + { + /* Return a dictionary. */ + char_u *lhs = str2special_save(mp->m_keys, TRUE); + char_u *mapmode = map_mode_to_chars(mp->m_mode); + dict_T *dict = rettv->vval.v_dict; + + dict_add_nr_str(dict, "lhs", 0L, lhs); + dict_add_nr_str(dict, "rhs", 0L, mp->m_orig_str); + dict_add_nr_str(dict, "noremap", mp->m_noremap ? 1L : 0L , NULL); + dict_add_nr_str(dict, "expr", mp->m_expr ? 1L : 0L, NULL); + dict_add_nr_str(dict, "silent", mp->m_silent ? 1L : 0L, NULL); + dict_add_nr_str(dict, "sid", (long)mp->m_script_ID, NULL); + dict_add_nr_str(dict, "buffer", (long)buffer_local, NULL); + dict_add_nr_str(dict, "nowait", mp->m_nowait ? 1L : 0L, NULL); + dict_add_nr_str(dict, "mode", 0L, mapmode); + + vim_free(lhs); + vim_free(mapmode); + } +} + +#ifdef FEAT_FLOAT +/* + * "log()" function + */ + static void +f_log(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = log(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "log10()" function + */ + static void +f_log10(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = log10(f); + else + rettv->vval.v_float = 0.0; +} +#endif + +#ifdef FEAT_LUA +/* + * "luaeval()" function + */ + static void +f_luaeval(typval_T *argvars, typval_T *rettv) +{ + char_u *str; + char_u buf[NUMBUFLEN]; + + str = get_tv_string_buf(&argvars[0], buf); + do_luaeval(str, argvars + 1, rettv); +} +#endif + +/* + * "map()" function + */ + static void +f_map(typval_T *argvars, typval_T *rettv) +{ + filter_map(argvars, rettv, TRUE); +} + +/* + * "maparg()" function + */ + static void +f_maparg(typval_T *argvars, typval_T *rettv) +{ + get_maparg(argvars, rettv, TRUE); +} + +/* + * "mapcheck()" function + */ + static void +f_mapcheck(typval_T *argvars, typval_T *rettv) +{ + get_maparg(argvars, rettv, FALSE); +} + +static void find_some_match(typval_T *argvars, typval_T *rettv, int start); + + static void +find_some_match(typval_T *argvars, typval_T *rettv, int 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 = (char_u *)""; + + rettv->vval.v_number = -1; + if (type == 3 || type == 4) + { + /* type 3: return empty list when there are no matches. + * type 4: return ["", -1, -1, -1] */ + if (rettv_list_alloc(rettv) == FAIL) + goto theend; + if (type == 4 + && (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 == 2) + { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + } + + if (argvars[0].v_type == VAR_LIST) + { + if ((l = argvars[0].vval.v_list) == NULL) + goto theend; + li = l->lv_first; + } + else + { + expr = str = get_tv_string(&argvars[0]); + len = (long)STRLEN(str); + } + + pat = get_tv_string_buf_chk(&argvars[1], patbuf); + if (pat == NULL) + goto theend; + + if (argvars[2].v_type != VAR_UNKNOWN) + { + int error = FALSE; + + start = (long)get_tv_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_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)get_tv_number_chk(&argvars[3], &error); + if (error) + goto theend; + } + + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog != NULL) + { + regmatch.rm_ic = p_ic; + + for (;;) + { + if (l != NULL) + { + if (li == NULL) + { + match = FALSE; + break; + } + vim_free(tofree); + expr = str = echo_string(&li->li_tv, &tofree, strbuf, 0); + if (str == NULL) + break; + } + + match = vim_regexec_nl(®match, str, (colnr_T)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 + { +#ifdef FEAT_MBYTE + startcol = (colnr_T)(regmatch.startp[0] + + (*mb_ptr2len)(regmatch.startp[0]) - str); +#else + startcol = (colnr_T)(regmatch.startp[0] + 1 - str); +#endif + if (startcol > (colnr_T)len + || str + startcol <= regmatch.startp[0]) + { + match = FALSE; + break; + } + } + } + + if (match) + { + if (type == 4) + { + 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], + (int)(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 == 3) + { + 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 == 2) + { + /* return matched string */ + if (l != NULL) + copy_tv(&li->li_tv, rettv); + else + rettv->vval.v_string = vim_strnsave(regmatch.startp[0], + (int)(regmatch.endp[0] - regmatch.startp[0])); + } + else if (l != NULL) + rettv->vval.v_number = idx; + else + { + if (type != 0) + 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); + } + + if (type == 4 && l == NULL) + /* matchstrpos() without a list: drop the second item. */ + listitem_remove(rettv->vval.v_list, + rettv->vval.v_list->lv_first->li_next); + +theend: + vim_free(tofree); + p_cpo = save_cpo; +} + +/* + * "match()" function + */ + static void +f_match(typval_T *argvars, typval_T *rettv) +{ + find_some_match(argvars, rettv, 1); +} + +/* + * "matchadd()" function + */ + static void +f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_SEARCH_EXTRA + char_u buf[NUMBUFLEN]; + char_u *grp = get_tv_string_buf_chk(&argvars[0], buf); /* group */ + char_u *pat = get_tv_string_buf_chk(&argvars[1], buf); /* pattern */ + int prio = 10; /* default priority */ + int id = -1; + int error = FALSE; + char_u *conceal_char = NULL; + + rettv->vval.v_number = -1; + + if (grp == NULL || pat == NULL) + return; + if (argvars[2].v_type != VAR_UNKNOWN) + { + prio = (int)get_tv_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) + { + id = (int)get_tv_number_chk(&argvars[3], &error); + if (argvars[4].v_type != VAR_UNKNOWN) + { + if (argvars[4].v_type != VAR_DICT) + { + EMSG(_(e_dictreq)); + return; + } + if (dict_find(argvars[4].vval.v_dict, + (char_u *)"conceal", -1) != NULL) + conceal_char = get_dict_string(argvars[4].vval.v_dict, + (char_u *)"conceal", FALSE); + } + } + } + if (error == TRUE) + return; + if (id >= 1 && id <= 3) + { + EMSGN("E798: ID is reserved for \":match\": %ld", id); + return; + } + + rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL, + conceal_char); +#endif +} + +/* + * "matchaddpos()" function + */ + static void +f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_SEARCH_EXTRA + char_u buf[NUMBUFLEN]; + char_u *group; + int prio = 10; + int id = -1; + int error = FALSE; + list_T *l; + char_u *conceal_char = NULL; + + rettv->vval.v_number = -1; + + group = get_tv_string_buf_chk(&argvars[0], buf); + if (group == NULL) + return; + + if (argvars[1].v_type != VAR_LIST) + { + EMSG2(_(e_listarg), "matchaddpos()"); + return; + } + l = argvars[1].vval.v_list; + if (l == NULL) + return; + + if (argvars[2].v_type != VAR_UNKNOWN) + { + prio = (int)get_tv_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) + { + id = (int)get_tv_number_chk(&argvars[3], &error); + if (argvars[4].v_type != VAR_UNKNOWN) + { + if (argvars[4].v_type != VAR_DICT) + { + EMSG(_(e_dictreq)); + return; + } + if (dict_find(argvars[4].vval.v_dict, + (char_u *)"conceal", -1) != NULL) + conceal_char = get_dict_string(argvars[4].vval.v_dict, + (char_u *)"conceal", FALSE); + } + } + } + if (error == TRUE) + return; + + /* id == 3 is ok because matchaddpos() is supposed to substitute :3match */ + if (id == 1 || id == 2) + { + EMSGN("E798: ID is reserved for \":match\": %ld", id); + return; + } + + rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l, + conceal_char); +#endif +} + +/* + * "matcharg()" function + */ + static void +f_matcharg(typval_T *argvars UNUSED, typval_T *rettv) +{ + if (rettv_list_alloc(rettv) == OK) + { +#ifdef FEAT_SEARCH_EXTRA + int id = (int)get_tv_number(&argvars[0]); + matchitem_T *m; + + if (id >= 1 && id <= 3) + { + if ((m = (matchitem_T *)get_match(curwin, id)) != NULL) + { + list_append_string(rettv->vval.v_list, + syn_id2name(m->hlg_id), -1); + list_append_string(rettv->vval.v_list, m->pattern, -1); + } + else + { + list_append_string(rettv->vval.v_list, NULL, -1); + list_append_string(rettv->vval.v_list, NULL, -1); + } + } +#endif + } +} + +/* + * "matchdelete()" function + */ + static void +f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_SEARCH_EXTRA + rettv->vval.v_number = match_delete(curwin, + (int)get_tv_number(&argvars[0]), TRUE); +#endif +} + +/* + * "matchend()" function + */ + static void +f_matchend(typval_T *argvars, typval_T *rettv) +{ + find_some_match(argvars, rettv, 0); +} + +/* + * "matchlist()" function + */ + static void +f_matchlist(typval_T *argvars, typval_T *rettv) +{ + find_some_match(argvars, rettv, 3); +} + +/* + * "matchstr()" function + */ + static void +f_matchstr(typval_T *argvars, typval_T *rettv) +{ + find_some_match(argvars, rettv, 2); +} + +/* + * "matchstrpos()" function + */ + static void +f_matchstrpos(typval_T *argvars, typval_T *rettv) +{ + find_some_match(argvars, rettv, 4); +} + +static void max_min(typval_T *argvars, typval_T *rettv, int domax); + + static void +max_min(typval_T *argvars, typval_T *rettv, int domax) +{ + varnumber_T n = 0; + varnumber_T i; + int error = FALSE; + + if (argvars[0].v_type == VAR_LIST) + { + list_T *l; + listitem_T *li; + + l = argvars[0].vval.v_list; + if (l != NULL) + { + li = l->lv_first; + if (li != NULL) + { + n = get_tv_number_chk(&li->li_tv, &error); + for (;;) + { + li = li->li_next; + if (li == NULL) + break; + i = get_tv_number_chk(&li->li_tv, &error); + 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 = get_tv_number_chk(&HI2DI(hi)->di_tv, &error); + if (first) + { + n = i; + first = FALSE; + } + else if (domax ? i > n : i < n) + n = i; + } + } + } + } + else + EMSG(_(e_listdictarg)); + rettv->vval.v_number = error ? 0 : 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); +} + +static int mkdir_recurse(char_u *dir, int prot); + +/* + * Create the directory in which "dir" is located, and higher levels when + * needed. + */ + static int +mkdir_recurse(char_u *dir, int prot) +{ + char_u *p; + char_u *updir; + int r = FAIL; + + /* Get end of directory name in "dir". + * We're done when it's "/" or "c:/". */ + p = gettail_sep(dir); + if (p <= get_past_head(dir)) + return OK; + + /* If the directory exists we're done. Otherwise: create it.*/ + updir = vim_strnsave(dir, (int)(p - dir)); + if (updir == NULL) + return FAIL; + if (mch_isdir(updir)) + r = OK; + else if (mkdir_recurse(updir, prot) == OK) + r = vim_mkdir_emsg(updir, prot); + vim_free(updir); + return r; +} + +#ifdef vim_mkdir +/* + * "mkdir()" function + */ + static void +f_mkdir(typval_T *argvars, typval_T *rettv) +{ + char_u *dir; + char_u buf[NUMBUFLEN]; + int prot = 0755; + + rettv->vval.v_number = FAIL; + if (check_restricted() || check_secure()) + return; + + dir = get_tv_string_buf(&argvars[0], buf); + if (*dir == NUL) + rettv->vval.v_number = FAIL; + else + { + if (*gettail(dir) == NUL) + /* remove trailing slashes */ + *gettail_sep(dir) = NUL; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + if (argvars[2].v_type != VAR_UNKNOWN) + prot = (int)get_tv_number_chk(&argvars[2], NULL); + if (prot != -1 && STRCMP(get_tv_string(&argvars[1]), "p") == 0) + mkdir_recurse(dir, prot); + } + rettv->vval.v_number = prot == -1 ? FAIL : vim_mkdir_emsg(dir, prot); + } +} +#endif + +/* + * "mode()" function + */ + static void +f_mode(typval_T *argvars, typval_T *rettv) +{ + char_u buf[3]; + + buf[1] = NUL; + buf[2] = NUL; + + if (time_for_testing == 93784) + { + /* Testing the two-character code. */ + buf[0] = 'x'; + buf[1] = '!'; + } + else if (VIsual_active) + { + if (VIsual_select) + buf[0] = VIsual_mode + 's' - 'v'; + else + buf[0] = VIsual_mode; + } + else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE + || State == CONFIRM) + { + buf[0] = 'r'; + if (State == ASKMORE) + buf[1] = 'm'; + else if (State == CONFIRM) + buf[1] = '?'; + } + else if (State == EXTERNCMD) + buf[0] = '!'; + else if (State & INSERT) + { +#ifdef FEAT_VREPLACE + if (State & VREPLACE_FLAG) + { + buf[0] = 'R'; + buf[1] = 'v'; + } + else +#endif + if (State & REPLACE_FLAG) + buf[0] = 'R'; + else + buf[0] = 'i'; + } + else if (State & CMDLINE) + { + buf[0] = 'c'; + if (exmode_active) + buf[1] = 'v'; + } + else if (exmode_active) + { + buf[0] = 'c'; + buf[1] = 'e'; + } + else + { + buf[0] = 'n'; + if (finish_op) + buf[1] = 'o'; + } + + /* Clear out the minor mode when the argument is not a non-zero number or + * non-empty string. */ + if (!non_zero_arg(&argvars[0])) + buf[1] = NUL; + + rettv->vval.v_string = vim_strsave(buf); + rettv->v_type = VAR_STRING; +} + +#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]; + + str = get_tv_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; + + for (lnum = get_tv_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]; + +#ifdef FEAT_MBYTE + if (has_mbyte) + { + int utf8 = 0; + + if (argvars[1].v_type != VAR_UNKNOWN) + utf8 = (int)get_tv_number_chk(&argvars[1], NULL); + if (utf8) + buf[(*utf_char2bytes)((int)get_tv_number(&argvars[0]), buf)] = NUL; + else + buf[(*mb_char2bytes)((int)get_tv_number(&argvars[0]), buf)] = NUL; + } + else +#endif + { + buf[0] = (char_u)get_tv_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) +{ + rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) + | get_tv_number_chk(&argvars[1], NULL); +} + +/* + * "pathshorten()" function + */ + static void +f_pathshorten(typval_T *argvars, typval_T *rettv) +{ + char_u *p; + + rettv->v_type = VAR_STRING; + p = get_tv_string_chk(&argvars[0]); + if (p == NULL) + rettv->vval.v_string = NULL; + else + { + p = vim_strsave(p); + rettv->vval.v_string = p; + if (p != NULL) + shorten_dir(p); + } +} + +#ifdef FEAT_PERL +/* + * "perleval()" function + */ + static void +f_perleval(typval_T *argvars, typval_T *rettv) +{ + char_u *str; + char_u buf[NUMBUFLEN]; + + str = get_tv_string_buf(&argvars[0], buf); + do_perleval(str, rettv); +} +#endif + +#ifdef FEAT_FLOAT +/* + * "pow()" function + */ + static void +f_pow(typval_T *argvars, typval_T *rettv) +{ + float_T fx = 0.0, fy = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &fx) == OK + && get_float_arg(&argvars[1], &fy) == OK) + rettv->vval.v_float = pow(fx, fy); + else + rettv->vval.v_float = 0.0; +} +#endif + +/* + * "prevnonblank()" function + */ + static void +f_prevnonblank(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum; + + lnum = get_tv_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; + + /* Get the required length, allocate the buffer and do it for real. */ + did_emsg = FALSE; + fmt = (char *)get_tv_string_buf(&argvars[0], buf); + len = vim_vsnprintf(NULL, 0, fmt, ap, argvars + 1); + if (!did_emsg) + { + s = alloc(len + 1); + if (s != NULL) + { + rettv->vval.v_string = s; + (void)vim_vsnprintf((char *)s, len + 1, fmt, ap, argvars + 1); + } + } + did_emsg |= saved_did_emsg; +} + +/* + * "pumvisible()" function + */ + static void +f_pumvisible(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_INS_EXPAND + if (pum_visible()) + rettv->vval.v_number = 1; +#endif +} + +#ifdef FEAT_PYTHON3 +/* + * "py3eval()" function + */ + static void +f_py3eval(typval_T *argvars, typval_T *rettv) +{ + char_u *str; + char_u buf[NUMBUFLEN]; + + str = get_tv_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]; + + str = get_tv_string_buf(&argvars[0], buf); + do_pyeval(str, rettv); +} +#endif + +/* + * "range()" function + */ + static void +f_range(typval_T *argvars, typval_T *rettv) +{ + varnumber_T start; + varnumber_T end; + varnumber_T stride = 1; + varnumber_T i; + int error = FALSE; + + start = get_tv_number_chk(&argvars[0], &error); + if (argvars[1].v_type == VAR_UNKNOWN) + { + end = start - 1; + start = 0; + } + else + { + end = get_tv_number_chk(&argvars[1], &error); + if (argvars[2].v_type != VAR_UNKNOWN) + stride = get_tv_number_chk(&argvars[2], &error); + } + + if (error) + return; /* type error; errmsg already given */ + if (stride == 0) + EMSG(_("E726: Stride is zero")); + else if (stride > 0 ? end + 1 < start : end - 1 > start) + EMSG(_("E727: Start past end")); + else + { + if (rettv_list_alloc(rettv) == OK) + for (i = start; stride > 0 ? i <= end : i >= end; i += stride) + if (list_append_number(rettv->vval.v_list, + (varnumber_T)i) == FAIL) + break; + } +} + +/* + * "readfile()" function + */ + static void +f_readfile(typval_T *argvars, typval_T *rettv) +{ + int binary = FALSE; + int failed = FALSE; + char_u *fname; + FILE *fd; + char_u buf[(IOSIZE/256)*256]; /* rounded to avoid odd + 1 */ + int io_size = sizeof(buf); + int readlen; /* size of last fread() */ + char_u *prev = NULL; /* previously read bytes, if any */ + long prevlen = 0; /* length of data in prev */ + long prevsize = 0; /* size of prev buffer */ + long maxline = MAXLNUM; + long cnt = 0; + char_u *p; /* position in buf */ + char_u *start; /* start of current line */ + + if (argvars[1].v_type != VAR_UNKNOWN) + { + if (STRCMP(get_tv_string(&argvars[1]), "b") == 0) + binary = TRUE; + if (argvars[2].v_type != VAR_UNKNOWN) + maxline = (long)get_tv_number(&argvars[2]); + } + + if (rettv_list_alloc(rettv) == FAIL) + return; + + /* Always open the file in binary mode, library functions have a mind of + * their own about CR-LF conversion. */ + fname = get_tv_string(&argvars[0]); + if (*fname == NUL || (fd = mch_fopen((char *)fname, READBIN)) == NULL) + { + EMSG2(_(e_notopen), *fname == NUL ? (char_u *)_("") : fname); + return; + } + + while (cnt < maxline || maxline < 0) + { + readlen = (int)fread(buf, 1, io_size, fd); + + /* This for loop processes what was read, but is also entered at end + * of file so that either: + * - an incomplete line gets written + * - a "binary" file gets an empty line at the end if it ends in a + * newline. */ + for (p = buf, start = buf; + p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary)); + ++p) + { + if (*p == '\n' || readlen <= 0) + { + listitem_T *li; + char_u *s = NULL; + long_u len = p - start; + + /* Finished a line. Remove CRs before NL. */ + if (readlen > 0 && !binary) + { + while (len > 0 && start[len - 1] == '\r') + --len; + /* removal may cross back to the "prev" string */ + if (len == 0) + while (prevlen > 0 && prev[prevlen - 1] == '\r') + --prevlen; + } + if (prevlen == 0) + s = vim_strnsave(start, (int)len); + else + { + /* Change "prev" buffer to be the right size. This way + * the bytes are only copied once, and very long lines are + * allocated only once. */ + if ((s = vim_realloc(prev, prevlen + len + 1)) != NULL) + { + mch_memmove(s + prevlen, start, len); + s[prevlen + len] = NUL; + prev = NULL; /* the list will own the string */ + prevlen = prevsize = 0; + } + } + if (s == NULL) + { + do_outofmem_msg((long_u) prevlen + len + 1); + failed = TRUE; + break; + } + + if ((li = listitem_alloc()) == NULL) + { + vim_free(s); + failed = TRUE; + break; + } + li->li_tv.v_type = VAR_STRING; + li->li_tv.v_lock = 0; + li->li_tv.vval.v_string = s; + list_append(rettv->vval.v_list, li); + + start = p + 1; /* step over newline */ + if ((++cnt >= maxline && maxline >= 0) || readlen <= 0) + break; + } + else if (*p == NUL) + *p = '\n'; +#ifdef FEAT_MBYTE + /* Check for utf8 "bom"; U+FEFF is encoded as EF BB BF. Do this + * when finding the BF and check the previous two bytes. */ + else if (*p == 0xbf && enc_utf8 && !binary) + { + /* Find the two bytes before the 0xbf. If p is at buf, or buf + * + 1, these may be in the "prev" string. */ + char_u back1 = p >= buf + 1 ? p[-1] + : prevlen >= 1 ? prev[prevlen - 1] : NUL; + char_u back2 = p >= buf + 2 ? p[-2] + : p == buf + 1 && prevlen >= 1 ? prev[prevlen - 1] + : prevlen >= 2 ? prev[prevlen - 2] : NUL; + + if (back2 == 0xef && back1 == 0xbb) + { + char_u *dest = p - 2; + + /* Usually a BOM is at the beginning of a file, and so at + * the beginning of a line; then we can just step over it. + */ + if (start == dest) + start = p + 1; + else + { + /* have to shuffle buf to close gap */ + int adjust_prevlen = 0; + + if (dest < buf) + { + adjust_prevlen = (int)(buf - dest); /* must be 1 or 2 */ + dest = buf; + } + if (readlen > p - buf + 1) + mch_memmove(dest, p + 1, readlen - (p - buf) - 1); + readlen -= 3 - adjust_prevlen; + prevlen -= adjust_prevlen; + p = dest - 1; + } + } + } +#endif + } /* for */ + + if (failed || (cnt >= maxline && maxline >= 0) || readlen <= 0) + break; + if (start < p) + { + /* There's part of a line in buf, store it in "prev". */ + if (p - start + prevlen >= prevsize) + { + /* need bigger "prev" buffer */ + char_u *newprev; + + /* A common use case is ordinary text files and "prev" gets a + * fragment of a line, so the first allocation is made + * small, to avoid repeatedly 'allocing' large and + * 'reallocing' small. */ + if (prevsize == 0) + prevsize = (long)(p - start); + else + { + long grow50pc = (prevsize * 3) / 2; + long growmin = (long)((p - start) * 2 + prevlen); + prevsize = grow50pc > growmin ? grow50pc : growmin; + } + newprev = prev == NULL ? alloc(prevsize) + : vim_realloc(prev, prevsize); + if (newprev == NULL) + { + do_outofmem_msg((long_u)prevsize); + failed = TRUE; + break; + } + prev = newprev; + } + /* Add the line part to end of "prev". */ + mch_memmove(prev + prevlen, start, p - start); + prevlen += (long)(p - start); + } + } /* while */ + + /* + * For a negative line count use only the lines at the end of the file, + * free the rest. + */ + if (!failed && maxline < 0) + while (cnt > -maxline) + { + listitem_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); + --cnt; + } + + if (failed) + { + list_free(rettv->vval.v_list); + /* readfile doc says an empty list is returned on error */ + rettv->vval.v_list = list_alloc(); + } + + vim_free(prev); + fclose(fd); +} + +#if defined(FEAT_RELTIME) +static int list2proftime(typval_T *arg, proftime_T *tm); + +/* + * Convert a List to proftime_T. + * Return FAIL when there is something wrong. + */ + static int +list2proftime(typval_T *arg, proftime_T *tm) +{ + long n1, n2; + int error = FALSE; + + if (arg->v_type != VAR_LIST || arg->vval.v_list == NULL + || arg->vval.v_list->lv_len != 2) + return FAIL; + n1 = list_find_nr(arg->vval.v_list, 0L, &error); + n2 = list_find_nr(arg->vval.v_list, 1L, &error); +# ifdef WIN3264 + tm->HighPart = n1; + tm->LowPart = n2; +# else + tm->tv_sec = n1; + tm->tv_usec = n2; +# endif + return error ? FAIL : OK; +} +#endif /* FEAT_RELTIME */ + +/* + * "reltime()" function + */ + static void +f_reltime(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_RELTIME + proftime_T res; + proftime_T start; + + if (argvars[0].v_type == VAR_UNKNOWN) + { + /* No arguments: get current time. */ + profile_start(&res); + } + else if (argvars[1].v_type == VAR_UNKNOWN) + { + if (list2proftime(&argvars[0], &res) == FAIL) + return; + profile_end(&res); + } + else + { + /* Two arguments: compute the difference. */ + if (list2proftime(&argvars[0], &start) == FAIL + || list2proftime(&argvars[1], &res) == FAIL) + return; + profile_sub(&res, &start); + } + + if (rettv_list_alloc(rettv) == OK) + { + long n1, n2; + +# ifdef WIN3264 + n1 = res.HighPart; + n2 = res.LowPart; +# else + n1 = res.tv_sec; + n2 = res.tv_usec; +# endif + list_append_number(rettv->vval.v_list, (varnumber_T)n1); + list_append_number(rettv->vval.v_list, (varnumber_T)n2); + } +#endif +} + +#ifdef FEAT_FLOAT +/* + * "reltimefloat()" function + */ + static void +f_reltimefloat(typval_T *argvars UNUSED, typval_T *rettv) +{ +# ifdef FEAT_RELTIME + proftime_T tm; +# endif + + rettv->v_type = VAR_FLOAT; + rettv->vval.v_float = 0; +# ifdef FEAT_RELTIME + if (list2proftime(&argvars[0], &tm) == OK) + rettv->vval.v_float = profile_float(&tm); +# endif +} +#endif + +/* + * "reltimestr()" function + */ + static void +f_reltimestr(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_RELTIME + proftime_T tm; +#endif + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; +#ifdef FEAT_RELTIME + if (list2proftime(&argvars[0], &tm) == OK) + rettv->vval.v_string = vim_strsave((char_u *)profile_msg(&tm)); +#endif +} + +#if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11) +static void make_connection(void); +static int check_connection(void); + + static void +make_connection(void) +{ + if (X_DISPLAY == NULL +# ifdef FEAT_GUI + && !gui.in_use +# endif + ) + { + x_force_connect = TRUE; + setup_term_clip(); + x_force_connect = FALSE; + } +} + + static int +check_connection(void) +{ + make_connection(); + if (X_DISPLAY == NULL) + { + EMSG(_("E240: No connection to Vim server")); + return FAIL; + } + return OK; +} +#endif + +#ifdef FEAT_CLIENTSERVER + static void +remote_common(typval_T *argvars, typval_T *rettv, int expr) +{ + char_u *server_name; + char_u *keys; + char_u *r = NULL; + char_u buf[NUMBUFLEN]; +# ifdef WIN32 + HWND w; +# else + Window w; +# endif + + if (check_restricted() || check_secure()) + return; + +# ifdef FEAT_X11 + if (check_connection() == FAIL) + return; +# endif + + server_name = get_tv_string_chk(&argvars[0]); + if (server_name == NULL) + return; /* type error; errmsg already given */ + keys = get_tv_string_buf(&argvars[1], buf); +# ifdef WIN32 + if (serverSendToVim(server_name, keys, &r, &w, expr, TRUE) < 0) +# else + if (serverSendToVim(X_DISPLAY, server_name, keys, &r, &w, expr, 0, TRUE) + < 0) +# endif + { + if (r != NULL) + EMSG(r); /* sending worked but evaluation failed */ + else + EMSG2(_("E241: Unable to send to %s"), server_name); + return; + } + + rettv->vval.v_string = r; + + if (argvars[2].v_type != VAR_UNKNOWN) + { + dictitem_T v; + char_u str[30]; + char_u *idvar; + + sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w); + v.di_tv.v_type = VAR_STRING; + v.di_tv.vval.v_string = vim_strsave(str); + idvar = get_tv_string_chk(&argvars[2]); + if (idvar != NULL) + set_var(idvar, &v.di_tv, FALSE); + vim_free(v.di_tv.vval.v_string); + } +} +#endif + +/* + * "remote_expr()" function + */ + static void +f_remote_expr(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; +#ifdef FEAT_CLIENTSERVER + remote_common(argvars, rettv, TRUE); +#endif +} + +/* + * "remote_foreground()" function + */ + static void +f_remote_foreground(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_CLIENTSERVER +# ifdef WIN32 + /* On Win32 it's done in this application. */ + { + char_u *server_name = get_tv_string_chk(&argvars[0]); + + if (server_name != NULL) + serverForeground(server_name); + } +# else + /* Send a foreground() expression to the server. */ + argvars[1].v_type = VAR_STRING; + argvars[1].vval.v_string = vim_strsave((char_u *)"foreground()"); + argvars[2].v_type = VAR_UNKNOWN; + remote_common(argvars, rettv, TRUE); + vim_free(argvars[1].vval.v_string); +# endif +#endif +} + + static void +f_remote_peek(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_CLIENTSERVER + dictitem_T v; + char_u *s = NULL; +# ifdef WIN32 + long_u n = 0; +# endif + char_u *serverid; + + if (check_restricted() || check_secure()) + { + rettv->vval.v_number = -1; + return; + } + serverid = get_tv_string_chk(&argvars[0]); + if (serverid == NULL) + { + rettv->vval.v_number = -1; + return; /* type error; errmsg already given */ + } +# ifdef WIN32 + sscanf((const char *)serverid, SCANF_HEX_LONG_U, &n); + if (n == 0) + rettv->vval.v_number = -1; + else + { + s = serverGetReply((HWND)n, FALSE, FALSE, FALSE); + rettv->vval.v_number = (s != NULL); + } +# else + if (check_connection() == FAIL) + return; + + rettv->vval.v_number = serverPeekReply(X_DISPLAY, + serverStrToWin(serverid), &s); +# endif + + if (argvars[1].v_type != VAR_UNKNOWN && rettv->vval.v_number > 0) + { + char_u *retvar; + + v.di_tv.v_type = VAR_STRING; + v.di_tv.vval.v_string = vim_strsave(s); + retvar = get_tv_string_chk(&argvars[1]); + if (retvar != NULL) + set_var(retvar, &v.di_tv, FALSE); + vim_free(v.di_tv.vval.v_string); + } +#else + rettv->vval.v_number = -1; +#endif +} + + static void +f_remote_read(typval_T *argvars UNUSED, typval_T *rettv) +{ + char_u *r = NULL; + +#ifdef FEAT_CLIENTSERVER + char_u *serverid = get_tv_string_chk(&argvars[0]); + + if (serverid != NULL && !check_restricted() && !check_secure()) + { +# ifdef WIN32 + /* The server's HWND is encoded in the 'id' parameter */ + long_u n = 0; + + sscanf((char *)serverid, SCANF_HEX_LONG_U, &n); + if (n != 0) + r = serverGetReply((HWND)n, FALSE, TRUE, TRUE); + if (r == NULL) +# else + if (check_connection() == FAIL || serverReadReply(X_DISPLAY, + serverStrToWin(serverid), &r, FALSE) < 0) +# endif + EMSG(_("E277: Unable to read a server reply")); + } +#endif + rettv->v_type = VAR_STRING; + rettv->vval.v_string = r; +} + +/* + * "remote_send()" function + */ + static void +f_remote_send(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; +#ifdef FEAT_CLIENTSERVER + remote_common(argvars, rettv, FALSE); +#endif +} + +/* + * "remove()" function + */ + static void +f_remove(typval_T *argvars, typval_T *rettv) +{ + list_T *l; + listitem_T *item, *item2; + listitem_T *li; + long idx; + long end; + char_u *key; + dict_T *d; + dictitem_T *di; + char_u *arg_errmsg = (char_u *)N_("remove() argument"); + + if (argvars[0].v_type == VAR_DICT) + { + if (argvars[2].v_type != VAR_UNKNOWN) + EMSG2(_(e_toomanyarg), "remove()"); + else if ((d = argvars[0].vval.v_dict) != NULL + && !tv_check_lock(d->dv_lock, arg_errmsg, TRUE)) + { + key = get_tv_string_chk(&argvars[1]); + if (key != NULL) + { + di = dict_find(d, key, -1); + if (di == NULL) + EMSG2(_(e_dictkey), key); + else if (!var_check_fixed(di->di_flags, arg_errmsg, TRUE) + && !var_check_ro(di->di_flags, arg_errmsg, TRUE)) + { + *rettv = di->di_tv; + init_tv(&di->di_tv); + dictitem_remove(d, di); + } + } + } + } + else if (argvars[0].v_type != VAR_LIST) + EMSG2(_(e_listdictarg), "remove()"); + else if ((l = argvars[0].vval.v_list) != NULL + && !tv_check_lock(l->lv_lock, arg_errmsg, TRUE)) + { + int error = FALSE; + + idx = (long)get_tv_number_chk(&argvars[1], &error); + if (error) + ; /* type error: do nothing, errmsg already given */ + else if ((item = list_find(l, idx)) == NULL) + EMSGN(_(e_listidx), idx); + else + { + if (argvars[2].v_type == VAR_UNKNOWN) + { + /* Remove one item, return its value. */ + vimlist_remove(l, item, item); + *rettv = item->li_tv; + vim_free(item); + } + else + { + /* Remove range of items, return list with values. */ + end = (long)get_tv_number_chk(&argvars[2], &error); + if (error) + ; /* type error: do nothing */ + else if ((item2 = list_find(l, end)) == NULL) + EMSGN(_(e_listidx), end); + else + { + int cnt = 0; + + for (li = item; li != NULL; li = li->li_next) + { + ++cnt; + if (li == item2) + break; + } + if (li == NULL) /* didn't find "item2" after "item" */ + EMSG(_(e_invrange)); + else + { + vimlist_remove(l, item, item2); + if (rettv_list_alloc(rettv) == OK) + { + l = rettv->vval.v_list; + l->lv_first = item; + l->lv_last = item2; + item->li_prev = NULL; + item2->li_next = NULL; + l->lv_len = cnt; + } + } + } + } + } + } +} + +/* + * "rename({from}, {to})" function + */ + static void +f_rename(typval_T *argvars, typval_T *rettv) +{ + char_u buf[NUMBUFLEN]; + + if (check_restricted() || check_secure()) + rettv->vval.v_number = -1; + else + rettv->vval.v_number = vim_rename(get_tv_string(&argvars[0]), + get_tv_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; + + n = (int)get_tv_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 = get_tv_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; + } +} + +/* + * "resolve()" function + */ + static void +f_resolve(typval_T *argvars, typval_T *rettv) +{ + char_u *p; +#ifdef HAVE_READLINK + char_u *buf = NULL; +#endif + + p = get_tv_string(&argvars[0]); +#ifdef FEAT_SHORTCUT + { + char_u *v = NULL; + + v = mch_resolve_shortcut(p); + if (v != NULL) + rettv->vval.v_string = v; + else + rettv->vval.v_string = vim_strsave(p); + } +#else +# ifdef HAVE_READLINK + { + char_u *cpy; + int len; + char_u *remain = NULL; + char_u *q; + int is_relative_to_current = FALSE; + int has_trailing_pathsep = FALSE; + int limit = 100; + + p = vim_strsave(p); + + if (p[0] == '.' && (vim_ispathsep(p[1]) + || (p[1] == '.' && (vim_ispathsep(p[2]))))) + is_relative_to_current = TRUE; + + len = STRLEN(p); + if (len > 0 && after_pathsep(p, p + len)) + { + has_trailing_pathsep = TRUE; + p[len - 1] = NUL; /* the trailing slash breaks readlink() */ + } + + q = getnextcomp(p); + if (*q != NUL) + { + /* Separate the first path component in "p", and keep the + * remainder (beginning with the path separator). */ + remain = vim_strsave(q - 1); + q[-1] = NUL; + } + + buf = alloc(MAXPATHL + 1); + if (buf == NULL) + goto fail; + + for (;;) + { + for (;;) + { + len = readlink((char *)p, (char *)buf, MAXPATHL); + if (len <= 0) + break; + buf[len] = NUL; + + if (limit-- == 0) + { + vim_free(p); + vim_free(remain); + EMSG(_("E655: Too many symbolic links (cycle?)")); + rettv->vval.v_string = NULL; + goto fail; + } + + /* Ensure that the result will have a trailing path separator + * if the argument has one. */ + if (remain == NULL && has_trailing_pathsep) + add_pathsep(buf); + + /* Separate the first path component in the link value and + * concatenate the remainders. */ + q = getnextcomp(vim_ispathsep(*buf) ? buf + 1 : buf); + if (*q != NUL) + { + if (remain == NULL) + remain = vim_strsave(q - 1); + else + { + cpy = concat_str(q - 1, remain); + if (cpy != NULL) + { + vim_free(remain); + remain = cpy; + } + } + q[-1] = NUL; + } + + q = gettail(p); + if (q > p && *q == NUL) + { + /* Ignore trailing path separator. */ + q[-1] = NUL; + q = gettail(p); + } + if (q > p && !mch_isFullName(buf)) + { + /* symlink is relative to directory of argument */ + cpy = alloc((unsigned)(STRLEN(p) + STRLEN(buf) + 1)); + if (cpy != NULL) + { + STRCPY(cpy, p); + STRCPY(gettail(cpy), buf); + vim_free(p); + p = cpy; + } + } + else + { + vim_free(p); + p = vim_strsave(buf); + } + } + + if (remain == NULL) + break; + + /* Append the first path component of "remain" to "p". */ + q = getnextcomp(remain + 1); + len = q - remain - (*q != NUL); + cpy = vim_strnsave(p, STRLEN(p) + len); + if (cpy != NULL) + { + STRNCAT(cpy, remain, len); + vim_free(p); + p = cpy; + } + /* Shorten "remain". */ + if (*q != NUL) + STRMOVE(remain, q - 1); + else + { + vim_free(remain); + remain = NULL; + } + } + + /* If the result is a relative path name, make it explicitly relative to + * the current directory if and only if the argument had this form. */ + if (!vim_ispathsep(*p)) + { + if (is_relative_to_current + && *p != NUL + && !(p[0] == '.' + && (p[1] == NUL + || vim_ispathsep(p[1]) + || (p[1] == '.' + && (p[2] == NUL + || vim_ispathsep(p[2])))))) + { + /* Prepend "./". */ + cpy = concat_str((char_u *)"./", p); + if (cpy != NULL) + { + vim_free(p); + p = cpy; + } + } + else if (!is_relative_to_current) + { + /* Strip leading "./". */ + q = p; + while (q[0] == '.' && vim_ispathsep(q[1])) + q += 2; + if (q > p) + STRMOVE(p, p + 2); + } + } + + /* Ensure that the result will have no trailing path separator + * if the argument had none. But keep "/" or "//". */ + if (!has_trailing_pathsep) + { + q = p + STRLEN(p); + if (after_pathsep(p, q)) + *gettail_sep(p) = NUL; + } + + rettv->vval.v_string = p; + } +# else + rettv->vval.v_string = vim_strsave(p); +# endif +#endif + + simplify_filename(rettv->vval.v_string); + +#ifdef HAVE_READLINK +fail: + vim_free(buf); +#endif + rettv->v_type = VAR_STRING; +} + +/* + * "reverse({list})" function + */ + static void +f_reverse(typval_T *argvars, typval_T *rettv) +{ + list_T *l; + listitem_T *li, *ni; + + if (argvars[0].v_type != VAR_LIST) + EMSG2(_(e_listarg), "reverse()"); + else if ((l = argvars[0].vval.v_list) != NULL + && !tv_check_lock(l->lv_lock, + (char_u *)N_("reverse() argument"), TRUE)) + { + li = l->lv_last; + l->lv_first = l->lv_last = NULL; + l->lv_len = 0; + while (li != NULL) + { + ni = li->li_prev; + list_append(l, li); + li = ni; + } + rettv->vval.v_list = l; + rettv->v_type = VAR_LIST; + ++l->lv_refcount; + l->lv_idx = l->lv_len - l->lv_idx - 1; + } +} + +#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 */ + +static int get_search_arg(typval_T *varp, int *flagsp); + +/* + * 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 = get_tv_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) + { + EMSG2(_(e_invarg2), 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; + proftime_T tm; +#ifdef FEAT_RELTIME + long time_limit = 0; +#endif + int options = SEARCH_KEEP; + int subpatnum; + + pat = get_tv_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 and timeout. */ + if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) + { + lnum_stop = (long)get_tv_number_chk(&argvars[2], NULL); + if (lnum_stop < 0) + goto theend; +#ifdef FEAT_RELTIME + if (argvars[3].v_type != VAR_UNKNOWN) + { + time_limit = (long)get_tv_number_chk(&argvars[3], NULL); + if (time_limit < 0) + goto theend; + } +#endif + } + +#ifdef FEAT_RELTIME + /* Set the time limit, if there is one. */ + profile_setlimit(time_limit, &tm); +#endif + + /* + * 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))) + { + EMSG2(_(e_invarg2), get_tv_string(&argvars[1])); + goto theend; + } + + pos = save_cursor = curwin->w_cursor; + subpatnum = searchit(curwin, curbuf, &pos, dir, pat, 1L, + options, RE_SEARCH, (linenr_T)lnum_stop, &tm); + 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_FLOAT + +/* + * round() is not in C90, use ceil() or floor() instead. + */ + float_T +vim_round(float_T f) +{ + return f > 0 ? floor(f + 0.5) : ceil(f - 0.5); +} + +/* + * "round({float})" function + */ + static void +f_round(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = vim_round(f); + else + rettv->vval.v_float = 0.0; +} +#endif + +/* + * "screenattr()" function + */ + static void +f_screenattr(typval_T *argvars, typval_T *rettv) +{ + int row; + int col; + int c; + + row = (int)get_tv_number_chk(&argvars[0], NULL) - 1; + col = (int)get_tv_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; + + row = (int)get_tv_number_chk(&argvars[0], NULL) - 1; + col = (int)get_tv_number_chk(&argvars[1], NULL) - 1; + if (row < 0 || row >= screen_Rows + || col < 0 || col >= screen_Columns) + c = -1; + else + { + off = LineOffset[row] + col; +#ifdef FEAT_MBYTE + if (enc_utf8 && ScreenLinesUC[off] != 0) + c = ScreenLinesUC[off]; + else +#endif + c = ScreenLines[off]; + } + rettv->vval.v_number = c; +} + +/* + * "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; +} + +/* + * "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 = 1; + int thisblock = 0; + int error = FALSE; + char_u *name; + + rettv->vval.v_number = 1; /* default: FAIL */ + + name = get_tv_string_chk(&argvars[0]); + if (argvars[1].v_type != VAR_UNKNOWN) + { + locally = (int)get_tv_number_chk(&argvars[1], &error) == 0; + if (!error && argvars[2].v_type != VAR_UNKNOWN) + thisblock = (int)get_tv_number_chk(&argvars[2], &error) != 0; + } + 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; + char_u *skip; + int save_p_ws = p_ws; + int dir; + int flags = 0; + char_u nbuf1[NUMBUFLEN]; + char_u nbuf2[NUMBUFLEN]; + char_u nbuf3[NUMBUFLEN]; + int retval = 0; /* default: FAIL */ + long lnum_stop = 0; + long time_limit = 0; + + /* Get the three pattern arguments: start, middle, end. */ + spat = get_tv_string_chk(&argvars[0]); + mpat = get_tv_string_buf_chk(&argvars[1], nbuf1); + epat = get_tv_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))) + { + EMSG2(_(e_invarg2), get_tv_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 = (char_u *)""; + else + { + skip = get_tv_string_buf_chk(&argvars[4], nbuf3); + if (argvars[5].v_type != VAR_UNKNOWN) + { + lnum_stop = (long)get_tv_number_chk(&argvars[5], NULL); + if (lnum_stop < 0) + goto theend; +#ifdef FEAT_RELTIME + if (argvars[6].v_type != VAR_UNKNOWN) + { + time_limit = (long)get_tv_number_chk(&argvars[6], NULL); + if (time_limit < 0) + goto theend; + } +#endif + } + } + if (skip == NULL) + goto theend; /* type error */ + + 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 */ + char_u *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 err; + int options = SEARCH_KEEP; + proftime_T tm; + + /* Make 'cpoptions' empty, the 'l' flag should not be used here. */ + save_cpo = p_cpo; + p_cpo = empty_option; + +#ifdef FEAT_RELTIME + /* Set the time limit, if there is one. */ + profile_setlimit(time_limit, &tm); +#endif + + /* Make two search patterns: start/end (pat2, for in nested pairs) and + * start/middle/end (pat3, for the top pair). */ + pat2 = alloc((unsigned)(STRLEN(spat) + STRLEN(epat) + 15)); + pat3 = alloc((unsigned)(STRLEN(spat) + STRLEN(mpat) + STRLEN(epat) + 23)); + if (pat2 == NULL || pat3 == NULL) + goto theend; + sprintf((char *)pat2, "\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat); + if (*mpat == NUL) + STRCPY(pat3, pat2); + else + sprintf((char *)pat3, "\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)", + spat, epat, mpat); + if (flags & SP_START) + options |= SEARCH_START; + + save_cursor = curwin->w_cursor; + pos = curwin->w_cursor; + clearpos(&firstpos); + clearpos(&foundpos); + pat = pat3; + for (;;) + { + n = searchit(curwin, curbuf, &pos, dir, pat, 1L, + options, RE_SEARCH, lnum_stop, &tm); + if (n == FAIL || (firstpos.lnum != 0 && equalpos(pos, firstpos))) + /* didn't find it or found the first match again: FAIL */ + break; + + if (firstpos.lnum == 0) + firstpos = pos; + if (equalpos(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 (*skip != NUL) + { + save_pos = curwin->w_cursor; + curwin->w_cursor = pos; + r = eval_to_bool(skip, &err, NULL, FALSE); + 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: + vim_free(pat2); + vim_free(pat3); + if (p_cpo == empty_option) + p_cpo = save_cpo; + else + /* Darn, evaluating the {skip} expression changed the value. */ + 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); +} + + static void +f_server2client(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_CLIENTSERVER + char_u buf[NUMBUFLEN]; + char_u *server = get_tv_string_chk(&argvars[0]); + char_u *reply = get_tv_string_buf_chk(&argvars[1], buf); + + rettv->vval.v_number = -1; + if (server == NULL || reply == NULL) + return; + if (check_restricted() || check_secure()) + return; +# ifdef FEAT_X11 + if (check_connection() == FAIL) + return; +# endif + + if (serverSendReply(server, reply) < 0) + { + EMSG(_("E258: Unable to send to client")); + return; + } + rettv->vval.v_number = 0; +#else + rettv->vval.v_number = -1; +#endif +} + + static void +f_serverlist(typval_T *argvars UNUSED, typval_T *rettv) +{ + char_u *r = NULL; + +#ifdef FEAT_CLIENTSERVER +# ifdef WIN32 + r = serverGetVimNames(); +# else + make_connection(); + if (X_DISPLAY != NULL) + r = serverGetVimNames(X_DISPLAY); +# endif +#endif + rettv->v_type = VAR_STRING; + rettv->vval.v_string = r; +} + +/* + * "setbufvar()" function + */ + static void +f_setbufvar(typval_T *argvars, typval_T *rettv UNUSED) +{ + buf_T *buf; + char_u *varname, *bufvarname; + typval_T *varp; + char_u nbuf[NUMBUFLEN]; + + if (check_restricted() || check_secure()) + return; + (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ + varname = get_tv_string_chk(&argvars[1]); + buf = get_buf_tv(&argvars[0], FALSE); + varp = &argvars[2]; + + if (buf != NULL && varname != NULL && varp != NULL) + { + if (*varname == '&') + { + long numval; + char_u *strval; + int error = FALSE; + aco_save_T aco; + + /* set curbuf to be our buf, temporarily */ + aucmd_prepbuf(&aco, buf); + + ++varname; + numval = (long)get_tv_number_chk(varp, &error); + strval = get_tv_string_buf_chk(varp, nbuf); + if (!error && strval != NULL) + set_option_value(varname, numval, strval, OPT_LOCAL); + + /* reset notion of buffer */ + aucmd_restbuf(&aco); + } + else + { + buf_T *save_curbuf = curbuf; + + bufvarname = alloc((unsigned)STRLEN(varname) + 3); + if (bufvarname != NULL) + { + curbuf = buf; + STRCPY(bufvarname, "b:"); + STRCPY(bufvarname + 2, varname); + set_var(bufvarname, varp, TRUE); + vim_free(bufvarname); + curbuf = save_curbuf; + } + } + } +} + + static void +f_setcharsearch(typval_T *argvars, typval_T *rettv UNUSED) +{ + dict_T *d; + dictitem_T *di; + char_u *csearch; + + if (argvars[0].v_type != VAR_DICT) + { + EMSG(_(e_dictreq)); + return; + } + + if ((d = argvars[0].vval.v_dict) != NULL) + { + csearch = get_dict_string(d, (char_u *)"char", FALSE); + if (csearch != NULL) + { +#ifdef FEAT_MBYTE + if (enc_utf8) + { + int pcc[MAX_MCO]; + int c = utfc_ptr2char(csearch, pcc); + + set_last_csearch(c, csearch, utfc_ptr2len(csearch)); + } + else +#endif + set_last_csearch(PTR2CHAR(csearch), + csearch, MB_PTR2LEN(csearch)); + } + + di = dict_find(d, (char_u *)"forward", -1); + if (di != NULL) + set_csearch_direction((int)get_tv_number(&di->di_tv) + ? FORWARD : BACKWARD); + + di = dict_find(d, (char_u *)"until", -1); + if (di != NULL) + set_csearch_until(!!get_tv_number(&di->di_tv)); + } +} + +/* + * "setcmdpos()" function + */ + static void +f_setcmdpos(typval_T *argvars, typval_T *rettv) +{ + int pos = (int)get_tv_number(&argvars[0]) - 1; + + if (pos >= 0) + rettv->vval.v_number = set_cmdline_pos(pos); +} + +/* + * "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; + fname = get_tv_string_chk(&argvars[0]); + if (fname == NULL) + return; + mode_str = get_tv_string_buf_chk(&argvars[1], modebuf); + if (mode_str == NULL) + return; + if (STRLEN(mode_str) != 9) + { + EMSG2(_(e_invarg2), 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; +} + +/* + * "setline()" function + */ + static void +f_setline(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum; + char_u *line = NULL; + list_T *l = NULL; + listitem_T *li = NULL; + long added = 0; + linenr_T lcount = curbuf->b_ml.ml_line_count; + + lnum = get_tv_lnum(&argvars[0]); + if (argvars[1].v_type == VAR_LIST) + { + l = argvars[1].vval.v_list; + li = l->lv_first; + } + else + line = get_tv_string_chk(&argvars[1]); + + /* default result is zero == OK */ + for (;;) + { + if (l != NULL) + { + /* list argument, get next string */ + if (li == NULL) + break; + line = get_tv_string_chk(&li->li_tv); + li = li->li_next; + } + + rettv->vval.v_number = 1; /* FAIL */ + if (line == NULL || lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) + break; + + /* When coming here from Insert mode, sync undo, so that this can be + * undone separately from what was previously inserted. */ + if (u_sync_once == 2) + { + u_sync_once = 1; /* notify that u_sync() was called */ + u_sync(TRUE); + } + + if (lnum <= curbuf->b_ml.ml_line_count) + { + /* existing line, replace it */ + if (u_savesub(lnum) == OK && ml_replace(lnum, line, TRUE) == OK) + { + changed_bytes(lnum, 0); + if (lnum == curwin->w_cursor.lnum) + check_cursor_col(); + rettv->vval.v_number = 0; /* OK */ + } + } + else if (added > 0 || u_save(lnum - 1, lnum) == OK) + { + /* lnum is one past the last line, append the line */ + ++added; + if (ml_append(lnum - 1, line, (colnr_T)0, FALSE) == OK) + rettv->vval.v_number = 0; /* OK */ + } + + if (l == NULL) /* only one string argument */ + break; + ++lnum; + } + + if (added > 0) + appended_lines_mark(lcount, added); +} + +static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, typval_T *rettv); + +/* + * Used by "setqflist()" and "setloclist()" functions + */ + static void +set_qf_ll_list( + win_T *wp UNUSED, + typval_T *list_arg UNUSED, + typval_T *action_arg UNUSED, + typval_T *rettv) +{ +#ifdef FEAT_QUICKFIX + static char *e_invact = N_("E927: Invalid action: '%s'"); + char_u *act; + int action = 0; +#endif + + rettv->vval.v_number = -1; + +#ifdef FEAT_QUICKFIX + if (list_arg->v_type != VAR_LIST) + EMSG(_(e_listreq)); + else + { + list_T *l = list_arg->vval.v_list; + + if (action_arg->v_type == VAR_STRING) + { + act = get_tv_string_chk(action_arg); + if (act == NULL) + return; /* type error; errmsg already given */ + if ((*act == 'a' || *act == 'r' || *act == ' ') && act[1] == NUL) + action = *act; + else + EMSG2(_(e_invact), act); + } + else if (action_arg->v_type == VAR_UNKNOWN) + action = ' '; + else + EMSG(_(e_stringreq)); + + if (l != NULL && action && set_errorlist(wp, l, action, + (char_u *)(wp == NULL ? "setqflist()" : "setloclist()")) == OK) + rettv->vval.v_number = 0; + } +#endif +} + +/* + * "setloclist()" function + */ + static void +f_setloclist(typval_T *argvars, typval_T *rettv) +{ + win_T *win; + + rettv->vval.v_number = -1; + + win = find_win_by_nr(&argvars[0], NULL); + if (win != NULL) + set_qf_ll_list(win, &argvars[1], &argvars[2], rettv); +} + +/* + * "setmatches()" function + */ + static void +f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_SEARCH_EXTRA + list_T *l; + listitem_T *li; + dict_T *d; + list_T *s = NULL; + + rettv->vval.v_number = -1; + if (argvars[0].v_type != VAR_LIST) + { + EMSG(_(e_listreq)); + return; + } + if ((l = argvars[0].vval.v_list) != NULL) + { + + /* To some extent make sure that we are dealing with a list from + * "getmatches()". */ + li = l->lv_first; + while (li != NULL) + { + if (li->li_tv.v_type != VAR_DICT + || (d = li->li_tv.vval.v_dict) == NULL) + { + EMSG(_(e_invarg)); + return; + } + if (!(dict_find(d, (char_u *)"group", -1) != NULL + && (dict_find(d, (char_u *)"pattern", -1) != NULL + || dict_find(d, (char_u *)"pos1", -1) != NULL) + && dict_find(d, (char_u *)"priority", -1) != NULL + && dict_find(d, (char_u *)"id", -1) != NULL)) + { + EMSG(_(e_invarg)); + return; + } + li = li->li_next; + } + + clear_matches(curwin); + li = l->lv_first; + while (li != NULL) + { + int i = 0; + char_u buf[5]; + dictitem_T *di; + char_u *group; + int priority; + int id; + char_u *conceal; + + d = li->li_tv.vval.v_dict; + if (dict_find(d, (char_u *)"pattern", -1) == NULL) + { + if (s == NULL) + { + s = list_alloc(); + if (s == NULL) + return; + } + + /* match from matchaddpos() */ + for (i = 1; i < 9; i++) + { + sprintf((char *)buf, (char *)"pos%d", i); + if ((di = dict_find(d, (char_u *)buf, -1)) != NULL) + { + if (di->di_tv.v_type != VAR_LIST) + return; + + list_append_tv(s, &di->di_tv); + s->lv_refcount++; + } + else + break; + } + } + + group = get_dict_string(d, (char_u *)"group", FALSE); + priority = (int)get_dict_number(d, (char_u *)"priority"); + id = (int)get_dict_number(d, (char_u *)"id"); + conceal = dict_find(d, (char_u *)"conceal", -1) != NULL + ? get_dict_string(d, (char_u *)"conceal", FALSE) + : NULL; + if (i == 0) + { + match_add(curwin, group, + get_dict_string(d, (char_u *)"pattern", FALSE), + priority, id, NULL, conceal); + } + else + { + match_add(curwin, group, NULL, priority, id, s, conceal); + list_unref(s); + s = NULL; + } + + li = li->li_next; + } + rettv->vval.v_number = 0; + } +#endif +} + +/* + * "setpos()" function + */ + static void +f_setpos(typval_T *argvars, typval_T *rettv) +{ + pos_T pos; + int fnum; + char_u *name; + colnr_T curswant = -1; + + rettv->vval.v_number = -1; + name = get_tv_string_chk(argvars); + if (name != NULL) + { + if (list2fpos(&argvars[1], &pos, &fnum, &curswant) == OK) + { + if (--pos.col < 0) + pos.col = 0; + if (name[0] == '.' && name[1] == NUL) + { + /* set cursor */ + if (fnum == curbuf->b_fnum) + { + 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 + EMSG(_(e_invarg)); + } + 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_invarg)); + } + } +} + +/* + * "setqflist()" function + */ + static void +f_setqflist(typval_T *argvars, typval_T *rettv) +{ + set_qf_ll_list(NULL, &argvars[0], &argvars[1], rettv); +} + +/* + * "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; + + block_len = -1; + yank_type = MAUTO; + append = FALSE; + + strregname = get_tv_string_chk(argvars); + rettv->vval.v_number = 1; /* FAIL is default */ + + if (strregname == NULL) + return; /* type error; errmsg already given */ + regname = *strregname; + if (regname == 0 || regname == '@') + regname = '"'; + + if (argvars[2].v_type != VAR_UNKNOWN) + { + stropt = get_tv_string_chk(&argvars[2]); + if (stropt == NULL) + return; /* type error */ + for (; *stropt != NUL; ++stropt) + switch (*stropt) + { + case 'a': case 'A': /* append */ + append = TRUE; + break; + 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; + } + } + + if (argvars[1].v_type == VAR_LIST) + { + char_u **lstval; + char_u **allocval; + char_u buf[NUMBUFLEN]; + char_u **curval; + char_u **curallocval; + list_T *ll = argvars[1].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 = (char_u **)alloc(sizeof(char_u *) * ((len + 1) * 2)); + if (lstval == NULL) + return; + curval = lstval; + allocval = lstval + len + 2; + curallocval = allocval; + + for (li = ll == NULL ? NULL : ll->lv_first; li != NULL; + li = li->li_next) + { + strval = get_tv_string_buf_chk(&li->li_tv, buf); + if (strval == NULL) + goto free_lstval; + if (strval == buf) + { + /* Need to make a copy, next get_tv_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 + { + strval = get_tv_string_chk(&argvars[1]); + if (strval == NULL) + return; + write_reg_contents_ex(regname, strval, -1, + append, yank_type, block_len); + } + rettv->vval.v_number = 0; +} + +/* + * "settabvar()" function + */ + static void +f_settabvar(typval_T *argvars, typval_T *rettv) +{ +#ifdef FEAT_WINDOWS + tabpage_T *save_curtab; + tabpage_T *tp; +#endif + char_u *varname, *tabvarname; + typval_T *varp; + + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) + return; + +#ifdef FEAT_WINDOWS + tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); +#endif + varname = get_tv_string_chk(&argvars[1]); + varp = &argvars[2]; + + if (varname != NULL && varp != NULL +#ifdef FEAT_WINDOWS + && tp != NULL +#endif + ) + { +#ifdef FEAT_WINDOWS + save_curtab = curtab; + goto_tabpage_tp(tp, FALSE, FALSE); +#endif + + tabvarname = alloc((unsigned)STRLEN(varname) + 3); + if (tabvarname != NULL) + { + STRCPY(tabvarname, "t:"); + STRCPY(tabvarname + 2, varname); + set_var(tabvarname, varp, TRUE); + vim_free(tabvarname); + } + +#ifdef FEAT_WINDOWS + /* Restore current tabpage */ + if (valid_tabpage(save_curtab)) + goto_tabpage_tp(save_curtab, FALSE, FALSE); +#endif + } +} + +/* + * "settabwinvar()" function + */ + static void +f_settabwinvar(typval_T *argvars, typval_T *rettv) +{ + setwinvar(argvars, rettv, 1); +} + +/* + * "setwinvar()" function + */ + static void +f_setwinvar(typval_T *argvars, typval_T *rettv) +{ + setwinvar(argvars, rettv, 0); +} + +#ifdef FEAT_CRYPT +/* + * "sha256({string})" function + */ + static void +f_sha256(typval_T *argvars, typval_T *rettv) +{ + char_u *p; + + p = get_tv_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) +{ + rettv->vval.v_string = vim_strsave_shellescape( + get_tv_string(&argvars[0]), non_zero_arg(&argvars[1]), TRUE); + rettv->v_type = VAR_STRING; +} + +/* + * shiftwidth() function + */ + static void +f_shiftwidth(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->vval.v_number = get_sw_value(curbuf); +} + +/* + * "simplify()" function + */ + static void +f_simplify(typval_T *argvars, typval_T *rettv) +{ + char_u *p; + + p = get_tv_string(&argvars[0]); + rettv->vval.v_string = vim_strsave(p); + simplify_filename(rettv->vval.v_string); /* simplify in place */ + rettv->v_type = VAR_STRING; +} + +#ifdef FEAT_FLOAT +/* + * "sin()" function + */ + static void +f_sin(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = sin(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "sinh()" function + */ + static void +f_sinh(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = sinh(f); + else + rettv->vval.v_float = 0.0; +} +#endif + +static int +#ifdef __BORLANDC__ + _RTLENTRYF +#endif + item_compare(const void *s1, const void *s2); +static int +#ifdef __BORLANDC__ + _RTLENTRYF +#endif + item_compare2(const void *s1, const void *s2); + +/* struct used in the array that's given to qsort() */ +typedef struct +{ + listitem_T *item; + int idx; +} sortItem_T; + +/* struct storing information about current sort */ +typedef struct +{ + int item_compare_ic; + int item_compare_numeric; + int item_compare_numbers; +#ifdef FEAT_FLOAT + int item_compare_float; +#endif + char_u *item_compare_func; + partial_T *item_compare_partial; + dict_T *item_compare_selfdict; + int item_compare_func_err; + int item_compare_keep_zero; +} sortinfo_T; +static sortinfo_T *sortinfo = NULL; +static void do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort); +#define ITEM_COMPARE_FAIL 999 + +/* + * Compare functions for f_sort() and f_uniq() below. + */ + static int +#ifdef __BORLANDC__ +_RTLENTRYF +#endif +item_compare(const void *s1, const void *s2) +{ + sortItem_T *si1, *si2; + typval_T *tv1, *tv2; + char_u *p1, *p2; + char_u *tofree1 = NULL, *tofree2 = NULL; + int res; + char_u numbuf1[NUMBUFLEN]; + char_u numbuf2[NUMBUFLEN]; + + si1 = (sortItem_T *)s1; + si2 = (sortItem_T *)s2; + tv1 = &si1->item->li_tv; + tv2 = &si2->item->li_tv; + + if (sortinfo->item_compare_numbers) + { + varnumber_T v1 = get_tv_number(tv1); + varnumber_T v2 = get_tv_number(tv2); + + return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + } + +#ifdef FEAT_FLOAT + if (sortinfo->item_compare_float) + { + float_T v1 = get_tv_float(tv1); + float_T v2 = get_tv_float(tv2); + + return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + } +#endif + + /* tv2string() puts quotes around a string and allocates memory. Don't do + * that for string variables. Use a single quote when comparing with a + * non-string to do what the docs promise. */ + if (tv1->v_type == VAR_STRING) + { + if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) + p1 = (char_u *)"'"; + else + p1 = tv1->vval.v_string; + } + else + p1 = tv2string(tv1, &tofree1, numbuf1, 0); + if (tv2->v_type == VAR_STRING) + { + if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) + p2 = (char_u *)"'"; + else + p2 = tv2->vval.v_string; + } + else + p2 = tv2string(tv2, &tofree2, numbuf2, 0); + if (p1 == NULL) + p1 = (char_u *)""; + if (p2 == NULL) + p2 = (char_u *)""; + if (!sortinfo->item_compare_numeric) + { + if (sortinfo->item_compare_ic) + res = STRICMP(p1, p2); + else + res = STRCMP(p1, p2); + } + else + { + double n1, n2; + n1 = strtod((char *)p1, (char **)&p1); + n2 = strtod((char *)p2, (char **)&p2); + res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; + } + + /* When the result would be zero, compare the item indexes. Makes the + * sort stable. */ + if (res == 0 && !sortinfo->item_compare_keep_zero) + res = si1->idx > si2->idx ? 1 : -1; + + vim_free(tofree1); + vim_free(tofree2); + return res; +} + + static int +#ifdef __BORLANDC__ +_RTLENTRYF +#endif +item_compare2(const void *s1, const void *s2) +{ + sortItem_T *si1, *si2; + int res; + typval_T rettv; + typval_T argv[3]; + int dummy; + char_u *func_name; + partial_T *partial = sortinfo->item_compare_partial; + + /* shortcut after failure in previous call; compare all items equal */ + if (sortinfo->item_compare_func_err) + return 0; + + si1 = (sortItem_T *)s1; + si2 = (sortItem_T *)s2; + + if (partial == NULL) + func_name = sortinfo->item_compare_func; + else + func_name = partial->pt_name; + + /* Copy the values. This is needed to be able to set v_lock to VAR_FIXED + * in the copy without changing the original list items. */ + copy_tv(&si1->item->li_tv, &argv[0]); + copy_tv(&si2->item->li_tv, &argv[1]); + + rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ + res = call_func(func_name, (int)STRLEN(func_name), + &rettv, 2, argv, 0L, 0L, &dummy, TRUE, + partial, sortinfo->item_compare_selfdict); + clear_tv(&argv[0]); + clear_tv(&argv[1]); + + if (res == FAIL) + res = ITEM_COMPARE_FAIL; + else + res = (int)get_tv_number_chk(&rettv, &sortinfo->item_compare_func_err); + if (sortinfo->item_compare_func_err) + res = ITEM_COMPARE_FAIL; /* return value has wrong type */ + clear_tv(&rettv); + + /* When the result would be zero, compare the pointers themselves. Makes + * the sort stable. */ + if (res == 0 && !sortinfo->item_compare_keep_zero) + res = si1->idx > si2->idx ? 1 : -1; + + return res; +} + +/* + * "sort({list})" function + */ + static void +do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort) +{ + list_T *l; + listitem_T *li; + sortItem_T *ptrs; + sortinfo_T *old_sortinfo; + sortinfo_T info; + long len; + long i; + + /* Pointer to current info struct used in compare function. Save and + * restore the current one for nested calls. */ + old_sortinfo = sortinfo; + sortinfo = &info; + + if (argvars[0].v_type != VAR_LIST) + EMSG2(_(e_listarg), sort ? "sort()" : "uniq()"); + else + { + l = argvars[0].vval.v_list; + if (l == NULL || tv_check_lock(l->lv_lock, + (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")), + TRUE)) + goto theend; + rettv->vval.v_list = l; + rettv->v_type = VAR_LIST; + ++l->lv_refcount; + + len = list_len(l); + if (len <= 1) + goto theend; /* short list sorts pretty quickly */ + + info.item_compare_ic = FALSE; + info.item_compare_numeric = FALSE; + info.item_compare_numbers = FALSE; +#ifdef FEAT_FLOAT + info.item_compare_float = FALSE; +#endif + info.item_compare_func = NULL; + info.item_compare_partial = NULL; + info.item_compare_selfdict = NULL; + if (argvars[1].v_type != VAR_UNKNOWN) + { + /* optional second argument: {func} */ + if (argvars[1].v_type == VAR_FUNC) + info.item_compare_func = argvars[1].vval.v_string; + else if (argvars[1].v_type == VAR_PARTIAL) + info.item_compare_partial = argvars[1].vval.v_partial; + else + { + int error = FALSE; + + i = (long)get_tv_number_chk(&argvars[1], &error); + if (error) + goto theend; /* type error; errmsg already given */ + if (i == 1) + info.item_compare_ic = TRUE; + else if (argvars[1].v_type != VAR_NUMBER) + info.item_compare_func = get_tv_string(&argvars[1]); + else if (i != 0) + { + EMSG(_(e_invarg)); + goto theend; + } + if (info.item_compare_func != NULL) + { + if (*info.item_compare_func == NUL) + { + /* empty string means default sort */ + info.item_compare_func = NULL; + } + else if (STRCMP(info.item_compare_func, "n") == 0) + { + info.item_compare_func = NULL; + info.item_compare_numeric = TRUE; + } + else if (STRCMP(info.item_compare_func, "N") == 0) + { + info.item_compare_func = NULL; + info.item_compare_numbers = TRUE; + } +#ifdef FEAT_FLOAT + else if (STRCMP(info.item_compare_func, "f") == 0) + { + info.item_compare_func = NULL; + info.item_compare_float = TRUE; + } +#endif + else if (STRCMP(info.item_compare_func, "i") == 0) + { + info.item_compare_func = NULL; + info.item_compare_ic = TRUE; + } + } + } + + if (argvars[2].v_type != VAR_UNKNOWN) + { + /* optional third argument: {dict} */ + if (argvars[2].v_type != VAR_DICT) + { + EMSG(_(e_dictreq)); + goto theend; + } + info.item_compare_selfdict = argvars[2].vval.v_dict; + } + } + + /* Make an array with each entry pointing to an item in the List. */ + ptrs = (sortItem_T *)alloc((int)(len * sizeof(sortItem_T))); + if (ptrs == NULL) + goto theend; + + i = 0; + if (sort) + { + /* sort(): ptrs will be the list to sort */ + for (li = l->lv_first; li != NULL; li = li->li_next) + { + ptrs[i].item = li; + ptrs[i].idx = i; + ++i; + } + + info.item_compare_func_err = FALSE; + info.item_compare_keep_zero = FALSE; + /* test the compare function */ + if ((info.item_compare_func != NULL + || info.item_compare_partial != NULL) + && item_compare2((void *)&ptrs[0], (void *)&ptrs[1]) + == ITEM_COMPARE_FAIL) + EMSG(_("E702: Sort compare function failed")); + else + { + /* Sort the array with item pointers. */ + qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T), + info.item_compare_func == NULL + && info.item_compare_partial == NULL + ? item_compare : item_compare2); + + if (!info.item_compare_func_err) + { + /* Clear the List and append the items in sorted order. */ + l->lv_first = l->lv_last = l->lv_idx_item = NULL; + l->lv_len = 0; + for (i = 0; i < len; ++i) + list_append(l, ptrs[i].item); + } + } + } + else + { + int (*item_compare_func_ptr)(const void *, const void *); + + /* f_uniq(): ptrs will be a stack of items to remove */ + info.item_compare_func_err = FALSE; + info.item_compare_keep_zero = TRUE; + item_compare_func_ptr = info.item_compare_func != NULL + || info.item_compare_partial != NULL + ? item_compare2 : item_compare; + + for (li = l->lv_first; li != NULL && li->li_next != NULL; + li = li->li_next) + { + if (item_compare_func_ptr((void *)&li, (void *)&li->li_next) + == 0) + ptrs[i++].item = li; + if (info.item_compare_func_err) + { + EMSG(_("E882: Uniq compare function failed")); + break; + } + } + + if (!info.item_compare_func_err) + { + while (--i >= 0) + { + li = ptrs[i].item->li_next; + ptrs[i].item->li_next = li->li_next; + if (li->li_next != NULL) + li->li_next->li_prev = ptrs[i].item; + else + l->lv_last = ptrs[i].item; + list_fix_watch(l, li); + listitem_free(li); + l->lv_len--; + } + } + } + + vim_free(ptrs); + } +theend: + sortinfo = old_sortinfo; +} + +/* + * "sort({list})" function + */ + static void +f_sort(typval_T *argvars, typval_T *rettv) +{ + do_sort_uniq(argvars, rettv, TRUE); +} + +/* + * "uniq({list})" function + */ + static void +f_uniq(typval_T *argvars, typval_T *rettv) +{ + do_sort_uniq(argvars, rettv, FALSE); +} + +/* + * "soundfold({word})" function + */ + static void +f_soundfold(typval_T *argvars, typval_T *rettv) +{ + char_u *s; + + rettv->v_type = VAR_STRING; + s = get_tv_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; + + if (rettv_list_alloc(rettv) == FAIL) + 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(); + } + else if (curwin->w_p_spell && *curbuf->b_s.b_p_spl != NUL) + { + char_u *str = get_tv_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; + } + } + } +#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; +#endif + + if (rettv_list_alloc(rettv) == FAIL) + return; + +#ifdef FEAT_SPELL + if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) + { + str = get_tv_string(&argvars[0]); + if (argvars[1].v_type != VAR_UNKNOWN) + { + maxcount = (int)get_tv_number_chk(&argvars[1], &typeerr); + if (maxcount <= 0) + return; + if (argvars[2].v_type != VAR_UNKNOWN) + { + need_capital = (int)get_tv_number_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); + } +#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; + + /* Make 'cpoptions' empty, the 'l' flag should not be used here. */ + save_cpo = p_cpo; + p_cpo = (char_u *)""; + + str = get_tv_string(&argvars[0]); + if (argvars[1].v_type != VAR_UNKNOWN) + { + pat = get_tv_string_buf_chk(&argvars[1], patbuf); + if (pat == NULL) + typeerr = TRUE; + if (argvars[2].v_type != VAR_UNKNOWN) + keepempty = (int)get_tv_number_chk(&argvars[2], &typeerr); + } + if (pat == NULL || *pat == NUL) + pat = (char_u *)"[\\x01- ]\\+"; + + if (rettv_list_alloc(rettv) == FAIL) + return; + if (typeerr) + return; + + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog != NULL) + { + regmatch.rm_ic = FALSE; + while (*str != NUL || keepempty) + { + if (*str == NUL) + match = FALSE; /* empty item at the end */ + else + match = vim_regexec_nl(®match, str, col); + if (match) + end = regmatch.startp[0]; + else + end = str + STRLEN(str); + if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0 + && *str != NUL && match && end < regmatch.endp[0])) + { + if (list_append_string(rettv->vval.v_list, str, + (int)(end - str)) == FAIL) + break; + } + if (!match) + break; + /* Advance to just after the match. */ + if (regmatch.endp[0] > str) + col = 0; + else + { + /* Don't get stuck at the same match. */ +#ifdef FEAT_MBYTE + col = (*mb_ptr2len)(regmatch.endp[0]); +#else + col = 1; +#endif + } + str = regmatch.endp[0]; + } + + vim_regfree(regmatch.regprog); + } + + p_cpo = save_cpo; +} + +#ifdef FEAT_FLOAT +/* + * "sqrt()" function + */ + static void +f_sqrt(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = sqrt(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "str2float()" function + */ + static void +f_str2float(typval_T *argvars, typval_T *rettv) +{ + char_u *p = skipwhite(get_tv_string(&argvars[0])); + + if (*p == '+') + p = skipwhite(p + 1); + (void)string2float(p, &rettv->vval.v_float); + rettv->v_type = VAR_FLOAT; +} +#endif + +/* + * "str2nr()" function + */ + static void +f_str2nr(typval_T *argvars, typval_T *rettv) +{ + int base = 10; + char_u *p; + varnumber_T n; + int what; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + base = (int)get_tv_number(&argvars[1]); + if (base != 2 && base != 8 && base != 10 && base != 16) + { + EMSG(_(e_invarg)); + return; + } + } + + p = skipwhite(get_tv_string(&argvars[0])); + if (*p == '+') + p = skipwhite(p + 1); + switch (base) + { + case 2: what = STR2NR_BIN + STR2NR_FORCE; break; + case 8: what = STR2NR_OCT + STR2NR_FORCE; break; + case 16: what = STR2NR_HEX + STR2NR_FORCE; break; + default: what = 0; + } + vim_str2nr(p, NULL, NULL, what, &n, NULL, 0); + rettv->vval.v_number = n; +} + +#ifdef HAVE_STRFTIME +/* + * "strftime({format}[, {time}])" function + */ + static void +f_strftime(typval_T *argvars, typval_T *rettv) +{ + char_u result_buf[256]; + struct tm *curtime; + time_t seconds; + char_u *p; + + rettv->v_type = VAR_STRING; + + p = get_tv_string(&argvars[0]); + if (argvars[1].v_type == VAR_UNKNOWN) + seconds = time(NULL); + else + seconds = (time_t)get_tv_number(&argvars[1]); + curtime = localtime(&seconds); + /* MSVC returns NULL for an invalid value of seconds. */ + if (curtime == NULL) + rettv->vval.v_string = vim_strsave((char_u *)_("(Invalid)")); + else + { +# ifdef FEAT_MBYTE + vimconv_T conv; + char_u *enc; + + conv.vc_type = CONV_NONE; + enc = enc_locale(); + convert_setup(&conv, p_enc, enc); + if (conv.vc_type != CONV_NONE) + p = string_convert(&conv, p, NULL); +# endif + if (p != NULL) + (void)strftime((char *)result_buf, sizeof(result_buf), + (char *)p, curtime); + else + result_buf[0] = NUL; + +# ifdef FEAT_MBYTE + if (conv.vc_type != CONV_NONE) + vim_free(p); + convert_setup(&conv, enc, p_enc); + if (conv.vc_type != CONV_NONE) + rettv->vval.v_string = string_convert(&conv, result_buf, NULL); + else +# endif + rettv->vval.v_string = vim_strsave(result_buf); + +# ifdef FEAT_MBYTE + /* Release conversion descriptors */ + convert_setup(&conv, NULL, NULL); + vim_free(enc); +# endif + } +} +#endif + +/* + * "strgetchar()" function + */ + static void +f_strgetchar(typval_T *argvars, typval_T *rettv) +{ + char_u *str; + int len; + int error = FALSE; + int charidx; + + rettv->vval.v_number = -1; + str = get_tv_string_chk(&argvars[0]); + if (str == NULL) + return; + len = (int)STRLEN(str); + charidx = (int)get_tv_number_chk(&argvars[1], &error); + if (error) + return; +#ifdef FEAT_MBYTE + { + int byteidx = 0; + + while (charidx >= 0 && byteidx < len) + { + if (charidx == 0) + { + rettv->vval.v_number = mb_ptr2char(str + byteidx); + break; + } + --charidx; + byteidx += mb_cptr2len(str + byteidx); + } + } +#else + if (charidx < len) + rettv->vval.v_number = str[charidx]; +#endif +} + +/* + * "stridx()" function + */ + static void +f_stridx(typval_T *argvars, typval_T *rettv) +{ + char_u buf[NUMBUFLEN]; + char_u *needle; + char_u *haystack; + char_u *save_haystack; + char_u *pos; + int start_idx; + + needle = get_tv_string_chk(&argvars[1]); + save_haystack = haystack = get_tv_string_buf_chk(&argvars[0], buf); + rettv->vval.v_number = -1; + if (needle == NULL || haystack == NULL) + return; /* type error; errmsg already given */ + + if (argvars[2].v_type != VAR_UNKNOWN) + { + int error = FALSE; + + start_idx = (int)get_tv_number_chk(&argvars[2], &error); + if (error || start_idx >= (int)STRLEN(haystack)) + return; + if (start_idx >= 0) + haystack += start_idx; + } + + pos = (char_u *)strstr((char *)haystack, (char *)needle); + if (pos != NULL) + rettv->vval.v_number = (varnumber_T)(pos - save_haystack); +} + +/* + * "string()" function + */ + static void +f_string(typval_T *argvars, typval_T *rettv) +{ + char_u *tofree; + char_u numbuf[NUMBUFLEN]; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = tv2string(&argvars[0], &tofree, numbuf, + get_copyID()); + /* Make a copy if we have a value but it's not in allocated memory. */ + if (rettv->vval.v_string != NULL && tofree == NULL) + rettv->vval.v_string = vim_strsave(rettv->vval.v_string); +} + +/* + * "strlen()" function + */ + static void +f_strlen(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = (varnumber_T)(STRLEN( + get_tv_string(&argvars[0]))); +} + +/* + * "strchars()" function + */ + static void +f_strchars(typval_T *argvars, typval_T *rettv) +{ + char_u *s = get_tv_string(&argvars[0]); + int skipcc = 0; +#ifdef FEAT_MBYTE + varnumber_T len = 0; + int (*func_mb_ptr2char_adv)(char_u **pp); +#endif + + if (argvars[1].v_type != VAR_UNKNOWN) + skipcc = (int)get_tv_number_chk(&argvars[1], NULL); + if (skipcc < 0 || skipcc > 1) + EMSG(_(e_invarg)); + else + { +#ifdef FEAT_MBYTE + func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv; + while (*s != NUL) + { + func_mb_ptr2char_adv(&s); + ++len; + } + rettv->vval.v_number = len; +#else + rettv->vval.v_number = (varnumber_T)(STRLEN(s)); +#endif + } +} + +/* + * "strdisplaywidth()" function + */ + static void +f_strdisplaywidth(typval_T *argvars, typval_T *rettv) +{ + char_u *s = get_tv_string(&argvars[0]); + int col = 0; + + if (argvars[1].v_type != VAR_UNKNOWN) + col = (int)get_tv_number(&argvars[1]); + + rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, s) - col); +} + +/* + * "strwidth()" function + */ + static void +f_strwidth(typval_T *argvars, typval_T *rettv) +{ + char_u *s = get_tv_string(&argvars[0]); + + rettv->vval.v_number = (varnumber_T)( +#ifdef FEAT_MBYTE + mb_string2cells(s, -1) +#else + STRLEN(s) +#endif + ); +} + +/* + * "strcharpart()" function + */ + static void +f_strcharpart(typval_T *argvars, typval_T *rettv) +{ +#ifdef FEAT_MBYTE + char_u *p; + int nchar; + int nbyte = 0; + int charlen; + int len = 0; + int slen; + int error = FALSE; + + p = get_tv_string(&argvars[0]); + slen = (int)STRLEN(p); + + nchar = (int)get_tv_number_chk(&argvars[1], &error); + if (!error) + { + if (nchar > 0) + while (nchar > 0 && nbyte < slen) + { + nbyte += mb_cptr2len(p + nbyte); + --nchar; + } + else + nbyte = nchar; + if (argvars[2].v_type != VAR_UNKNOWN) + { + charlen = (int)get_tv_number(&argvars[2]); + while (charlen > 0 && nbyte + len < slen) + { + int off = nbyte + len; + + if (off < 0) + len += 1; + else + len += mb_cptr2len(p + off); + --charlen; + } + } + else + len = slen - nbyte; /* default: all bytes that are available. */ + } + + /* + * Only return the overlap between the specified part and the actual + * string. + */ + if (nbyte < 0) + { + len += nbyte; + nbyte = 0; + } + else if (nbyte > slen) + nbyte = slen; + if (len < 0) + len = 0; + else if (nbyte + len > slen) + len = slen - nbyte; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strnsave(p + nbyte, len); +#else + f_strpart(argvars, rettv); +#endif +} + +/* + * "strpart()" function + */ + static void +f_strpart(typval_T *argvars, typval_T *rettv) +{ + char_u *p; + int n; + int len; + int slen; + int error = FALSE; + + p = get_tv_string(&argvars[0]); + slen = (int)STRLEN(p); + + n = (int)get_tv_number_chk(&argvars[1], &error); + if (error) + len = 0; + else if (argvars[2].v_type != VAR_UNKNOWN) + len = (int)get_tv_number(&argvars[2]); + else + len = slen - n; /* default len: all bytes that are available. */ + + /* + * Only return the overlap between the specified part and the actual + * string. + */ + if (n < 0) + { + len += n; + n = 0; + } + else if (n > slen) + n = slen; + if (len < 0) + len = 0; + else if (n + len > slen) + len = slen - n; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strnsave(p + n, len); +} + +/* + * "strridx()" function + */ + static void +f_strridx(typval_T *argvars, typval_T *rettv) +{ + char_u buf[NUMBUFLEN]; + char_u *needle; + char_u *haystack; + char_u *rest; + char_u *lastmatch = NULL; + int haystack_len, end_idx; + + needle = get_tv_string_chk(&argvars[1]); + haystack = get_tv_string_buf_chk(&argvars[0], buf); + + rettv->vval.v_number = -1; + if (needle == NULL || haystack == NULL) + return; /* type error; errmsg already given */ + + haystack_len = (int)STRLEN(haystack); + if (argvars[2].v_type != VAR_UNKNOWN) + { + /* Third argument: upper limit for index */ + end_idx = (int)get_tv_number_chk(&argvars[2], NULL); + if (end_idx < 0) + return; /* can never find a match */ + } + else + end_idx = haystack_len; + + if (*needle == NUL) + { + /* Empty string matches past the end. */ + lastmatch = haystack + end_idx; + } + else + { + for (rest = haystack; *rest != '\0'; ++rest) + { + rest = (char_u *)strstr((char *)rest, (char *)needle); + if (rest == NULL || rest > haystack + end_idx) + break; + lastmatch = rest; + } + } + + if (lastmatch == NULL) + rettv->vval.v_number = -1; + else + rettv->vval.v_number = (varnumber_T)(lastmatch - haystack); +} + +/* + * "strtrans()" function + */ + static void +f_strtrans(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = transstr(get_tv_string(&argvars[0])); +} + +/* + * "submatch()" function + */ + static void +f_submatch(typval_T *argvars, typval_T *rettv) +{ + int error = FALSE; + int no; + int retList = 0; + + no = (int)get_tv_number_chk(&argvars[0], &error); + if (error) + return; + error = FALSE; + if (argvars[1].v_type != VAR_UNKNOWN) + retList = (int)get_tv_number_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 = get_tv_string_chk(&argvars[0]); + char_u *pat = get_tv_string_buf_chk(&argvars[1], patbuf); + char_u *sub = get_tv_string_buf_chk(&argvars[2], subbuf); + char_u *flg = get_tv_string_buf_chk(&argvars[3], flagsbuf); + + rettv->v_type = VAR_STRING; + if (str == NULL || pat == NULL || sub == NULL || flg == NULL) + rettv->vval.v_string = NULL; + else + rettv->vval.v_string = do_string_sub(str, pat, sub, flg); +} + +/* + * "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; + + lnum = get_tv_lnum(argvars); /* -1 on type error */ + col = (linenr_T)get_tv_number(&argvars[1]) - 1; /* -1 on type error */ + trans = (int)get_tv_number_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, (colnr_T)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; + + id = (int)get_tv_number(&argvars[0]); + what = get_tv_string(&argvars[1]); + if (argvars[2].v_type != VAR_UNKNOWN) + { + mode = get_tv_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(NULL, id - 1); + 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); + else /* standout */ + p = highlight_has_attr(id, HL_STANDOUT, modec); + break; + + case 'u': + 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 + id = (int)get_tv_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->v_type = VAR_LIST; + rettv->vval.v_list = NULL; + +#if defined(FEAT_SYN_HL) && defined(FEAT_CONCEAL) + lnum = get_tv_lnum(argvars); /* -1 on type error */ + col = (colnr_T)get_tv_number(&argvars[1]) - 1; /* -1 on type error */ + + vim_memset(str, NUL, sizeof(str)); + + if (rettv_list_alloc(rettv) != FAIL) + { + 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 && lcs_conceal != NUL) + cchar = lcs_conceal; + if (cchar != NUL) + { +# ifdef FEAT_MBYTE + if (has_mbyte) + (*mb_char2bytes)(cchar, str); + else +# endif + 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->v_type = VAR_LIST; + rettv->vval.v_list = NULL; + +#ifdef FEAT_SYN_HL + lnum = get_tv_lnum(argvars); /* -1 on type error */ + col = (colnr_T)get_tv_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) != FAIL) + { + (void)syn_get_id(curwin, lnum, (colnr_T)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 +} + + static void +get_cmd_output_as_rettv( + typval_T *argvars, + typval_T *rettv, + int retlist) +{ + char_u *res = NULL; + char_u *p; + char_u *infile = NULL; + char_u buf[NUMBUFLEN]; + int err = FALSE; + FILE *fd; + list_T *list = NULL; + int flags = SHELL_SILENT; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + if (check_restricted() || check_secure()) + goto errret; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + /* + * Write the string to a temp file, to be used for input of the shell + * command. + */ + if ((infile = vim_tempname('i', TRUE)) == NULL) + { + EMSG(_(e_notmp)); + goto errret; + } + + fd = mch_fopen((char *)infile, WRITEBIN); + if (fd == NULL) + { + EMSG2(_(e_notopen), infile); + goto errret; + } + if (argvars[1].v_type == VAR_LIST) + { + if (write_list(fd, argvars[1].vval.v_list, TRUE) == FAIL) + err = TRUE; + } + else + { + size_t len; + + p = get_tv_string_buf_chk(&argvars[1], buf); + if (p == NULL) + { + fclose(fd); + goto errret; /* type error; errmsg already given */ + } + len = STRLEN(p); + if (len > 0 && fwrite(p, len, 1, fd) != 1) + err = TRUE; + } + if (fclose(fd) != 0) + err = TRUE; + if (err) + { + EMSG(_("E677: Error writing temp file")); + goto errret; + } + } + + /* Omit SHELL_COOKED when invoked with ":silent". Avoids that the shell + * echoes typeahead, that messes up the display. */ + if (!msg_silent) + flags += SHELL_COOKED; + + if (retlist) + { + int len; + listitem_T *li; + char_u *s = NULL; + char_u *start; + char_u *end; + int i; + + res = get_cmd_output(get_tv_string(&argvars[0]), infile, flags, &len); + if (res == NULL) + goto errret; + + list = list_alloc(); + if (list == NULL) + goto errret; + + for (i = 0; i < len; ++i) + { + start = res + i; + while (i < len && res[i] != NL) + ++i; + end = res + i; + + s = alloc((unsigned)(end - start + 1)); + if (s == NULL) + goto errret; + + for (p = s; start < end; ++p, ++start) + *p = *start == NUL ? NL : *start; + *p = NUL; + + li = listitem_alloc(); + if (li == NULL) + { + vim_free(s); + goto errret; + } + li->li_tv.v_type = VAR_STRING; + li->li_tv.v_lock = 0; + li->li_tv.vval.v_string = s; + list_append(list, li); + } + + ++list->lv_refcount; + rettv->v_type = VAR_LIST; + rettv->vval.v_list = list; + list = NULL; + } + else + { + res = get_cmd_output(get_tv_string(&argvars[0]), infile, flags, NULL); +#ifdef USE_CR + /* translate into */ + if (res != NULL) + { + char_u *s; + + for (s = res; *s; ++s) + { + if (*s == CAR) + *s = NL; + } + } +#else +# ifdef USE_CRNL + /* translate into */ + if (res != NULL) + { + char_u *s, *d; + + d = res; + for (s = res; *s; ++s) + { + if (s[0] == CAR && s[1] == NL) + ++s; + *d++ = *s; + } + *d = NUL; + } +# endif +#endif + rettv->vval.v_string = res; + res = NULL; + } + +errret: + if (infile != NULL) + { + mch_remove(infile); + vim_free(infile); + } + if (res != NULL) + vim_free(res); + if (list != NULL) + list_free(list); +} + +/* + * "system()" function + */ + static void +f_system(typval_T *argvars, typval_T *rettv) +{ + get_cmd_output_as_rettv(argvars, rettv, FALSE); +} + +/* + * "systemlist()" function + */ + static void +f_systemlist(typval_T *argvars, typval_T *rettv) +{ + get_cmd_output_as_rettv(argvars, rettv, TRUE); +} + +/* + * "tabpagebuflist()" function + */ + static void +f_tabpagebuflist(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_WINDOWS + tabpage_T *tp; + win_T *wp = NULL; + + if (argvars[0].v_type == VAR_UNKNOWN) + wp = firstwin; + else + { + tp = find_tabpage((int)get_tv_number(&argvars[0])); + if (tp != NULL) + wp = (tp == curtab) ? firstwin : tp->tp_firstwin; + } + if (wp != NULL && rettv_list_alloc(rettv) != FAIL) + { + for (; wp != NULL; wp = wp->w_next) + if (list_append_number(rettv->vval.v_list, + wp->w_buffer->b_fnum) == FAIL) + break; + } +#endif +} + + +/* + * "tabpagenr()" function + */ + static void +f_tabpagenr(typval_T *argvars UNUSED, typval_T *rettv) +{ + int nr = 1; +#ifdef FEAT_WINDOWS + char_u *arg; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + arg = get_tv_string_chk(&argvars[0]); + nr = 0; + if (arg != NULL) + { + if (STRCMP(arg, "$") == 0) + nr = tabpage_index(NULL) - 1; + else + EMSG2(_(e_invexpr2), arg); + } + } + else + nr = tabpage_index(curtab); +#endif + rettv->vval.v_number = nr; +} + + +#ifdef FEAT_WINDOWS +static int get_winnr(tabpage_T *tp, typval_T *argvar); + +/* + * Common code for tabpagewinnr() and winnr(). + */ + static int +get_winnr(tabpage_T *tp, typval_T *argvar) +{ + win_T *twin; + int nr = 1; + win_T *wp; + char_u *arg; + + twin = (tp == curtab) ? curwin : tp->tp_curwin; + if (argvar->v_type != VAR_UNKNOWN) + { + arg = get_tv_string_chk(argvar); + if (arg == NULL) + nr = 0; /* type error; errmsg already given */ + else if (STRCMP(arg, "$") == 0) + twin = (tp == curtab) ? lastwin : tp->tp_lastwin; + else if (STRCMP(arg, "#") == 0) + { + twin = (tp == curtab) ? prevwin : tp->tp_prevwin; + if (twin == NULL) + nr = 0; + } + else + { + EMSG2(_(e_invexpr2), arg); + nr = 0; + } + } + + if (nr > 0) + for (wp = (tp == curtab) ? firstwin : tp->tp_firstwin; + wp != twin; wp = wp->w_next) + { + if (wp == NULL) + { + /* didn't find it in this tabpage */ + nr = 0; + break; + } + ++nr; + } + return nr; +} +#endif + +/* + * "tabpagewinnr()" function + */ + static void +f_tabpagewinnr(typval_T *argvars UNUSED, typval_T *rettv) +{ + int nr = 1; +#ifdef FEAT_WINDOWS + tabpage_T *tp; + + tp = find_tabpage((int)get_tv_number(&argvars[0])); + if (tp == NULL) + nr = 0; + else + nr = get_winnr(tp, &argvars[1]); +#endif + rettv->vval.v_number = nr; +} + + +/* + * "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 *tag_pattern; + + tag_pattern = get_tv_string(&argvars[0]); + + rettv->vval.v_number = FALSE; + if (*tag_pattern == NUL) + return; + + if (rettv_list_alloc(rettv) == OK) + (void)get_tags(rettv->vval.v_list, tag_pattern); +} + +/* + * "tempname()" function + */ + static void +f_tempname(typval_T *argvars UNUSED, typval_T *rettv) +{ + static int x = 'A'; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_tempname(x, FALSE); + + /* Advance 'x' to use A-Z and 0-9, so that there are at least 34 different + * names. Skip 'I' and 'O', they are used for shell redirection. */ + do + { + if (x == 'Z') + x = '0'; + else if (x == '9') + x = 'A'; + else + { +#ifdef EBCDIC + if (x == 'I') + x = 'J'; + else if (x == 'R') + x = 'S'; + else +#endif + ++x; + } + } while (x == 'I' || x == 'O'); +} + +#ifdef FEAT_FLOAT +/* + * "tan()" function + */ + static void +f_tan(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = tan(f); + else + rettv->vval.v_float = 0.0; +} + +/* + * "tanh()" function + */ + static void +f_tanh(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + rettv->vval.v_float = tanh(f); + else + rettv->vval.v_float = 0.0; +} +#endif + +/* + * "test_alloc_fail(id, countdown, repeat)" function + */ + static void +f_test_alloc_fail(typval_T *argvars, typval_T *rettv UNUSED) +{ + if (argvars[0].v_type != VAR_NUMBER + || argvars[0].vval.v_number <= 0 + || argvars[1].v_type != VAR_NUMBER + || argvars[1].vval.v_number < 0 + || argvars[2].v_type != VAR_NUMBER) + EMSG(_(e_invarg)); + else + { + alloc_fail_id = argvars[0].vval.v_number; + if (alloc_fail_id >= aid_last) + EMSG(_(e_invarg)); + alloc_fail_countdown = argvars[1].vval.v_number; + alloc_fail_repeat = argvars[2].vval.v_number; + did_outofmem_msg = FALSE; + } +} + +/* + * "test_autochdir()" + */ + static void +f_test_autochdir(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#if defined(FEAT_AUTOCHDIR) + test_autochdir = TRUE; +#endif +} + +/* + * "test_disable_char_avail({expr})" function + */ + static void +f_test_disable_char_avail(typval_T *argvars, typval_T *rettv UNUSED) +{ + disable_char_avail_for_testing = (int)get_tv_number(&argvars[0]); +} + +/* + * "test_garbagecollect_now()" function + */ + static void +f_test_garbagecollect_now(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ + /* This is dangerous, any Lists and Dicts used internally may be freed + * while still in use. */ + garbage_collect(TRUE); +} + +#ifdef FEAT_JOB_CHANNEL + static void +f_test_null_channel(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_CHANNEL; + rettv->vval.v_channel = NULL; +} +#endif + + static void +f_test_null_dict(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_DICT; + rettv->vval.v_dict = NULL; +} + +#ifdef FEAT_JOB_CHANNEL + static void +f_test_null_job(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_JOB; + rettv->vval.v_job = NULL; +} +#endif + + static void +f_test_null_list(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_LIST; + rettv->vval.v_list = NULL; +} + + static void +f_test_null_partial(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = NULL; +} + + static void +f_test_null_string(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; +} + + static void +f_test_settime(typval_T *argvars, typval_T *rettv UNUSED) +{ + time_for_testing = (time_t)get_tv_number(&argvars[0]); +} + +#if defined(FEAT_JOB_CHANNEL) || defined(FEAT_TIMERS) || defined(PROTO) +/* + * Get a callback from "arg". It can be a Funcref or a function name. + * When "arg" is zero return an empty string. + * Return NULL for an invalid argument. + */ + char_u * +get_callback(typval_T *arg, partial_T **pp) +{ + if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL) + { + *pp = arg->vval.v_partial; + ++(*pp)->pt_refcount; + return (*pp)->pt_name; + } + *pp = NULL; + if (arg->v_type == VAR_FUNC) + { + func_ref(arg->vval.v_string); + return arg->vval.v_string; + } + if (arg->v_type == VAR_STRING) + return arg->vval.v_string; + if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0) + return (char_u *)""; + EMSG(_("E921: Invalid callback argument")); + return NULL; +} + +/* + * Unref/free "callback" and "partial" retured by get_callback(). + */ + void +free_callback(char_u *callback, partial_T *partial) +{ + if (partial != NULL) + partial_unref(partial); + else if (callback != NULL) + { + func_unref(callback); + vim_free(callback); + } +} +#endif + +#ifdef FEAT_TIMERS +/* + * "timer_start(time, callback [, options])" function + */ + static void +f_timer_start(typval_T *argvars, typval_T *rettv) +{ + long msec = (long)get_tv_number(&argvars[0]); + timer_T *timer; + int repeat = 0; + char_u *callback; + dict_T *dict; + + if (check_secure()) + return; + if (argvars[2].v_type != VAR_UNKNOWN) + { + if (argvars[2].v_type != VAR_DICT + || (dict = argvars[2].vval.v_dict) == NULL) + { + EMSG2(_(e_invarg2), get_tv_string(&argvars[2])); + return; + } + if (dict_find(dict, (char_u *)"repeat", -1) != NULL) + repeat = get_dict_number(dict, (char_u *)"repeat"); + } + + timer = create_timer(msec, repeat); + callback = get_callback(&argvars[1], &timer->tr_partial); + if (callback == NULL) + { + stop_timer(timer); + rettv->vval.v_number = -1; + } + else + { + timer->tr_callback = vim_strsave(callback); + rettv->vval.v_number = timer->tr_id; + } +} + +/* + * "timer_stop(timer)" function + */ + static void +f_timer_stop(typval_T *argvars, typval_T *rettv UNUSED) +{ + timer_T *timer; + + if (argvars[0].v_type != VAR_NUMBER) + { + EMSG(_(e_number_exp)); + return; + } + timer = find_timer((int)get_tv_number(&argvars[0])); + if (timer != NULL) + stop_timer(timer); +} +#endif + +/* + * "tolower(string)" function + */ + static void +f_tolower(typval_T *argvars, typval_T *rettv) +{ + char_u *p; + + p = vim_strsave(get_tv_string(&argvars[0])); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = p; + + if (p != NULL) + while (*p != NUL) + { +#ifdef FEAT_MBYTE + int l; + + if (enc_utf8) + { + int c, lc; + + c = utf_ptr2char(p); + lc = utf_tolower(c); + l = utf_ptr2len(p); + /* TODO: reallocate string when byte count changes. */ + if (utf_char2len(lc) == l) + utf_char2bytes(lc, p); + p += l; + } + else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1) + p += l; /* skip multi-byte character */ + else +#endif + { + *p = TOLOWER_LOC(*p); /* note that tolower() can be a macro */ + ++p; + } + } +} + +/* + * "toupper(string)" function + */ + static void +f_toupper(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = strup_save(get_tv_string(&argvars[0])); +} + +/* + * "tr(string, fromstr, tostr)" function + */ + static void +f_tr(typval_T *argvars, typval_T *rettv) +{ + char_u *in_str; + char_u *fromstr; + char_u *tostr; + char_u *p; +#ifdef FEAT_MBYTE + int inlen; + int fromlen; + int tolen; + int idx; + char_u *cpstr; + int cplen; + int first = TRUE; +#endif + char_u buf[NUMBUFLEN]; + char_u buf2[NUMBUFLEN]; + garray_T ga; + + in_str = get_tv_string(&argvars[0]); + fromstr = get_tv_string_buf_chk(&argvars[1], buf); + tostr = get_tv_string_buf_chk(&argvars[2], buf2); + + /* Default return value: empty string. */ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + if (fromstr == NULL || tostr == NULL) + return; /* type error; errmsg already given */ + ga_init2(&ga, (int)sizeof(char), 80); + +#ifdef FEAT_MBYTE + if (!has_mbyte) +#endif + /* not multi-byte: fromstr and tostr must be the same length */ + if (STRLEN(fromstr) != STRLEN(tostr)) + { +#ifdef FEAT_MBYTE +error: +#endif + EMSG2(_(e_invarg2), fromstr); + ga_clear(&ga); + return; + } + + /* fromstr and tostr have to contain the same number of chars */ + while (*in_str != NUL) + { +#ifdef FEAT_MBYTE + if (has_mbyte) + { + inlen = (*mb_ptr2len)(in_str); + cpstr = in_str; + cplen = inlen; + idx = 0; + for (p = fromstr; *p != NUL; p += fromlen) + { + fromlen = (*mb_ptr2len)(p); + if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0) + { + for (p = tostr; *p != NUL; p += tolen) + { + tolen = (*mb_ptr2len)(p); + if (idx-- == 0) + { + cplen = tolen; + cpstr = p; + break; + } + } + if (*p == NUL) /* tostr is shorter than fromstr */ + goto error; + break; + } + ++idx; + } + + if (first && cpstr == in_str) + { + /* Check that fromstr and tostr have the same number of + * (multi-byte) characters. Done only once when a character + * of in_str doesn't appear in fromstr. */ + first = FALSE; + for (p = tostr; *p != NUL; p += tolen) + { + tolen = (*mb_ptr2len)(p); + --idx; + } + if (idx != 0) + goto error; + } + + (void)ga_grow(&ga, cplen); + mch_memmove((char *)ga.ga_data + ga.ga_len, cpstr, (size_t)cplen); + ga.ga_len += cplen; + + in_str += inlen; + } + else +#endif + { + /* When not using multi-byte chars we can do it faster. */ + p = vim_strchr(fromstr, *in_str); + if (p != NULL) + ga_append(&ga, tostr[p - fromstr]); + else + ga_append(&ga, *in_str); + ++in_str; + } + } + + /* add a terminating NUL */ + (void)ga_grow(&ga, 1); + ga_append(&ga, NUL); + + rettv->vval.v_string = ga.ga_data; +} + +#ifdef FEAT_FLOAT +/* + * "trunc({float})" function + */ + static void +f_trunc(typval_T *argvars, typval_T *rettv) +{ + float_T f = 0.0; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) + /* trunc() is not in C90, use floor() or ceil() instead. */ + rettv->vval.v_float = f > 0 ? floor(f) : ceil(f); + else + rettv->vval.v_float = 0.0; +} +#endif + +/* + * "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 = 0; break; + case VAR_STRING: n = 1; break; + case VAR_PARTIAL: + case VAR_FUNC: n = 2; break; + case VAR_LIST: n = 3; break; + case VAR_DICT: n = 4; break; + case VAR_FLOAT: n = 5; break; + case VAR_SPECIAL: + if (argvars[0].vval.v_number == VVAL_FALSE + || argvars[0].vval.v_number == VVAL_TRUE) + n = 6; + else + n = 7; + break; + case VAR_JOB: n = 8; break; + case VAR_CHANNEL: n = 9; break; + case VAR_UNKNOWN: + EMSG2(_(e_intern2), "f_type(UNKNOWN)"); + n = -1; + break; + } + rettv->vval.v_number = n; +} + +/* + * "undofile(name)" function + */ + static void +f_undofile(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; +#ifdef FEAT_PERSISTENT_UNDO + { + char_u *fname = get_tv_string(&argvars[0]); + + if (*fname == NUL) + { + /* If there is no file name there will be no undo file. */ + rettv->vval.v_string = NULL; + } + else + { + char_u *ffname = FullName_save(fname, FALSE); + + if (ffname != NULL) + rettv->vval.v_string = u_get_undo_file_name(ffname, FALSE); + vim_free(ffname); + } + } +#else + rettv->vval.v_string = NULL; +#endif +} + +/* + * "undotree()" function + */ + static void +f_undotree(typval_T *argvars UNUSED, typval_T *rettv) +{ + if (rettv_dict_alloc(rettv) == OK) + { + dict_T *dict = rettv->vval.v_dict; + list_T *list; + + dict_add_nr_str(dict, "synced", (long)curbuf->b_u_synced, NULL); + dict_add_nr_str(dict, "seq_last", curbuf->b_u_seq_last, NULL); + dict_add_nr_str(dict, "save_last", + (long)curbuf->b_u_save_nr_last, NULL); + dict_add_nr_str(dict, "seq_cur", curbuf->b_u_seq_cur, NULL); + dict_add_nr_str(dict, "time_cur", (long)curbuf->b_u_time_cur, NULL); + dict_add_nr_str(dict, "save_cur", (long)curbuf->b_u_save_nr_cur, NULL); + + list = list_alloc(); + if (list != NULL) + { + u_eval_tree(curbuf->b_u_oldhead, list); + dict_add_list(dict, "entries", list); + } + } +} + +/* + * "values(dict)" function + */ + static void +f_values(typval_T *argvars, typval_T *rettv) +{ + dict_list(argvars, rettv, 1); +} + +/* + * "virtcol(string)" function + */ + static void +f_virtcol(typval_T *argvars, typval_T *rettv) +{ + colnr_T vcol = 0; + pos_T *fp; + int fnum = curbuf->b_fnum; + + fp = var2fpos(&argvars[0], FALSE, &fnum); + if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count + && fnum == curbuf->b_fnum) + { + getvvcol(curwin, fp, NULL, NULL, &vcol); + ++vcol; + } + + rettv->vval.v_number = vcol; +} + +/* + * "visualmode()" function + */ + static void +f_visualmode(typval_T *argvars, typval_T *rettv) +{ + char_u str[2]; + + 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) + rettv->vval.v_number = 1; +#endif +} + +/* + * "winbufnr(nr)" function + */ + static void +f_winbufnr(typval_T *argvars, typval_T *rettv) +{ + win_T *wp; + + wp = find_win_by_nr(&argvars[0], NULL); + if (wp == NULL) + rettv->vval.v_number = -1; + else + rettv->vval.v_number = wp->w_buffer->b_fnum; +} + +/* + * "wincol()" function + */ + static void +f_wincol(typval_T *argvars UNUSED, typval_T *rettv) +{ + validate_cursor(); + rettv->vval.v_number = curwin->w_wcol + 1; +} + +/* + * "winheight(nr)" function + */ + static void +f_winheight(typval_T *argvars, typval_T *rettv) +{ + win_T *wp; + + wp = find_win_by_nr(&argvars[0], NULL); + if (wp == NULL) + rettv->vval.v_number = -1; + else + rettv->vval.v_number = wp->w_height; +} + +/* + * "winline()" function + */ + static void +f_winline(typval_T *argvars UNUSED, typval_T *rettv) +{ + validate_cursor(); + rettv->vval.v_number = curwin->w_wrow + 1; +} + +/* + * "winnr()" function + */ + static void +f_winnr(typval_T *argvars UNUSED, typval_T *rettv) +{ + int nr = 1; + +#ifdef FEAT_WINDOWS + nr = get_winnr(curtab, &argvars[0]); +#endif + rettv->vval.v_number = nr; +} + +/* + * "winrestcmd()" function + */ + static void +f_winrestcmd(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_WINDOWS + win_T *wp; + int winnr = 1; + garray_T ga; + char_u buf[50]; + + ga_init2(&ga, (int)sizeof(char), 70); + for (wp = firstwin; wp != NULL; wp = wp->w_next) + { + sprintf((char *)buf, "%dresize %d|", winnr, wp->w_height); + ga_concat(&ga, buf); + sprintf((char *)buf, "vert %dresize %d|", winnr, wp->w_width); + ga_concat(&ga, buf); + ++winnr; + } + ga_append(&ga, NUL); + + rettv->vval.v_string = ga.ga_data; +#else + rettv->vval.v_string = NULL; +#endif + rettv->v_type = VAR_STRING; +} + +/* + * "winrestview()" function + */ + static void +f_winrestview(typval_T *argvars, typval_T *rettv UNUSED) +{ + dict_T *dict; + + if (argvars[0].v_type != VAR_DICT + || (dict = argvars[0].vval.v_dict) == NULL) + EMSG(_(e_invarg)); + else + { + if (dict_find(dict, (char_u *)"lnum", -1) != NULL) + curwin->w_cursor.lnum = (linenr_T)get_dict_number(dict, (char_u *)"lnum"); + if (dict_find(dict, (char_u *)"col", -1) != NULL) + curwin->w_cursor.col = (colnr_T)get_dict_number(dict, (char_u *)"col"); +#ifdef FEAT_VIRTUALEDIT + if (dict_find(dict, (char_u *)"coladd", -1) != NULL) + curwin->w_cursor.coladd = (colnr_T)get_dict_number(dict, (char_u *)"coladd"); +#endif + if (dict_find(dict, (char_u *)"curswant", -1) != NULL) + { + curwin->w_curswant = (colnr_T)get_dict_number(dict, (char_u *)"curswant"); + curwin->w_set_curswant = FALSE; + } + + if (dict_find(dict, (char_u *)"topline", -1) != NULL) + set_topline(curwin, (linenr_T)get_dict_number(dict, (char_u *)"topline")); +#ifdef FEAT_DIFF + if (dict_find(dict, (char_u *)"topfill", -1) != NULL) + curwin->w_topfill = (int)get_dict_number(dict, (char_u *)"topfill"); +#endif + if (dict_find(dict, (char_u *)"leftcol", -1) != NULL) + curwin->w_leftcol = (colnr_T)get_dict_number(dict, (char_u *)"leftcol"); + if (dict_find(dict, (char_u *)"skipcol", -1) != NULL) + curwin->w_skipcol = (colnr_T)get_dict_number(dict, (char_u *)"skipcol"); + + check_cursor(); + win_new_height(curwin, curwin->w_height); +# ifdef FEAT_WINDOWS + win_new_width(curwin, W_WIDTH(curwin)); +# endif + changed_window_setting(); + + if (curwin->w_topline <= 0) + curwin->w_topline = 1; + if (curwin->w_topline > curbuf->b_ml.ml_line_count) + curwin->w_topline = curbuf->b_ml.ml_line_count; +#ifdef FEAT_DIFF + check_topfill(curwin, TRUE); +#endif + } +} + +/* + * "winsaveview()" function + */ + static void +f_winsaveview(typval_T *argvars UNUSED, typval_T *rettv) +{ + dict_T *dict; + + if (rettv_dict_alloc(rettv) == FAIL) + return; + dict = rettv->vval.v_dict; + + dict_add_nr_str(dict, "lnum", (long)curwin->w_cursor.lnum, NULL); + dict_add_nr_str(dict, "col", (long)curwin->w_cursor.col, NULL); +#ifdef FEAT_VIRTUALEDIT + dict_add_nr_str(dict, "coladd", (long)curwin->w_cursor.coladd, NULL); +#endif + update_curswant(); + dict_add_nr_str(dict, "curswant", (long)curwin->w_curswant, NULL); + + dict_add_nr_str(dict, "topline", (long)curwin->w_topline, NULL); +#ifdef FEAT_DIFF + dict_add_nr_str(dict, "topfill", (long)curwin->w_topfill, NULL); +#endif + dict_add_nr_str(dict, "leftcol", (long)curwin->w_leftcol, NULL); + dict_add_nr_str(dict, "skipcol", (long)curwin->w_skipcol, NULL); +} + +/* + * "winwidth(nr)" function + */ + static void +f_winwidth(typval_T *argvars, typval_T *rettv) +{ + win_T *wp; + + wp = find_win_by_nr(&argvars[0], NULL); + if (wp == NULL) + rettv->vval.v_number = -1; + else +#ifdef FEAT_WINDOWS + rettv->vval.v_number = wp->w_width; +#else + rettv->vval.v_number = Columns; +#endif +} + +/* + * "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); +} + +/* + * "writefile()" function + */ + static void +f_writefile(typval_T *argvars, typval_T *rettv) +{ + int binary = FALSE; + int append = FALSE; + char_u *fname; + FILE *fd; + int ret = 0; + + if (check_restricted() || check_secure()) + return; + + if (argvars[0].v_type != VAR_LIST) + { + EMSG2(_(e_listarg), "writefile()"); + return; + } + if (argvars[0].vval.v_list == NULL) + return; + + if (argvars[2].v_type != VAR_UNKNOWN) + { + if (vim_strchr(get_tv_string(&argvars[2]), 'b') != NULL) + binary = TRUE; + if (vim_strchr(get_tv_string(&argvars[2]), 'a') != NULL) + append = TRUE; + } + + /* Always open the file in binary mode, library functions have a mind of + * their own about CR-LF conversion. */ + fname = get_tv_string(&argvars[1]); + if (*fname == NUL || (fd = mch_fopen((char *)fname, + append ? APPENDBIN : WRITEBIN)) == NULL) + { + EMSG2(_(e_notcreate), *fname == NUL ? (char_u *)_("") : fname); + ret = -1; + } + else + { + if (write_list(fd, argvars[0].vval.v_list, binary) == FAIL) + ret = -1; + fclose(fd); + } + + rettv->vval.v_number = ret; +} + +/* + * "xor(expr, expr)" function + */ + static void +f_xor(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) + ^ get_tv_number_chk(&argvars[1], NULL); +} + + +#endif /* FEAT_EVAL */ diff --git a/src/globals.h b/src/globals.h index f33120214a..b569680a41 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1538,6 +1538,8 @@ EXTERN char_u e_dictreq[] INIT(= N_("E715: Dictionary required")); EXTERN char_u e_listidx[] INIT(= N_("E684: list index out of range: %ld")); EXTERN char_u e_toomanyarg[] INIT(= N_("E118: Too many arguments for function: %s")); EXTERN char_u e_dictkey[] INIT(= N_("E716: Key not present in Dictionary: %s")); +EXTERN char_u e_listreq[] INIT(= N_("E714: List required")); +EXTERN char_u e_listdictarg[] INIT(= N_("E712: Argument of %s must be a List or Dictionary")); #endif #ifdef FEAT_QUICKFIX EXTERN char_u e_readerrf[] INIT(= N_("E47: Error while reading errorfile")); diff --git a/src/list.c b/src/list.c index e3794b6973..13f920039c 100644 --- a/src/list.c +++ b/src/list.c @@ -884,4 +884,44 @@ failret: return OK; } -#endif /* defined(FEAT_MODIFY_FNAME) || defined(FEAT_EVAL) */ +/* + * Write list of strings to file + */ + int +write_list(FILE *fd, list_T *list, int binary) +{ + listitem_T *li; + int c; + int ret = OK; + char_u *s; + + for (li = list->lv_first; li != NULL; li = li->li_next) + { + for (s = get_tv_string(&li->li_tv); *s != NUL; ++s) + { + if (*s == '\n') + c = putc(NUL, fd); + else + c = putc(*s, fd); + if (c == EOF) + { + ret = FAIL; + break; + } + } + if (!binary || li->li_next != NULL) + if (putc('\n', fd) == EOF) + { + ret = FAIL; + break; + } + if (ret == FAIL) + { + EMSG(_(e_write)); + break; + } + } + return ret; +} + +#endif /* defined(FEAT_EVAL) */ diff --git a/src/proto.h b/src/proto.h index b3683af424..660abc9f0a 100644 --- a/src/proto.h +++ b/src/proto.h @@ -72,6 +72,7 @@ extern int _stricoll(char *a, char *b); # include "digraph.pro" # include "edit.pro" # include "eval.pro" +# include "evalfunc.pro" # include "ex_cmds.pro" # include "ex_cmds2.pro" # include "ex_docmd.pro" diff --git a/src/proto/eval.pro b/src/proto/eval.pro index d7315b058c..c6d202d582 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -25,6 +25,7 @@ void *call_func_retlist(char_u *func, int argc, char_u **argv, int safe); int eval_foldexpr(char_u *arg, int *cp); void ex_let(exarg_T *eap); void list_hashtable_vars(hashtab_T *ht, char_u *prefix, int empty, int *first); +int check_changedtick(char_u *arg); char_u *get_lval(char_u *name, typval_T *rettv, lval_T *lp, int unlet, int skip, int flags, int fne_flags); void clear_lval(lval_T *lp); void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip); @@ -38,6 +39,7 @@ void del_menutrans_vars(void); char_u *get_user_var_name(expand_T *xp, int idx); int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate); int eval1(char_u **arg, typval_T *rettv, int evaluate); +int get_option_tv(char_u **arg, typval_T *rettv, int evaluate); void partial_unref(partial_T *pt); int tv_equal(typval_T *tv1, typval_T *tv2, int ic, int recursive); int get_copyID(void); @@ -50,18 +52,10 @@ char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); char_u *string_quote(char_u *str, int function); int string2float(char_u *text, float_T *value); -char_u *get_function_name(expand_T *xp, int idx); -char_u *get_expr_name(expand_T *xp, int idx); -int find_internal_func(char_u *name); -int call_internal_func(char_u *name, int argcount, typval_T *argvars, typval_T *rettv); -buf_T *buflist_find_by_name(char_u *name, int curtab_only); -void execute_redir_str(char_u *value, int value_len); -void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv); -float_T vim_round(float_T f); -long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, char_u *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit); -char_u *get_callback(typval_T *arg, partial_T **pp); -void free_callback(char_u *callback, partial_T *partial); +pos_T *var2fpos(typval_T *varp, int dollar_lnum, int *fnum); +int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp); int get_id_len(char_u **arg); +int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose); char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end, int flags); int eval_isnamec(int c); int eval_isnamec1(int c); @@ -78,32 +72,43 @@ void set_reg_var(int c); char_u *v_exception(char_u *oldval); char_u *v_throwpoint(char_u *oldval); char_u *set_cmdarg(exarg_T *eap, char_u *oldarg); +int get_var_tv(char_u *name, int len, typval_T *rettv, dictitem_T **dip, int verbose, int no_autoload); int handle_subscript(char_u **arg, typval_T *rettv, int evaluate, int verbose); typval_T *alloc_tv(void); void free_tv(typval_T *varp); void clear_tv(typval_T *varp); +void init_tv(typval_T *varp); varnumber_T get_tv_number(typval_T *varp); varnumber_T get_tv_number_chk(typval_T *varp, int *denote); +float_T get_tv_float(typval_T *varp); char_u *get_tv_string(typval_T *varp); char_u *get_tv_string_buf(typval_T *varp, char_u *buf); char_u *get_tv_string_chk(typval_T *varp); char_u *get_tv_string_buf_chk(typval_T *varp, char_u *buf); dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload); +dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload); char_u *get_var_value(char_u *name); void new_script_vars(scid_T id); void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope); void unref_var_dict(dict_T *dict); void vars_clear(hashtab_T *ht); void vars_clear_ext(hashtab_T *ht, int free_val); +void set_var(char_u *name, typval_T *tv, int copy); int var_check_ro(int flags, char_u *name, int use_gettext); +int var_check_fixed(int flags, char_u *name, int use_gettext); int var_check_func_name(char_u *name, int new_var); int valid_varname(char_u *varname); int tv_check_lock(int lock, char_u *name, int use_gettext); void copy_tv(typval_T *from, typval_T *to); int item_copy(typval_T *from, typval_T *to, int deep, int copyID); +void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog, int secret); void ex_echo(exarg_T *eap); void ex_echohl(exarg_T *eap); void ex_execute(exarg_T *eap); +win_T *find_win_by_nr(typval_T *vp, tabpage_T *tp); +win_T *find_tabwin(typval_T *wvp, typval_T *tvp); +void getwinvar(typval_T *argvars, typval_T *rettv, int off); +void setwinvar(typval_T *argvars, typval_T *rettv, int off); char_u *autoload_name(char_u *name); int script_autoload(char_u *name, int reload); int read_viminfo_varlist(vir_T *virp, int writing); @@ -112,6 +117,15 @@ int store_session_globals(FILE *fd); void last_set_msg(scid_T scriptID); void ex_oldfiles(exarg_T *eap); void reset_v_option_vars(void); +void prepare_assert_error(garray_T *gap); +void assert_error(garray_T *gap); +void assert_equal_common(typval_T *argvars, assert_type_T atype); +void assert_match_common(typval_T *argvars, assert_type_T atype); +void assert_bool(typval_T *argvars, int isTrue); +void assert_exception(typval_T *argvars); +void assert_fails(typval_T *argvars); +void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_str, typval_T *exp_tv, typval_T *got_tv, assert_type_T atype); int modify_fname(char_u *src, int *usedlen, char_u **fnamep, char_u **bufp, int *fnamelen); char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags); +void filter_map(typval_T *argvars, typval_T *rettv, int map); /* vim: set ft=c : */ diff --git a/src/proto/evalfunc.pro b/src/proto/evalfunc.pro new file mode 100644 index 0000000000..c673e27a53 --- /dev/null +++ b/src/proto/evalfunc.pro @@ -0,0 +1,13 @@ +/* evalfunc.c */ +char_u *get_function_name(expand_T *xp, int idx); +char_u *get_expr_name(expand_T *xp, int idx); +int find_internal_func(char_u *name); +int call_internal_func(char_u *name, int argcount, typval_T *argvars, typval_T *rettv); +buf_T *buflist_find_by_name(char_u *name, int curtab_only); +void execute_redir_str(char_u *value, int value_len); +void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv); +float_T vim_round(float_T f); +long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, char_u *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit); +char_u *get_callback(typval_T *arg, partial_T **pp); +void free_callback(char_u *callback, partial_T *partial); +/* vim: set ft=c : */ diff --git a/src/proto/list.pro b/src/proto/list.pro index 602166abe1..9849379597 100644 --- a/src/proto/list.pro +++ b/src/proto/list.pro @@ -31,4 +31,5 @@ void vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2); char_u *list2string(typval_T *tv, int copyID, int restore_copyID); int list_join(garray_T *gap, list_T *l, char_u *sep, int echo_style, int restore_copyID, int copyID); int get_list_tv(char_u **arg, typval_T *rettv, int evaluate); +int write_list(FILE *fd, list_T *list, int binary); /* vim: set ft=c : */ diff --git a/src/version.c b/src/version.c index 665c93e928..e46a13f60f 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2063, /**/ 2062, /**/ diff --git a/src/vim.h b/src/vim.h index 49fb8704cd..ed2ab4abd2 100644 --- a/src/vim.h +++ b/src/vim.h @@ -2051,6 +2051,15 @@ typedef struct _stat64 stat_T; typedef struct stat stat_T; #endif +typedef enum +{ + ASSERT_EQUAL, + ASSERT_NOTEQUAL, + ASSERT_MATCH, + ASSERT_NOTMATCH, + ASSERT_OTHER +} assert_type_T; + #include "ex_cmds.h" /* Ex command defines */ #include "proto.h" /* function prototypes */ From 4f5c5f29806e53251b7a7b68ce7de86a21ff8015 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 17 Jul 2016 22:25:36 +0200 Subject: [PATCH 7/9] patch 7.4.2064 Problem: Coverity warns for possible buffer overflow. Solution: Use vim_strcat() instead of strcat(). --- src/quickfix.c | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/quickfix.c b/src/quickfix.c index b2b6e8e294..3794e6ab09 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -2595,7 +2595,7 @@ qf_msg(qf_info_T *qi, int which, char *lead) { while (STRLEN(buf) < 34) STRCAT(buf, " "); - STRCAT(buf, title); + vim_strcat(buf, title, IOSIZE); } trunc_string(buf, buf, Columns - 1, IOSIZE); msg(buf); diff --git a/src/version.c b/src/version.c index e46a13f60f..e1fcacac23 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2064, /**/ 2063, /**/ From 69aa099641616268bfcde9dc3aa313c677846b12 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 17 Jul 2016 22:33:53 +0200 Subject: [PATCH 8/9] patch 7.4.2065 Problem: Compiler warns for uninitialzed variable. (John Marriott) Solution: Set lnum to the right value. --- src/evalfunc.c | 5 +---- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index 7d3f875210..07c7575173 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -3447,12 +3447,9 @@ f_foldtext(typval_T *argvars UNUSED, typval_T *rettv) && dashes != NULL) { /* Find first non-empty line in the fold. */ - while (lnum < (linenr_T)get_vim_var_nr(VV_FOLDEND)) - { + for (lnum = foldstart; lnum < foldend; ++lnum) if (!linewhite(lnum)) break; - ++lnum; - } /* Find interesting text in this line. */ s = skipwhite(ml_get(lnum)); diff --git a/src/version.c b/src/version.c index e1fcacac23..8ebd309556 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2065, /**/ 2064, /**/ From c1fb763184c8ae82300357867fa2070aa94366e9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 17 Jul 2016 23:34:21 +0200 Subject: [PATCH 9/9] patch 7.4.2066 Problem: getcompletion() not well tested. Solution: Add more testing. --- src/testdir/test_cmdline.vim | 74 ++++++++++++++++++++++++++++++++++++ src/version.c | 2 + 2 files changed, 76 insertions(+) diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 3482153237..054d39b0f0 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -45,5 +45,79 @@ func Test_getcompletion() call assert_true(matchcount > 0) endif + let l = getcompletion('v:n', 'var') + call assert_true(index(l, 'v:null') >= 0) + + let l = getcompletion('', 'augroup') + call assert_true(index(l, 'END') >= 0) + + let l = getcompletion('', 'behave') + call assert_true(index(l, 'mswin') >= 0) + + let l = getcompletion('', 'color') + call assert_true(index(l, 'default') >= 0) + + let l = getcompletion('', 'command') + call assert_true(index(l, 'sleep') >= 0) + + let l = getcompletion('', 'dir') + call assert_true(index(l, 'samples') >= 0) + + let l = getcompletion('exe', 'expression') + call assert_true(index(l, 'executable(') >= 0) + + let l = getcompletion('tag', 'function') + call assert_true(index(l, 'taglist(') >= 0) + + let l = getcompletion('run', 'file') + call assert_true(index(l, 'runtest.vim') >= 0) + + let l = getcompletion('ha', 'filetype') + call assert_true(index(l, 'hamster') >= 0) + + let l = getcompletion('z', 'syntax') + call assert_true(index(l, 'zimbu') >= 0) + + let l = getcompletion('jikes', 'compiler') + call assert_true(index(l, 'jikes') >= 0) + + let l = getcompletion('last', 'help') + call assert_true(index(l, ':tablast') >= 0) + + let l = getcompletion('time', 'option') + call assert_true(index(l, 'timeoutlen') >= 0) + + let l = getcompletion('er', 'highlight') + call assert_true(index(l, 'ErrorMsg') >= 0) + + " For others test if the name is recognized. + let names = ['buffer', 'environment', 'file_in_path', + \ 'mapping', 'shellcmd', 'tag', 'tag_listfiles', 'user'] + if has('cscope') + call add(names, 'cscope') + endif + if has('cmdline_hist') + call add(names, 'history') + endif + if has('gettext') + call add(names, 'locale') + endif + if has('profile') + call add(names, 'syntime') + endif + if has('signs') + call add(names, 'sign') + endif + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", "word\tfile\tcmd"], 'Xtags') + + for name in names + let matchcount = len(getcompletion('', name)) + call assert_true(matchcount >= 0, 'No matches for ' . name) + endfor + + call delete('Xtags') + call assert_fails('call getcompletion("", "burp")', 'E475:') endfunc diff --git a/src/version.c b/src/version.c index 8ebd309556..8f6134b5be 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2066, /**/ 2065, /**/