# HG changeset patch # User Christian Brabandt # Date 1468787406 -7200 # Node ID 5eaa708ab50dd17b685a25ec11f5975815a795ee # Parent 695186e11daa8bfb3738dd04136e01d5b815ef36 commit https://github.com/vim/vim/commit/73dad1e64cb42842d8259cb1a255a6fa59822f76 Author: Bram Moolenaar Date: Sun Jul 17 22:13:49 2016 +0200 patch 7.4.2063 Problem: eval.c is still too big. Solution: Split off internal functions to evalfunc.c. diff --git a/Filelist b/Filelist --- 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 --- 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 --- 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 --- 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 --- 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 --- 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 --- 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 --- 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 @@ testclean: $(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 --- 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 --- 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 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 --- 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 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 lo --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 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 *d } #ifdef FEAT_FLOAT - static float_T + float_T get_tv_float(typval_T *varp) { switch (varp->v_type) @@ -20445,44 +7125,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. * get_tv_string() uses a single, static buffer. YOU CAN ONLY USE IT ONCE! @@ -20624,7 +7266,7 @@ find_var(char_u *name, hashtab_T **htp, * 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, in * 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) @@ -21310,6 +7952,110 @@ item_copy( } /* + * 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. * ":echon expr1 ..." print each argument plain. @@ -21516,6 +8262,224 @@ ex_execute(exarg_T *eap) } /* + * 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. * Returns NULL when no option name found. Otherwise pointer to the char @@ -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 --- /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 --- a/src/globals.h +++ b/src/globals.h @@ -1538,6 +1538,8 @@ EXTERN char_u e_dictreq[] INIT(= N_("E71 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 --- 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 --- 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 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -25,6 +25,7 @@ void *call_func_retlist(char_u *func, in 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 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 --- /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 --- a/src/proto/list.pro +++ b/src/proto/list.pro @@ -31,4 +31,5 @@ void vimlist_remove(list_T *l, listitem_ 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 --- a/src/version.c +++ b/src/version.c @@ -759,6 +759,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2063, +/**/ 2062, /**/ 2061, diff --git a/src/vim.h b/src/vim.h --- 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 */