To: vim_dev@googlegroups.com Subject: Patch 7.4.2063 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 7.4.2063 Problem: eval.c is still too big. Solution: Split off internal functions to evalfunc.c. Files: src/eval.c, src/evalfunc.c, src/list.c, src/proto.h, src/globals.h, src/vim.h, src/proto/eval.pro, src/proto/evalfunc.pro, src/proto/list.pro, src/Makefile, Filelist, src/Make_bc5.mak, src/Make_cyg_ming.mak, src/Make_dice.mak, src/Make_ivc.mak, src/Make_manx.mak, src/Make_morph.mak, src/Make_mvc.mak, src/Make_sas.mak *** ../vim-7.4.2062/src/eval.c 2016-07-17 18:28:59.023697505 +0200 --- src/eval.c 2016-07-17 22:00:20.456911568 +0200 *************** *** 16,44 **** #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"); --- 16,30 ---- *************** *** 101,107 **** * 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:" */ --- 87,92 ---- *************** *** 206,213 **** 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); --- 191,196 ---- *************** *** 221,234 **** 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); --- 204,215 ---- *************** *** 238,682 **** 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 /* * Initialize the global and v: variables. */ --- 219,247 ---- 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_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 get_env_len(char_u **arg); static char_u * make_expanded_name(char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end); static typval_T *alloc_string_tv(char_u *string); 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 char_u *find_option_end(char_u **arg, int *opt_flags); #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,2206 **** /* * If "arg" is equal to "b:changedtick" give an error and return TRUE. */ ! static int check_changedtick(char_u *arg) { if (STRNCMP(arg, "b:changedtick", 13) == 0 && !eval_isnamec(arg[13])) --- 1765,1771 ---- /* * If "arg" is equal to "b:changedtick" give an error and return TRUE. */ ! int check_changedtick(char_u *arg) { if (STRNCMP(arg, "b:changedtick", 13) == 0 && !eval_isnamec(arg[13])) *************** *** 3413,3434 **** --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. --- 2978,2983 ---- *************** *** 5159,5165 **** * "arg" is advanced to character after the option name. * Return OK or FAIL. */ ! static int get_option_tv( char_u **arg, typval_T *rettv, /* when NULL, only check if option exists */ --- 4708,4714 ---- * "arg" is advanced to character after the option name. * Return OK or FAIL. */ ! int get_option_tv( char_u **arg, typval_T *rettv, /* when NULL, only check if option exists */ *************** *** 6421,19421 **** 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 * ! var2fpos( ! typval_T *varp, ! int dollar_lnum, /* TRUE when $ is last line */ ! int *fnum) /* set to fnum for '0, 'A, etc. */ ! { ! char_u *name; ! static pos_T pos; ! pos_T *pp; ! ! /* Argument can be [lnum, col, coladd]. */ ! if (varp->v_type == VAR_LIST) ! { ! list_T *l; ! int len; ! int error = FALSE; ! listitem_T *li; ! ! l = varp->vval.v_list; ! if (l == NULL) ! return NULL; ! ! /* Get the line number */ ! pos.lnum = list_find_nr(l, 0L, &error); ! if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) ! return NULL; /* invalid line number */ ! ! /* Get the column number */ ! pos.col = list_find_nr(l, 1L, &error); ! if (error) ! return NULL; ! len = (long)STRLEN(ml_get(pos.lnum)); ! ! /* We accept "$" for the column number: last column. */ ! li = list_find(l, 1L); ! if (li != NULL && li->li_tv.v_type == VAR_STRING ! && li->li_tv.vval.v_string != NULL ! && STRCMP(li->li_tv.vval.v_string, "$") == 0) ! pos.col = len + 1; ! ! /* Accept a position up to the NUL after the line. */ ! if (pos.col == 0 || (int)pos.col > len + 1) ! return NULL; /* invalid column number */ ! --pos.col; ! ! #ifdef FEAT_VIRTUALEDIT ! /* Get the virtual offset. Defaults to zero. */ ! pos.coladd = list_find_nr(l, 2L, &error); ! if (error) ! pos.coladd = 0; ! #endif ! ! return &pos; ! } ! ! name = get_tv_string_chk(varp); ! if (name == NULL) ! return NULL; ! if (name[0] == '.') /* cursor */ ! return &curwin->w_cursor; ! if (name[0] == 'v' && name[1] == NUL) /* Visual start */ ! { ! if (VIsual_active) ! return &VIsual; ! return &curwin->w_cursor; ! } ! if (name[0] == '\'') /* mark */ ! { ! pp = getmark_buf_fnum(curbuf, name[1], FALSE, fnum); ! if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) ! return NULL; ! return pp; ! } ! ! #ifdef FEAT_VIRTUALEDIT ! pos.coladd = 0; ! #endif ! ! if (name[0] == 'w' && dollar_lnum) ! { ! pos.col = 0; ! if (name[1] == '0') /* "w0": first visible line */ ! { ! update_topline(); ! pos.lnum = curwin->w_topline; ! return &pos; ! } ! else if (name[1] == '$') /* "w$": last visible line */ ! { ! validate_botline(); ! pos.lnum = curwin->w_botline - 1; ! return &pos; ! } ! } ! else if (name[0] == '$') /* last column or line */ ! { ! if (dollar_lnum) ! { ! pos.lnum = curbuf->b_ml.ml_line_count; ! pos.col = 0; ! } ! else ! { ! pos.lnum = curwin->w_cursor.lnum; ! pos.col = (colnr_T)STRLEN(ml_get_curline()); ! } ! return &pos; ! } ! return NULL; ! } ! ! /* ! * Convert list in "arg" into a position and optional file number. ! * When "fnump" is NULL there is no file number, only 3 items. ! * Note that the column is passed on as-is, the caller may want to decrement ! * it to use 1 for the first column. ! * Return FAIL when conversion is not possible, doesn't check the position for ! * validity. ! */ ! static int list2fpos( typval_T *arg, pos_T *posp, --- 5970,6101 ---- return OK; } /* ! * Translate a String variable into a position. ! * Returns NULL when there is an error. */ ! pos_T * ! var2fpos( ! typval_T *varp, ! int dollar_lnum, /* TRUE when $ is last line */ ! int *fnum) /* set to fnum for '0, 'A, etc. */ { ! char_u *name; ! static pos_T pos; ! pos_T *pp; ! /* Argument can be [lnum, col, coladd]. */ ! if (varp->v_type == VAR_LIST) { ! list_T *l; ! int len; ! int error = FALSE; ! listitem_T *li; ! l = varp->vval.v_list; ! if (l == NULL) ! return NULL; ! /* Get the line number */ ! pos.lnum = list_find_nr(l, 0L, &error); ! if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) ! return NULL; /* invalid line number */ ! /* Get the column number */ ! pos.col = list_find_nr(l, 1L, &error); ! if (error) ! return NULL; ! len = (long)STRLEN(ml_get(pos.lnum)); ! /* We accept "$" for the column number: last column. */ ! li = list_find(l, 1L); ! if (li != NULL && li->li_tv.v_type == VAR_STRING ! && li->li_tv.vval.v_string != NULL ! && STRCMP(li->li_tv.vval.v_string, "$") == 0) ! pos.col = len + 1; ! /* Accept a position up to the NUL after the line. */ ! if (pos.col == 0 || (int)pos.col > len + 1) ! return NULL; /* invalid column number */ ! --pos.col; ! #ifdef FEAT_VIRTUALEDIT ! /* Get the virtual offset. Defaults to zero. */ ! pos.coladd = list_find_nr(l, 2L, &error); ! if (error) ! pos.coladd = 0; #endif + return &pos; + } ! name = get_tv_string_chk(varp); ! if (name == NULL) ! return NULL; ! if (name[0] == '.') /* cursor */ ! return &curwin->w_cursor; ! if (name[0] == 'v' && name[1] == NUL) /* Visual start */ { ! if (VIsual_active) ! return &VIsual; ! return &curwin->w_cursor; ! } ! if (name[0] == '\'') /* mark */ ! { ! pp = getmark_buf_fnum(curbuf, name[1], FALSE, fnum); ! if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) ! return NULL; ! return pp; } ! #ifdef FEAT_VIRTUALEDIT ! pos.coladd = 0; ! #endif ! if (name[0] == 'w' && dollar_lnum) ! { ! pos.col = 0; ! if (name[1] == '0') /* "w0": first visible line */ ! { ! update_topline(); ! pos.lnum = curwin->w_topline; ! return &pos; ! } ! else if (name[1] == '$') /* "w$": last visible line */ ! { ! validate_botline(); ! pos.lnum = curwin->w_botline - 1; ! return &pos; ! } ! } ! else if (name[0] == '$') /* last column or line */ ! { ! if (dollar_lnum) ! { ! pos.lnum = curbuf->b_ml.ml_line_count; ! pos.col = 0; ! } ! else ! { ! pos.lnum = curwin->w_cursor.lnum; ! pos.col = (colnr_T)STRLEN(ml_get_curline()); ! } ! return &pos; ! } ! return NULL; } /* ! * Convert list in "arg" into a position and optional file number. ! * When "fnump" is NULL there is no file number, only 3 items. ! * Note that the column is passed on as-is, the caller may want to decrement ! * it to use 1 for the first column. ! * Return FAIL when conversion is not possible, doesn't check the position for ! * validity. */ ! int list2fpos( typval_T *arg, pos_T *posp, *************** *** 19531,19537 **** * If the name contains 'magic' {}'s, expand them and return the * expanded name in an allocated string via 'alias' - caller must free. */ ! static int get_name_len( char_u **arg, char_u **alias, --- 6211,6217 ---- * If the name contains 'magic' {}'s, expand them and return the * expanded name in an allocated string via 'alias' - caller must free. */ ! int get_name_len( char_u **arg, char_u **alias, *************** *** 20032,20038 **** * Get the value of internal variable "name". * Return OK or FAIL. */ ! static int get_var_tv( char_u *name, int len, /* length of "name" */ --- 6712,6718 ---- * Get the value of internal variable "name". * Return OK or FAIL. */ ! int get_var_tv( char_u *name, int len, /* length of "name" */ *************** *** 20324,20330 **** /* * Set the value of a variable to NULL without freeing items. */ ! static void init_tv(typval_T *varp) { if (varp != NULL) --- 7004,7010 ---- /* * Set the value of a variable to NULL without freeing items. */ ! void init_tv(typval_T *varp) { if (varp != NULL) *************** *** 20401,20407 **** } #ifdef FEAT_FLOAT ! static float_T get_tv_float(typval_T *varp) { switch (varp->v_type) --- 7081,7087 ---- } #ifdef FEAT_FLOAT ! float_T get_tv_float(typval_T *varp) { switch (varp->v_type) *************** *** 20445,20488 **** #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! --- 7125,7130 ---- *************** *** 20624,20630 **** * Find variable "varname" in hashtab "ht" with name "htname". * Returns NULL if not found. */ ! static dictitem_T * find_var_in_ht( hashtab_T *ht, int htname, --- 7266,7272 ---- * Find variable "varname" in hashtab "ht" with name "htname". * Returns NULL if not found. */ ! dictitem_T * find_var_in_ht( hashtab_T *ht, int htname, *************** *** 20936,20942 **** * If the variable already exists, the value is updated. * Otherwise the variable is created. */ ! static void set_var( char_u *name, typval_T *tv, --- 7578,7584 ---- * If the variable already exists, the value is updated. * Otherwise the variable is created. */ ! void set_var( char_u *name, typval_T *tv, *************** *** 21064,21070 **** * Return TRUE if di_flags "flags" indicates variable "name" is fixed. * Also give an error message. */ ! static int var_check_fixed(int flags, char_u *name, int use_gettext) { if (flags & DI_FLAGS_FIX) --- 7706,7712 ---- * Return TRUE if di_flags "flags" indicates variable "name" is fixed. * Also give an error message. */ ! int var_check_fixed(int flags, char_u *name, int use_gettext) { if (flags & DI_FLAGS_FIX) *************** *** 21305,21312 **** EMSG2(_(e_intern2), "item_copy(UNKNOWN)"); ret = FAIL; } ! --recurse; ! return ret; } /* --- 7947,8058 ---- EMSG2(_(e_intern2), "item_copy(UNKNOWN)"); ret = FAIL; } ! --recurse; ! return ret; ! } ! ! /* ! * This function is used by f_input() and f_inputdialog() functions. The third ! * argument to f_input() specifies the type of completion to use at the ! * prompt. The third argument to f_inputdialog() specifies the value to return ! * when the user cancels the prompt. ! */ ! void ! get_user_input( ! typval_T *argvars, ! typval_T *rettv, ! int inputdialog, ! int secret) ! { ! char_u *prompt = get_tv_string_chk(&argvars[0]); ! char_u *p = NULL; ! int c; ! char_u buf[NUMBUFLEN]; ! int cmd_silent_save = cmd_silent; ! char_u *defstr = (char_u *)""; ! int xp_type = EXPAND_NOTHING; ! char_u *xp_arg = NULL; ! ! rettv->v_type = VAR_STRING; ! rettv->vval.v_string = NULL; ! ! #ifdef NO_CONSOLE_INPUT ! /* While starting up, there is no place to enter text. */ ! if (no_console_input()) ! return; ! #endif ! ! cmd_silent = FALSE; /* Want to see the prompt. */ ! if (prompt != NULL) ! { ! /* Only the part of the message after the last NL is considered as ! * prompt for the command line */ ! p = vim_strrchr(prompt, '\n'); ! if (p == NULL) ! p = prompt; ! else ! { ! ++p; ! c = *p; ! *p = NUL; ! msg_start(); ! msg_clr_eos(); ! msg_puts_attr(prompt, echo_attr); ! msg_didout = FALSE; ! msg_starthere(); ! *p = c; ! } ! cmdline_row = msg_row; ! ! if (argvars[1].v_type != VAR_UNKNOWN) ! { ! defstr = get_tv_string_buf_chk(&argvars[1], buf); ! if (defstr != NULL) ! stuffReadbuffSpec(defstr); ! ! if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) ! { ! char_u *xp_name; ! int xp_namelen; ! long argt; ! ! /* input() with a third argument: completion */ ! rettv->vval.v_string = NULL; ! ! xp_name = get_tv_string_buf_chk(&argvars[2], buf); ! if (xp_name == NULL) ! return; ! ! xp_namelen = (int)STRLEN(xp_name); ! ! if (parse_compl_arg(xp_name, xp_namelen, &xp_type, &argt, ! &xp_arg) == FAIL) ! return; ! } ! } ! ! if (defstr != NULL) ! { ! int save_ex_normal_busy = ex_normal_busy; ! ex_normal_busy = 0; ! rettv->vval.v_string = ! getcmdline_prompt(secret ? NUL : '@', p, echo_attr, ! xp_type, xp_arg); ! ex_normal_busy = save_ex_normal_busy; ! } ! if (inputdialog && rettv->vval.v_string == NULL ! && argvars[1].v_type != VAR_UNKNOWN ! && argvars[2].v_type != VAR_UNKNOWN) ! rettv->vval.v_string = vim_strsave(get_tv_string_buf( ! &argvars[2], buf)); ! ! vim_free(xp_arg); ! ! /* since the user typed this, no need to wait for return */ ! need_wait_return = FALSE; ! msg_didout = FALSE; ! } ! cmd_silent = cmd_silent_save; } /* *************** *** 21473,21518 **** len = (int)STRLEN(p); if (ga_grow(&ga, len + 2) == FAIL) { ! clear_tv(&rettv); ! ret = FAIL; ! break; } - if (ga.ga_len) - ((char_u *)(ga.ga_data))[ga.ga_len++] = ' '; - STRCPY((char_u *)(ga.ga_data) + ga.ga_len, p); - ga.ga_len += len; - } - - clear_tv(&rettv); - arg = skipwhite(arg); - } - - if (ret != FAIL && ga.ga_data != NULL) - { - if (eap->cmdidx == CMD_echomsg) - { - MSG_ATTR(ga.ga_data, echo_attr); - out_flush(); - } - else if (eap->cmdidx == CMD_echoerr) - { - /* We don't want to abort following commands, restore did_emsg. */ - save_did_emsg = did_emsg; - EMSG((char_u *)ga.ga_data); - if (!force_abort) - did_emsg = save_did_emsg; } ! else if (eap->cmdidx == CMD_execute) ! do_cmdline((char_u *)ga.ga_data, ! eap->getline, eap->cookie, DOCMD_NOWAIT|DOCMD_VERBOSE); } - - ga_clear(&ga); - - if (eap->skip) - --emsg_skip; - - eap->nextcmd = check_nextcmd(arg); } /* --- 8219,8482 ---- len = (int)STRLEN(p); if (ga_grow(&ga, len + 2) == FAIL) { ! clear_tv(&rettv); ! ret = FAIL; ! break; ! } ! if (ga.ga_len) ! ((char_u *)(ga.ga_data))[ga.ga_len++] = ' '; ! STRCPY((char_u *)(ga.ga_data) + ga.ga_len, p); ! ga.ga_len += len; ! } ! ! clear_tv(&rettv); ! arg = skipwhite(arg); ! } ! ! if (ret != FAIL && ga.ga_data != NULL) ! { ! if (eap->cmdidx == CMD_echomsg) ! { ! MSG_ATTR(ga.ga_data, echo_attr); ! out_flush(); ! } ! else if (eap->cmdidx == CMD_echoerr) ! { ! /* We don't want to abort following commands, restore did_emsg. */ ! save_did_emsg = did_emsg; ! EMSG((char_u *)ga.ga_data); ! if (!force_abort) ! did_emsg = save_did_emsg; ! } ! else if (eap->cmdidx == CMD_execute) ! do_cmdline((char_u *)ga.ga_data, ! eap->getline, eap->cookie, DOCMD_NOWAIT|DOCMD_VERBOSE); ! } ! ! ga_clear(&ga); ! ! if (eap->skip) ! --emsg_skip; ! ! eap->nextcmd = check_nextcmd(arg); ! } ! ! /* ! * Find window specified by "vp" in tabpage "tp". ! */ ! win_T * ! find_win_by_nr( ! typval_T *vp, ! tabpage_T *tp UNUSED) /* NULL for current tab page */ ! { ! #ifdef FEAT_WINDOWS ! win_T *wp; ! #endif ! int nr; ! ! nr = (int)get_tv_number_chk(vp, NULL); ! ! #ifdef FEAT_WINDOWS ! if (nr < 0) ! return NULL; ! if (nr == 0) ! return curwin; ! ! for (wp = (tp == NULL || tp == curtab) ? firstwin : tp->tp_firstwin; ! wp != NULL; wp = wp->w_next) ! if (nr >= LOWEST_WIN_ID) ! { ! if (wp->w_id == nr) ! return wp; ! } ! else if (--nr <= 0) ! break; ! if (nr >= LOWEST_WIN_ID) ! return NULL; ! return wp; ! #else ! if (nr == 0 || nr == 1 || nr == curwin->w_id) ! return curwin; ! return NULL; ! #endif ! } ! ! /* ! * Find window specified by "wvp" in tabpage "tvp". ! */ ! win_T * ! find_tabwin( ! typval_T *wvp, /* VAR_UNKNOWN for current window */ ! typval_T *tvp) /* VAR_UNKNOWN for current tab page */ ! { ! win_T *wp = NULL; ! tabpage_T *tp = NULL; ! long n; ! ! if (wvp->v_type != VAR_UNKNOWN) ! { ! if (tvp->v_type != VAR_UNKNOWN) ! { ! n = (long)get_tv_number(tvp); ! if (n >= 0) ! tp = find_tabpage(n); ! } ! else ! tp = curtab; ! ! if (tp != NULL) ! wp = find_win_by_nr(wvp, tp); ! } ! else ! wp = curwin; ! ! return wp; ! } ! ! /* ! * getwinvar() and gettabwinvar() ! */ ! void ! getwinvar( ! typval_T *argvars, ! typval_T *rettv, ! int off) /* 1 for gettabwinvar() */ ! { ! win_T *win; ! char_u *varname; ! dictitem_T *v; ! tabpage_T *tp = NULL; ! int done = FALSE; ! #ifdef FEAT_WINDOWS ! win_T *oldcurwin; ! tabpage_T *oldtabpage; ! int need_switch_win; ! #endif ! ! #ifdef FEAT_WINDOWS ! if (off == 1) ! tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); ! else ! tp = curtab; ! #endif ! win = find_win_by_nr(&argvars[off], tp); ! varname = get_tv_string_chk(&argvars[off + 1]); ! ++emsg_off; ! ! rettv->v_type = VAR_STRING; ! rettv->vval.v_string = NULL; ! ! if (win != NULL && varname != NULL) ! { ! #ifdef FEAT_WINDOWS ! /* Set curwin to be our win, temporarily. Also set the tabpage, ! * otherwise the window is not valid. Only do this when needed, ! * autocommands get blocked. */ ! need_switch_win = !(tp == curtab && win == curwin); ! if (!need_switch_win ! || switch_win(&oldcurwin, &oldtabpage, win, tp, TRUE) == OK) ! #endif ! { ! if (*varname == '&') /* window-local-option */ ! { ! if (get_option_tv(&varname, rettv, 1) == OK) ! done = TRUE; ! } ! else ! { ! /* Look up the variable. */ ! /* Let getwinvar({nr}, "") return the "w:" dictionary. */ ! v = find_var_in_ht(&win->w_vars->dv_hashtab, 'w', ! varname, FALSE); ! if (v != NULL) ! { ! copy_tv(&v->di_tv, rettv); ! done = TRUE; ! } ! } ! } ! ! #ifdef FEAT_WINDOWS ! if (need_switch_win) ! /* restore previous notion of curwin */ ! restore_win(oldcurwin, oldtabpage, TRUE); ! #endif ! } ! ! if (!done && argvars[off + 2].v_type != VAR_UNKNOWN) ! /* use the default return value */ ! copy_tv(&argvars[off + 2], rettv); ! ! --emsg_off; ! } ! ! /* ! * "setwinvar()" and "settabwinvar()" functions ! */ ! void ! setwinvar(typval_T *argvars, typval_T *rettv UNUSED, int off) ! { ! win_T *win; ! #ifdef FEAT_WINDOWS ! win_T *save_curwin; ! tabpage_T *save_curtab; ! int need_switch_win; ! #endif ! char_u *varname, *winvarname; ! typval_T *varp; ! char_u nbuf[NUMBUFLEN]; ! tabpage_T *tp = NULL; ! ! if (check_restricted() || check_secure()) ! return; ! ! #ifdef FEAT_WINDOWS ! if (off == 1) ! tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); ! else ! tp = curtab; ! #endif ! win = find_win_by_nr(&argvars[off], tp); ! varname = get_tv_string_chk(&argvars[off + 1]); ! varp = &argvars[off + 2]; ! ! if (win != NULL && varname != NULL && varp != NULL) ! { ! #ifdef FEAT_WINDOWS ! need_switch_win = !(tp == curtab && win == curwin); ! if (!need_switch_win ! || switch_win(&save_curwin, &save_curtab, win, tp, TRUE) == OK) ! #endif ! { ! if (*varname == '&') ! { ! long numval; ! char_u *strval; ! int error = FALSE; ! ! ++varname; ! numval = (long)get_tv_number_chk(varp, &error); ! strval = get_tv_string_buf_chk(varp, nbuf); ! if (!error && strval != NULL) ! set_option_value(varname, numval, strval, OPT_LOCAL); ! } ! else ! { ! winvarname = alloc((unsigned)STRLEN(varname) + 3); ! if (winvarname != NULL) ! { ! STRCPY(winvarname, "w:"); ! STRCPY(winvarname + 2, varname); ! set_var(winvarname, varp, TRUE); ! vim_free(winvarname); ! } } } ! #ifdef FEAT_WINDOWS ! if (need_switch_win) ! restore_win(save_curwin, save_curtab, TRUE); ! #endif } } /* *************** *** 21910,21935 **** if (p != NULL) { ! p = expand_env_save(p); ! eap->arg = p; ! eap->cmdidx = CMD_edit; ! cmdmod.browse = FALSE; ! do_exedit(eap, NULL); ! vim_free(p); } ! } } - #endif - } } ! /* reset v:option_new, v:option_old and v:option_type */ void ! reset_v_option_vars(void) { ! set_vim_var_string(VV_OPTION_NEW, NULL, -1); ! set_vim_var_string(VV_OPTION_OLD, NULL, -1); ! set_vim_var_string(VV_OPTION_TYPE, NULL, -1); } --- 8874,9148 ---- if (p != NULL) { ! p = expand_env_save(p); ! eap->arg = p; ! eap->cmdidx = CMD_edit; ! cmdmod.browse = FALSE; ! do_exedit(eap, NULL); ! vim_free(p); ! } ! } ! } ! #endif ! } ! } ! ! /* reset v:option_new, v:option_old and v:option_type */ ! void ! reset_v_option_vars(void) ! { ! set_vim_var_string(VV_OPTION_NEW, NULL, -1); ! set_vim_var_string(VV_OPTION_OLD, NULL, -1); ! 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); ! } } *************** *** 22678,22681 **** --- 9891,10082 ---- 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) */ *** ../vim-7.4.2062/src/evalfunc.c 2016-07-17 22:12:06.577893023 +0200 --- src/evalfunc.c 2016-07-17 22:00:26.832848207 +0200 *************** *** 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 */ *** ../vim-7.4.2062/src/list.c 2016-07-17 15:46:08.464046515 +0200 --- src/list.c 2016-07-17 21:12:42.397806148 +0200 *************** *** 884,887 **** return OK; } ! #endif /* defined(FEAT_MODIFY_FNAME) || defined(FEAT_EVAL) */ --- 884,927 ---- return OK; } ! /* ! * 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) */ *** ../vim-7.4.2062/src/proto.h 2016-07-17 18:28:59.027697464 +0200 --- src/proto.h 2016-07-17 20:54:37.640852931 +0200 *************** *** 72,77 **** --- 72,78 ---- # include "digraph.pro" # include "edit.pro" # include "eval.pro" + # include "evalfunc.pro" # include "ex_cmds.pro" # include "ex_cmds2.pro" # include "ex_docmd.pro" *** ../vim-7.4.2062/src/globals.h 2016-07-17 20:37:38.235378864 +0200 --- src/globals.h 2016-07-17 21:42:07.311866180 +0200 *************** *** 1538,1543 **** --- 1538,1545 ---- 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")); *** ../vim-7.4.2062/src/vim.h 2016-07-17 20:37:38.235378864 +0200 --- src/vim.h 2016-07-17 21:22:16.843977154 +0200 *************** *** 2051,2056 **** --- 2051,2065 ---- 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 */ *** ../vim-7.4.2062/src/proto/eval.pro 2016-07-17 18:28:59.027697464 +0200 --- src/proto/eval.pro 2016-07-17 22:00:24.420872177 +0200 *************** *** 25,30 **** --- 25,31 ---- 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,43 **** --- 39,45 ---- 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,67 **** 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); int get_id_len(char_u **arg); char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end, int flags); int eval_isnamec(int c); int eval_isnamec1(int c); --- 52,61 ---- 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); ! 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,109 **** --- 72,114 ---- 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,117 **** --- 117,131 ---- 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 : */ *** ../vim-7.4.2062/src/proto/evalfunc.pro 2016-07-17 22:12:06.613892666 +0200 --- src/proto/evalfunc.pro 2016-07-17 20:55:57.340030382 +0200 *************** *** 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 : */ *** ../vim-7.4.2062/src/proto/list.pro 2016-07-17 15:46:08.464046515 +0200 --- src/proto/list.pro 2016-07-17 21:13:05.025575564 +0200 *************** *** 31,34 **** --- 31,35 ---- 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 : */ *** ../vim-7.4.2062/src/Makefile 2016-07-17 18:28:59.027697464 +0200 --- src/Makefile 2016-07-17 20:54:30.184929985 +0200 *************** *** 1489,1494 **** --- 1489,1495 ---- digraph.c \ edit.c \ eval.c \ + evalfunc.c \ ex_cmds.c \ ex_cmds2.c \ ex_docmd.c \ *************** *** 1593,1598 **** --- 1594,1600 ---- 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,1688 **** --- 1685,1691 ---- digraph.pro \ edit.pro \ eval.pro \ + evalfunc.pro \ ex_cmds.pro \ ex_cmds2.pro \ ex_docmd.pro \ *************** *** 2830,2835 **** --- 2833,2841 ---- 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,3225 **** --- 3226,3235 ---- 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 \ *** ../vim-7.4.2062/Filelist 2016-07-17 18:28:59.027697464 +0200 --- Filelist 2016-07-17 20:56:25.507744349 +0200 *************** *** 23,28 **** --- 23,29 ---- 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,139 **** --- 135,141 ---- 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 \ *** ../vim-7.4.2062/src/Make_bc5.mak 2016-07-17 18:41:43.083790495 +0200 --- src/Make_bc5.mak 2016-07-17 20:56:46.383532343 +0200 *************** *** 548,553 **** --- 548,554 ---- $(OBJDIR)\digraph.obj \ $(OBJDIR)\edit.obj \ $(OBJDIR)\eval.obj \ + $(OBJDIR)\evalfunc.obj \ $(OBJDIR)\ex_cmds.obj \ $(OBJDIR)\ex_cmds2.obj \ $(OBJDIR)\ex_docmd.obj \ *** ../vim-7.4.2062/src/Make_cyg_ming.mak 2016-07-17 18:41:43.083790495 +0200 --- src/Make_cyg_ming.mak 2016-07-17 20:57:01.399379836 +0200 *************** *** 615,620 **** --- 615,621 ---- $(OUTDIR)/digraph.o \ $(OUTDIR)/edit.o \ $(OUTDIR)/eval.o \ + $(OUTDIR)/evalfunc.o \ $(OUTDIR)/ex_cmds.o \ $(OUTDIR)/ex_cmds2.o \ $(OUTDIR)/ex_docmd.o \ *** ../vim-7.4.2062/src/Make_dice.mak 2016-07-17 18:41:43.083790495 +0200 --- src/Make_dice.mak 2016-07-17 20:57:18.083210379 +0200 *************** *** 37,42 **** --- 37,43 ---- digraph.c \ edit.c \ eval.c \ + evalfunc.c \ ex_cmds.c \ ex_cmds2.c \ ex_docmd.c \ *************** *** 91,96 **** --- 92,98 ---- 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,180 **** --- 177,184 ---- 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) *** ../vim-7.4.2062/src/Make_ivc.mak 2016-07-17 18:41:43.083790495 +0200 --- src/Make_ivc.mak 2016-07-17 20:57:44.570941320 +0200 *************** *** 221,226 **** --- 221,227 ---- "$(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,384 **** --- 380,389 ---- # 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 *** ../vim-7.4.2062/src/Make_manx.mak 2016-07-17 18:41:43.083790495 +0200 --- src/Make_manx.mak 2016-07-17 20:58:12.166660978 +0200 *************** *** 47,52 **** --- 47,53 ---- digraph.c \ edit.c \ eval.c \ + evalfunc.c \ ex_cmds.c \ ex_cmds2.c \ ex_docmd.c \ *************** *** 103,108 **** --- 104,110 ---- 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,162 **** --- 159,165 ---- 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,282 **** --- 280,288 ---- 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 *** ../vim-7.4.2062/src/Make_morph.mak 2016-07-17 18:41:43.083790495 +0200 --- src/Make_morph.mak 2016-07-17 20:58:26.814512160 +0200 *************** *** 35,40 **** --- 35,41 ---- digraph.c \ edit.c \ eval.c \ + evalfunc.c \ ex_cmds.c \ ex_cmds2.c \ ex_docmd.c \ *** ../vim-7.4.2062/src/Make_mvc.mak 2016-07-17 19:02:12.251003603 +0200 --- src/Make_mvc.mak 2016-07-17 20:58:55.634219336 +0200 *************** *** 559,564 **** --- 559,565 ---- $(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,1180 **** --- 1176,1183 ---- $(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,1377 **** --- 1375,1381 ---- proto/digraph.pro \ proto/edit.pro \ proto/eval.pro \ + proto/evalfunc.pro \ proto/ex_cmds.pro \ proto/ex_cmds2.pro \ proto/ex_docmd.pro \ *** ../vim-7.4.2062/src/Make_sas.mak 2016-07-17 18:41:43.083790495 +0200 --- src/Make_sas.mak 2016-07-17 20:59:24.841922536 +0200 *************** *** 100,105 **** --- 100,106 ---- digraph.c \ edit.c \ eval.c \ + evalfunc.c \ ex_cmds.c \ ex_cmds2.c \ ex_docmd.c \ *************** *** 155,160 **** --- 156,162 ---- digraph.o \ edit.o \ eval.o \ + evalfunc.o \ ex_cmds.o \ ex_cmds2.o \ ex_docmd.o \ *************** *** 210,215 **** --- 212,218 ---- 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,335 **** --- 333,340 ---- 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 *** ../vim-7.4.2062/src/version.c 2016-07-17 20:37:38.243378783 +0200 --- src/version.c 2016-07-17 20:53:03.029830641 +0200 *************** *** 760,761 **** --- 760,763 ---- { /* Add new patch number below this line */ + /**/ + 2063, /**/ -- GUARD #1: What, ridden on a horse? ARTHUR: Yes! GUARD #1: You're using coconuts! ARTHUR: What? GUARD #1: You've got two empty halves of coconut and you're bangin' 'em together. The Quest for the Holy Grail (Monty Python) /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///